File: /var/www/vhosts/uyarreklam.com.tr/httpdocs/reports.tar
abstract-report.php 0000644 00000065313 15154254173 0010410 0 ustar 00 <?php
/**
* Report Abstract
*
* Ensures all of the reports have a uniform class with helper functions.
*
* @since 6.0.0
*
* @package MonsterInsights
* @subpackage Reports
* @author Chris Christoff
*/
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class MonsterInsights_Report {
public $title;
public $class;
public $name;
public $version = '1.0.0';
public $source = 'reports';
public $start_date;
public $end_date;
/**
* We will use this value if we are not using the same value for report store and relay path.
*
* @var string
*/
protected $api_path;
/**
* Primary class constructor.
*
* @access public
* @since 6.0.0
*/
public function __construct() {
add_filter( 'monsterinsights_reports_abstract_get_data_pre_cache', array( $this, 'requirements' ), 10, 3 );
}
// Let's get the HTML to output for a particular report. This is not the AJAX endpoint. Args can hold things (generally start/end date range)
protected function get_report_html( $args = array() ) {
/* Defined in the report class */
// For ajax, args start, end, and data will be set with the data to use. Else call $this->get_data( array( 'default' => true ) )
return '';
}
public function additional_data() {
return array();
}
public function requirements( $error = false, $args = array(), $name = '' ) {
return $error;
}
public function show_report( $args = array() ) {
if ( ! current_user_can( 'monsterinsights_view_dashboard' ) ) {
return monsterinsights_get_message( 'error', esc_html__( 'Oops! Access is denied. If you believe you should be able to view this report, please contact your website administrator to ensure you have the correct user role to view MonsterInsights reports.', 'google-analytics-for-wordpress' ) );
}
if ( monsterinsights_get_option( 'dashboard_disabled', false ) ) {
if ( current_user_can( 'monsterinsights_save_settings' ) ) {
$url = is_network_admin() ? network_admin_url( 'admin.php?page=monsterinsights_settings' ) : admin_url( 'admin.php?page=monsterinsights_settings' );
// Translators: Placeholders add a link to the settings panel.
return monsterinsights_get_message( 'error', sprintf( esc_html__( 'Please %1$senable the dashboard%2$s to see report data.', 'google-analytics-for-wordpress' ), '<a href="' . $url . '">', '</a>' ) );
} else {
$message = sprintf(
// Translators: Link tag starts with url and link tag ends.
esc_html__( 'Oops! The MonsterInsights dashboard has been disabled. Please check with your site administrator that your role is included in the MonsterInsights permissions settings. %1$sClick here for more information%2$s.', 'google-analytics-for-wordpress' ),
'<a target="_blank" href="' . monsterinsights_get_url( 'notice', 'cannot-view-reports', 'https://www.monsterinsights.com/docs/how-to-allow-user-roles-to-access-the-monsterinsights-reports-and-settings/' ) . '">',
'</a>'
);
return monsterinsights_get_message( 'error', $message );
}
}
if ( monsterinsights_is_pro_version() ) {
if ( ! MonsterInsights()->license->has_license() ) {
$url = is_network_admin() ? network_admin_url( 'admin.php?page=monsterinsights_settings' ) : admin_url( 'admin.php?page=monsterinsights_settings' );
// Translators: Placeholders add a link to the settings panel, Support link tag starts with url and support link tag ends.
$message = sprintf(
esc_html__( 'Oops! We did not find an active MonsterInsights license. Please %1$scheck your license settings%2$s or %3$scontact our support team%4$s for help.', 'google-analytics-for-wordpress' ),
'<a href="' . $url . '">',
'</a>',
'<a target="_blank" href="' . monsterinsights_get_url( 'notice', 'no-active-license', 'https://www.monsterinsights.com/my-account/support/' ) . '">',
'</a>'
);
return monsterinsights_get_message( 'error', $message );
} else if ( MonsterInsights()->license->license_has_error() ) {
return monsterinsights_get_message( 'error', $this->get_license_error() );
}
}
if ( ! ( MonsterInsights()->auth->is_authed() || MonsterInsights()->auth->is_network_authed() ) ) {
if ( current_user_can( 'monsterinsights_save_settings' ) ) {
$url = is_network_admin() ? network_admin_url( 'admin.php?page=monsterinsights_settings' ) : admin_url( 'admin.php?page=monsterinsights_settings' );
// Translators: Placeholders add a link to the settings panel.
return monsterinsights_get_message( 'error', sprintf( esc_html__( 'Oops! We did not find a properly authenticated analytics account. Please %1$sauthenticate with Google%2$s to allow MonsterInsights to show you reports.', 'google-analytics-for-wordpress' ), '<a href="' . $url . '">', '</a>' ) );
} else {
return monsterinsights_get_message( 'error', esc_html__( 'Oops! It appears as though you do not have the right user permissions to authenticate. Please contact your website administrator to check your user roles.', 'google-analytics-for-wordpress' ) );
}
}
if ( monsterinsights_is_pro_version() ) {
if ( ! MonsterInsights()->license->license_can( $this->level ) ) {
return $this->get_upsell_notice();
}
}
$error = $this->requirements( false, array(), $this->name );
if ( ! empty( $error ) ) {
return monsterinsights_get_message( 'error', $error );
}
if ( ! empty( $args['error'] ) ) {
return monsterinsights_get_message( 'error', $args['error'] );
}
if ( empty( $args['data'] ) || ! is_array( $args['data'] ) ) {
if ( monsterinsights_is_pro_version() ) {
return '';
} else {
// Try to get default data.
$args = $this->get_data( array( 'default' => true ) );
if ( empty( $args['data'] ) || is_array( $args['data'] ) ) {
return monsterinsights_get_message( 'error', __( 'No data found', 'google-analytics-for-wordpress' ) );
}
if ( ! empty( $args['error'] ) ) {
return monsterinsights_get_message( 'error', $args['error'] );
}
}
}
return $this->get_report_html( $args['data'] );
}
// Deletes the report data from the cache
public function delete_cache( $where = 'site' ) {
if ( $where === 'site' || $where === 'both' ) {
delete_option( 'monsterinsights_report_data_' . $this->name );
}
if ( $where === 'network' || $where === 'both' ) {
delete_option( 'monsterinsights_network_report_data_' . $this->name );
}
}
// Get report data
public function get_data( $args = array() ) {
if ( ! empty( $args['default'] ) ) {
$args['start'] = $this->default_start_date();
$args['end'] = $this->default_end_date();
}
$start = ! empty( $args['start'] ) && $this->is_valid_date( $args['start'] ) ? $args['start'] : '';
$end = ! empty( $args['end'] ) && $this->is_valid_date( $args['end'] ) ? $args['end'] : '';
$extra_params = [];
if ( ! empty( $args['included_metrics'] ) ) {
$extra_params['included_metrics'] = $args['included_metrics'];
}
$compare_start = null;
$compare_end = null;
if ( isset( $args['compare_start'] ) ) {
$compare_start = ! empty( $args['compare_start'] ) && $this->is_valid_date( $args['compare_start'] ) ? $args['compare_start'] : '';
$compare_end = ! empty( $args['compare_end'] ) && $this->is_valid_date( $args['compare_end'] ) ? $args['compare_end'] : '';
}
if ( monsterinsights_is_pro_version() && ! MonsterInsights()->license->license_can( $this->level ) ) {
return array(
'success' => true,
'upgrade' => true,
'data' => array(),
);
}
if ( ! $this->is_valid_date_range( $start, $end ) ) {
return array(
'success' => false,
'error' => __( 'Whoops! No data found for this date range', 'google-analytics-for-wordpress' ),
'data' => array(
'type' => 'INVALID_DATE_RANGE',
),
);
}
if ( ( $start !== $this->default_start_date() || $end !== $this->default_end_date() ) && ! monsterinsights_is_pro_version() ) {
// On lite version, the date range is blocked with upgrade to pro message and this conflicts with getting YIR report.
// $start = $this->default_start_date();
// $end = $this->default_end_date();
// return array(
// 'success' => false,
// 'error' => __( 'Please upgrade to MonsterInsights Pro to use custom date ranges.', 'google-analytics-for-wordpress' ),
// 'data' => array(),
// );
}
$error = apply_filters( 'monsterinsights_reports_abstract_get_data_pre_cache', false, $args, $this->name );
if ( $error ) {
return apply_filters( 'monsterinsights_reports_handle_error_message', array(
'success' => false,
'error' => $error,
'data' => array(),
) );
}
// These values are going to use on child classes.
$this->start_date = $start;
$this->end_date = $end;
$check_cache = ( $start === $this->default_start_date() && $end === $this->default_end_date() ) || apply_filters( 'monsterinsights_report_use_cache', false, $this->name );
$site_auth = MonsterInsights()->auth->get_viewname();
$ms_auth = is_multisite() && MonsterInsights()->auth->get_network_viewname();
if ( $compare_start && $compare_end ) {
$transient = 'monsterinsights_report_' . $this->name . '_' . $start . '_' . $end . '_to_' . $compare_start . '_' . $compare_end;
} else {
$transient = 'monsterinsights_report_' . $this->name . '_' . $start . '_' . $end;
}
$current_timestamp = current_time( 'U' );
// Set to same time as MI cache. MI caches same day to 15 and others to 1 day, so there's no point pinging MI before then.
$expiration = apply_filters( 'monsterinsights_report_transient_expiration',
date( 'Y-m-d' ) === $end ? ( 15 * MINUTE_IN_SECONDS ) : ( strtotime( 'Tomorrow 12:05am', $current_timestamp ) - $current_timestamp ), $this->name ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date -- We need this to depend on the runtime timezone.
// Default date range, check.
if ( $site_auth || $ms_auth ) {
// Single site or MS with auth at subsite
if ( $compare_start && $compare_end ) {
$option_name = $site_auth ? 'monsterinsights_report_data_compare_' . $this->name : 'monsterinsights_network_report_data_compare_' . $this->name;
} else {
$option_name = $site_auth ? 'monsterinsights_report_data_' . $this->name : 'monsterinsights_network_report_data_' . $this->name;
}
$p = $site_auth ? MonsterInsights()->auth->get_viewid() : MonsterInsights()->auth->get_network_viewid();
$data = array();
// If default date range then get cache data from option.
if ( $check_cache ) {
$data = ! $site_auth && $ms_auth ? get_site_option( $option_name, array() ) : get_option( $option_name, array() );
} else {
$data = ! $site_auth && $ms_auth ? get_site_transient( $transient ) : get_transient( $transient );
}
$user_included_metrics = get_user_meta( get_current_user_id(), 'monsterinsights_included_metrics', true );
if ( $compare_start && $compare_end ) {
$previous_included_metrics = get_user_meta( get_current_user_id(), 'monsterinsights_previous_included_metrics_' . $start . '_' . $end . '_to_' . $compare_start . '_' . $compare_end, true );
} else {
$previous_included_metrics = get_user_meta( get_current_user_id(), 'monsterinsights_previous_included_metrics_' . $start . '_' . $end, true );
}
if ( $user_included_metrics === $previous_included_metrics ) {
if ( ! empty( $data ) &&
! empty( $data['expires'] ) &&
$data['expires'] >= time() &&
! empty( $data['data'] ) &&
! empty( $data['p'] ) &&
$data['p'] === $p
) {
return $this->prepare_report_data( array(
'success' => true,
'data' => $data['data'],
) );
}
} else {
// if the metrics has changed, then lets ignore the cache report and trigger a request.
if ( $compare_start && $compare_end ) {
update_user_meta( get_current_user_id(), 'monsterinsights_previous_included_metrics_' . $start . '_' . $end . '_to_' . $compare_start . '_' . $compare_end, $user_included_metrics );
} else {
update_user_meta( get_current_user_id(), 'monsterinsights_previous_included_metrics_' . $start . '_' . $end, $user_included_metrics );
}
}
// Nothing in cache, either not saved before, expired or mismatch. Let's grab from API.
$api_options = array( 'start' => $start, 'end' => $end );
if ( $compare_start && $compare_end ) {
$api_options['compare_start'] = $compare_start;
$api_options['compare_end'] = $compare_end;
}
if ( ! $site_auth && $ms_auth ) {
$api_options['network'] = true;
}
// Get the path of the relay.
$api_path = empty( $this->api_path ) ? $this->name : $this->api_path;
$api = new MonsterInsights_API_Request( 'analytics/reports/' . $api_path . '/', $api_options, 'GET' );
// Use a report source indicator for requests.
if ( ! empty( $this->source ) ) {
$api->set_additional_data( array(
'source' => $this->source,
) );
}
$additional_data = $this->additional_data();
if ( ! empty( $additional_data ) ) {
$api->set_additional_data( $additional_data );
}
$ret = $api->request( $extra_params );
if ( is_wp_error( $ret ) ) {
return array(
'success' => false,
'error' => $ret->get_error_message(),
'data' => array(),
);
} else {
// Success
// Strip any HTML tags from API response
$ret['data'] = json_encode($ret['data']);
$ret['data'] = strip_tags($ret['data']);
$ret['data'] = json_decode($ret['data'], true);
$data = array(
'expires' => time() + $expiration,
'p' => $p,
'data' => $ret['data'],
);
if ( $check_cache ) {
! $site_auth && $ms_auth ? update_site_option( $option_name, $data ) : update_option( $option_name, $data );
} else {
! $site_auth && $ms_auth ? set_site_transient( $transient, $data, $expiration ) : set_transient( $transient, $data, $expiration );
}
return $this->prepare_report_data( array(
'success' => true,
'data' => $ret['data'],
) );
}
} else {
$url = admin_url( 'admin.php?page=monsterinsights-onboarding' );
// Check for MS dashboard
if ( is_network_admin() ) {
$url = network_admin_url( 'admin.php?page=monsterinsights-onboarding' );
}
return array(
'success' => false,
'error' => sprintf(
/* translators: Placeholders add a link to the Setup Wizard page. */
__( 'You must be properly authenticated with MonsterInsights to use our reports. Please use our %1$ssetup wizard%2$s to get started.', 'google-analytics-for-wordpress' ),
'<a href=" ' . $url . ' ">',
'</a>'
),
'data' => array(),
);
}
}
public function default_start_date() {
return date( 'Y-m-d', strtotime( '-30 days' ) ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date -- We need this to depend on the runtime timezone.
}
public function default_end_date() {
return date( 'Y-m-d', strtotime( '-1 day' ) ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date -- We need this to depend on the runtime timezone.
}
/**
* Default date for compare start.
*
* @return string
*/
public function default_compare_start_date() {
return date( 'Y-m-d', strtotime( '-60 days' ) ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date -- We need this to depend on the runtime timezone.
}
/**
* Default date for compare end.
*
* @return string
*/
public function default_compare_end_date() {
return date( 'Y-m-d', strtotime( '-31 day' ) ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date -- We need this to depend on the runtime timezone.
}
// Checks to see if date range is valid. Should be 30-yesterday always for lite & any valid date range to today for Pro.
public function is_valid_date_range( $start, $end ) {
$now = current_datetime();
$wp_timezone = wp_timezone_string();
$start_date = DateTime::createFromFormat( 'Y-m-d', $start, new DateTimeZone( $wp_timezone ) );
$end_date = DateTime::createFromFormat( 'Y-m-d', $end, new DateTimeZone( $wp_timezone ) );
$ancient_date = DateTime::createFromFormat( 'Y-m-d', '2005-01-01', new DateTimeZone( $wp_timezone ) );
if ( $start_date > $now || $end_date > $now || $start_date < $ancient_date || $end_date < $ancient_date ) {
return false;
}
// return false if the start date is after the end date
return ( $start > $end ) ? false : true;
}
// Is a valid date value
public function is_valid_date( $date = '' ) {
$d = MonsterInsightsDateTime::create_from_format( 'Y-m-d', $date );
return $d && $d->format( 'Y-m-d' ) === $date;
}
/**
* Do not use the functions below this. They are unused and are just here so people
* with out of date MonsterInsights addons won't get fatal errors.
*/
protected function get_api_max_limit() {
return 300;
}
protected function get_date_range() {
return array();
}
protected function get_ga_report_url( $v4_name, $data, $v4_extra_params = '', $v4_endpoint = 'explorer', $is_real_time = false ) {
$auth = MonsterInsights()->auth;
$params = $this->get_ga_report_range( $data );
$format = 'https://analytics.google.com/analytics/web/#/%1$s/' . ( $is_real_time ? 'realtime' : 'reports' ) . '/%5$s?params=%3$s%4$s&r=%2$s';
if ( empty( $v4_name ) ) {
$report_name = '';
} else {
$report_name = $v4_name;
}
$extra_params = '&' . $v4_extra_params;
$endpoint = $v4_endpoint;
return sprintf(
$format,
$auth->get_referral_url(),
$report_name,
$params,
urlencode( $extra_params ),
$endpoint
);
}
public function get_upsell_notice() {
$has_level = monsterinsights_is_pro_version() ? MonsterInsights()->license->get_license_type() : false;
$has_level = $has_level ? $has_level : 'lite';
// Translators: Placeholders add the license level and the report title.
$message = sprintf( __( 'You currently have a %1$s level license, but this report requires at least a %2$s level license to view the %3$s. Please upgrade to view this report.', 'google-analytics-for-wordpress' ), $has_level, $this->level, $this->title );
ob_start(); ?>
<div
class="monsterinsights-upsell-report-container monsterinsights-upsell-report-<?php echo esc_attr($this->name); ?>-bg">
<div class="monsterinsights-upsell-container">
<div class="row justify-content-center">
<div class="col-lg-10 col-lg-offset-1 align-self-center">
<div class="monsterinsights-upsell-card">
<img class="monsterinsights-upgrade-mascot"
src="<?php echo esc_url(trailingslashit( MONSTERINSIGHTS_PLUGIN_URL )); ?>assets/css/images/mascot.png"
srcset="<?php echo esc_url(trailingslashit( MONSTERINSIGHTS_PLUGIN_URL )); ?>assets/css/images/mascot@2x.png 2x"
alt="">
<div class="monsterinsights-upsell-card-card-content">
<span
class="monsterinsights-upsell-card-title"><?php esc_html_e( 'Ready to Get Analytics Super-Powers?', 'google-analytics-for-wordpress' ); ?></span>
<p class="monsterinsights-upsell-card-subtitle">
<strong><?php esc_html_e( '(And Crush Your Competition?)', 'google-analytics-for-wordpress' ); ?></strong>
</p>
<?php if ( monsterinsights_is_pro_version() ) { ?>
<p>
<?php
// Translators: License level and smiley.
echo sprintf( esc_html__( 'Hey there! It looks like you\'ve got the %1$s license installed on your site. That\'s awesome! %s', 'google-analytics-for-wordpress' ), $has_level, '<span class="dashicons dashicons-smiley"></span>' ); // phpcs:ignore
?>
</p>
<p>
<?php
// Translators: Placeholders add the report title and license level.
echo sprintf( esc_html__( 'Do you want to access to %1$s reporting right now%2$s in your WordPress Dashboard? That comes with the %3$s level%4$s of our paid packages. You\'ll need to upgrade your license to get instant access.', 'google-analytics-for-wordpress' ), '<strong>' . $this->title, '</strong>', '<strong><a href="' . monsterinsights_get_url( 'reports-page', $this->name . '-report-upsell-license-link', 'https://monsterinsights.com/my-account/' ) . '">' . $this->level, '</a></strong>' ); // phpcs:ignore
?>
</p>
<p>
<?php
// Translators: Placeholdes add links to the account area and a guide.
echo sprintf( esc_html__( 'It\'s easy! To upgrade, navigate to %1$sMy Account%2$s on MonsterInsights.com, go to the licenses tab, and click upgrade. We also have a %3$sstep by step guide%4$s with pictures of this process.', 'google-analytics-for-wordpress' ), '<a href="' . monsterinsights_get_url( 'reports-page', $this->name . '-report-upsell-license-link', 'https://monsterinsights.com/my-account/' ) . '"><strong>', '</strong></a>', '<a href="' . monsterinsights_get_url( 'reports-page', $this->name . '-report-upsell-license-link', 'https://www.monsterinsights.com/docs/upgrade-monsterinsights-license/' ) . '" style="text-decoration:underline !important">', '</a>' ); // phpcs:ignore
?>
</p>
<p><?php esc_html_e( 'If you have any questions, don\'t hesitate to reach out. We\'re here to help.', 'google-analytics-for-wordpress' ); ?></p>
<?php } else { ?>
<p>
<?php
// Translators: Placeholder adds a smiley face.
echo sprintf( esc_html__( 'Hey there! %s It looks like you\'ve got the free version of MonsterInsights installed on your site. That\'s awesome!', 'google-analytics-for-wordpress' ), '<span class="dashicons dashicons-smiley"></span>' );
?>
</p>
<p>
<?php
// Translators: Placeholders make the text bold, add the license level and add a link to upgrade.
echo sprintf( esc_html__( 'Do you you want to access to %1$s reporting right now%2$s in your WordPress Dashboard? That comes with %3$s level%4$s of our paid packages. To get instant access, you\'ll want to buy a MonsterInsights license, which also gives you access to powerful addons, expanded reporting (including the ability to use custom date ranges), comprehensive tracking features (like UserID tracking) and access to our world-class support team.', 'google-analytics-for-wordpress' ), '<strong>' . $this->title, '</strong>', '<a href="' . monsterinsights_get_upgrade_link( 'reports-page', $this->name . '-report-upsell-license-link' ) . '">' . $this->level, '</a>' ); // phpcs:ignore
?>
</p>
<p>
<?php
// Translators: Placeholders make the text bold, add the license level and add a link to upgrade.
echo sprintf( esc_html__( 'Upgrading is easy! To upgrade, navigate to %1$ssour pricing page%2$s, purchase the required license, and then follow the %3$sinstructions in the email receipt%4$s to upgrade. It only takes a few minutes to unlock the most powerful, yet easy to use analytics tracking system for WordPress.', 'google-analytics-for-wordpress' ), '<a href="' . monsterinsights_get_upgrade_link( 'reports-page', $this->name . '-report-upsell-license-link' ) . '"><strong>', '</strong></a>', '<a style="text-decoration:underline !important" href="' . monsterinsights_get_url( 'reports-page', $this->name . '-report-go-lite-pro-link', 'https://www.monsterinsights.com/docs/go-lite-pro/' ) . '">', '</a>' ); // phpcs:ignore
?>
</p>
<p><?php esc_html_e( 'If you have any questions, don\'t hesitate to reach out. We\'re here to help.', 'google-analytics-for-wordpress' ); ?></p>
<?php } ?>
</div>
<div class="monsterinsights-upsell-card-action">
<?php if ( monsterinsights_is_pro_version() ) { ?>
<a href="<?php echo monsterinsights_get_upgrade_link( 'reports-page', $this->name . '-report-upsell-license-link' ); // phpcs:ignore -- Escaped in the function ?>"
class="monsterinsights-upsell-card-button"><?php esc_html_e( 'Upgrade Now', 'google-analytics-for-wordpress' ); ?></a>
<?php } else { ?>
<a href="<?php echo monsterinsights_get_url( 'reports-page', $this->name . '-report-upsell-license-link', 'https://www.monsterinsights.com/docs/upgrade-monsterinsights-license/' ); // phpcs:ignore -- Escaped in the function ?>"
class="monsterinsights-upsell-card-button"><?php esc_html_e( 'Get MonsterInsights Pro', 'google-analytics-for-wordpress' ); ?></a>
<?php } ?>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<?php
return ob_get_clean();
}
function get_ga_report_range( $data = array() ) {
if ( empty( $data['reportcurrentrange'] ) || empty( $data['reportcurrentrange']['startDate'] ) || empty( $data['reportcurrentrange']['endDate'] ) ) {
return '';
} else {
if ( ! empty( $data['reportprevrange'] ) && ! empty( $data['reportprevrange']['startDate'] ) && ! empty( $data['reportprevrange']['endDate'] ) ) {
return urlencode( '_u.date00=' . str_replace( '-', '', $data['reportcurrentrange']['startDate'] ) . '&_u.date01=' . str_replace( '-', '', $data['reportcurrentrange']['endDate'] ) . '&_u.date10=' . str_replace( '-', '', $data['reportprevrange']['startDate'] ) . '&_u.date11=' . str_replace( '-', '', $data['reportprevrange']['endDate'] ) );
}
return urlencode( '_u.date00=' . str_replace( '-', '', $data['reportcurrentrange']['startDate'] ) . '&_u.date01=' . str_replace( '-', '', $data['reportcurrentrange']['endDate'] ) );
}
}
/**
* Grab the link to the addons page used in each report's error message.
*
* @return string
*/
public function get_addons_page_link() {
if ( monsterinsights_can_install_plugins() ) {
$addons_link = 'install_addon';
} else {
$addons_link = esc_html__( 'Please ask your webmaster to enable this addon.', 'google-analytics-for-wordpress' );
}
return $addons_link;
}
/**
* When called will add the footer link to be displayed in the error popup.
*
* @param array $data The data sent as error response to the ajax call.
*
* @return array
*/
public function add_error_addon_link( $data ) {
$data['data']['footer'] = $this->get_addons_page_link();
return $data;
}
/**
* Added to allow individual reports to alter data when outputting for Vue reports.
*
* @param $data
*
* @return mixed
*/
public function prepare_report_data( $data ) {
return $data;
}
/**
* Set a report source to be sent with the request.
*
* @param string $source The source where the report is called from, defaults to reports.
*/
public function set_report_source( $source ) {
if ( ! empty( $source ) && is_string( $source ) ) {
$this->source = $source;
}
}
}
if ( ! class_exists( 'MonsterInsightsDateTime' ) ) {
class MonsterInsightsDateTime extends DateTime {
public static function create_from_format( $format, $time, $timezone = null ) {
if ( ! $timezone ) {
$timezone = new DateTimeZone( date_default_timezone_get() );
}
if ( version_compare( PHP_VERSION, '5.3', '>=' ) ) {
return parent::createFromFormat( $format, $time, $timezone );
}
return new DateTime( date( $format, strtotime( $time ) ), $timezone ); // phpcs:ignore
}
}
}
index.php 0000644 00000000101 15154254173 0006363 0 ustar 00 <?php
//Nothing to see here
header( 'HTTP/1.0 403 Forbidden' );
overview.php 0000644 00000007752 15154254173 0007145 0 ustar 00 <?php
/**
* Overview Report
*
* Ensures all of the reports have a uniform class with helper functions.
*
* @since 6.0.0
*
* @package MonsterInsights
* @subpackage Reports
* @author Chris Christoff
*/
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
final class MonsterInsights_Report_Overview extends MonsterInsights_Report {
public $title;
public $class = 'MonsterInsights_Report_Overview';
public $name = 'overview';
public $version = '1.0.0';
public $level = 'lite';
/**
* Primary class constructor.
*
* @access public
* @since 6.0.0
*/
public function __construct() {
$this->title = __( 'Overview', 'google-analytics-for-wordpress' );
parent::__construct();
}
/**
* Prepare report-specific data for output.
*
* @param array $data The data from the report before it gets sent to the frontend.
*
* @return mixed
*/
public function prepare_report_data( $data ) {
// Add flags to the countries report.
if ( ! empty( $data['data']['countries'] ) ) {
$country_names = monsterinsights_get_country_list( true );
foreach ( $data['data']['countries'] as $key => $country ) {
$data['data']['countries'][ $key ]['name'] = isset( $country_names[ $country['iso'] ] ) ? $country_names[ $country['iso'] ] : $country['iso'];
}
}
// Escape urls for the top pages report.
if ( ! empty( $data['data']['toppages'] ) ) {
foreach ( $data['data']['toppages'] as $key => $page ) {
$title = $data['data']['toppages'][ $key ]['title'];
$url = '(not set)' === $title ? '' : esc_url( $data['data']['toppages'][ $key ]['hostname'] );
$data['data']['toppages'][ $key ]['hostname'] = $url;
}
}
// Bounce rate add symbol.
if ( ! empty( $data['data']['infobox']['bounce']['value'] ) ) {
$data['data']['infobox']['bounce']['value'] .= '%';
}
// Add GA links.
if ( ! empty( $data['data'] ) ) {
$data['data']['galinks'] = array(
'countries' => $this->get_ga_report_url( 'user-demographics-detail', $data['data'] ),
'referrals' => $this->get_ga_report_url( 'lifecycle-user-acquisition', $data['data'], '_r.explorerCard..seldim=["userAcquiredCampaignSource"]' ),
'topposts' => $this->get_ga_report_url( 'all-pages-and-screens', $data['data'] ),
);
}
$this->define_chart_overlay( $data );
return apply_filters( 'monsterinsights_report_overview_data', $data );
}
/**
* Determine we need show chart overlay or not.
*
* @param array $data
*
* @return void
*/
private function define_chart_overlay( &$data ) {
// Set default value.
$data['data']['show_chart_overlay'] = false;
$connection_time = $this->get_connection_time();
if ( ! $connection_time ) {
return;
}
// If 24 hour passed then remove overlay.
if ( $connection_time + DAY_IN_SECONDS < time() ) {
return;
}
// If till now no data has tracked.
if ( ! $data['data']['overviewgraph']['sessions']['max'] ) {
$data['data']['show_chart_overlay'] = true;
// Generate random chart data.
for ( $i = 0; $i < $data['data']['overviewgraph']['count']; $i++ ) {
$data['data']['overviewgraph']['sessions']['datapoints'][ $i ] = wp_rand(1, 5);
$data['data']['overviewgraph']['pageviews']['datapoints'][ $i ] = wp_rand(1, 5);
}
}
}
/**
* Find when GA connected.
*
* @return int
*/
private function get_connection_time() {
$activated = get_option( 'monsterinsights_over_time', array() );
// If user had both activate.
if ( ! empty( $activated['connected_date_lite'] ) && ! empty( $activated['connected_date_pro'] ) ) {
// If lite activated last.
if ( $activated['connected_date_lite'] > $activated['connected_date_pro'] ) {
return $activated['connected_date_lite'];
}
return $activated['connected_date_pro'];
}
// If user has only lite activated.
if ( ! empty( $activated['connected_date_lite'] ) ) {
return $activated['connected_date_lite'];
}
// If user has only pro activated.
if ( ! empty( $activated['connected_date_pro'] ) ) {
return $activated['connected_date_pro'];
}
}
}
site-insights.php 0000644 00000004435 15154254173 0010064 0 ustar 00 <?php
/**
* Site Insights Report
*
* @since 8.24.0
*
* @package MonsterInsights
* @subpackage Reports
* @author Andrei Lupu
*/
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
final class MonsterInsights_Report_Site_Insights extends MonsterInsights_Report {
public $title;
public $class = 'MonsterInsights_Report_Site_Insights';
public $name = 'site-insights';
public $version = '1.0.0';
public $level = 'basic';
/**
* Primary class constructor.
*
* @access public
* @since 7.11.0
*/
public function __construct() {
$this->title = __( 'Site Insights', 'google-analytics-premium' );
add_filter( 'monsterinsights_report_use_cache', array( $this, 'use_cache' ), 10, 2 );
add_filter( 'monsterinsights_report_transient_expiration', array( $this, 'set_cache_expiration' ), 10, 2 );
parent::__construct();
}
/**
* Force the use of transients for site-insights report cache.
* Used in `monsterinsights_report_use_cache` where true means that cache is saved with set_options,
* and false means set_transient will be used.
*
* @param $use_cache
* @param $name
* @return bool
*/
public function use_cache( $use_cache, $name ) {
return $this->name === $name ? false : $use_cache;
}
/**
* A method for the `monsterinsights_report_transient_expiration` filter.
* It will force the expiration time for the transient to be "tomorrow".
*
* @param $length
* @param $name
* @return int|string
*/
public function set_cache_expiration( $length, $name ) {
$current_timestamp = current_time( 'U' );
$expire = ( strtotime( 'Tomorrow 12:05am', $current_timestamp ) - $current_timestamp );
return $this->name === $name ? $expire : $length;
}
/**
* Prepare report-specific data for output.
*
* @param array $data The data from the report before it gets sent to the frontend.
*
* @return mixed
*/
public function prepare_report_data( $data ) {
// Add flags to the countries report.
if ( ! empty( $data['data']['countries'] ) ) {
$country_names = monsterinsights_get_country_list( true );
foreach ( $data['data']['countries'] as $key => $country ) {
$data['data']['countries'][ $key ]['name'] = isset( $country_names[ $country['iso'] ] ) ? $country_names[ $country['iso'] ] : $country['iso'];
}
}
return $data;
}
}
site-summary.php 0000644 00000004703 15154254173 0007727 0 ustar 00 <?php
/**
* Site Summary Report
*
* Ensures all of the reports have a uniform class with helper functions.
*
* @since 6.0.0
*
* @package MonsterInsights
* @subpackage Reports
* @author Andrei Lupu
*/
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
final class MonsterInsights_Report_Site_Summary extends MonsterInsights_Report {
public $title;
public $class = 'MonsterInsights_Report_Site_Summary';
public $name = 'site_summary';
public $version = '1.0.0';
public $level = 'lite';
/**
* Primary class constructor.
*
* @access public
* @since 6.0.0
*/
public function __construct() {
$this->title = __( 'Site Summary', 'google-analytics-for-wordpress' );
parent::__construct();
add_filter( 'monsterinsights_report_site_summary_data', array( $this, 'prepare_data' ) );
}
/**
* Prepare report-specific data for output.
*
* @param array $data The data from the report before it gets sent to the frontend.
*
* @return mixed
*/
public function prepare_report_data( $data ) {
return apply_filters( 'monsterinsights_report_site_summary_data', $data );
}
public function prepare_data( $data ) {
// Fill summary data with total number of posts, pages, and comments.
if ( isset( $data['data']['summary'] ) ) {
$posts = wp_count_posts('post');
$data['data']['summary']['total_posts'] = $posts->publish;
$pages = wp_count_posts('page');
$data['data']['summary']['total_pages'] = $pages->publish;
$comments = get_comment_count();
$data['data']['summary']['total_comments'] = $comments['all'];
}
if ( ! empty( $data['data']['popular_post'] ) ) {
// Get the thumbnail for the post.
$post_id = url_to_postid( home_url() . $data['data']['popular_post']['url'] );
$data['data']['popular_post']['id'] = $post_id;
$data['data']['popular_post']['title'] = esc_html( get_the_title($post_id) );
$data['data']['popular_post']['img'] = get_the_post_thumbnail_url( $post_id, 'thumbnail' );
}
if ( !empty( $data['data']['popular_page'] ) ) {
// Compose the page url from homepage and the path we fetch from GA.
$data['data']['popular_page']['url'] = esc_url( home_url() . $data['data']['popular_page']['url'] );
// Get the thumbnail for the post.
$post_id = url_to_postid( $data['data']['popular_page']['url'] );
$data['data']['popular_page']['id'] = $post_id;
$data['data']['popular_page']['img'] = get_the_post_thumbnail_url( $post_id, 'thumbnail' );
}
return $data;
}
}
report-dimensions.php 0000644 00000001171 15155041332 0010735 0 ustar 00 <?php
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
final class MonsterInsights_Lite_Report_Dimensions extends MonsterInsights_Report {
public $title;
public $class = 'MonsterInsights_Lite_Report_Dimensions';
public $name = 'dimensions';
public $version = '1.0.0';
public $level = 'pro';
/**
* Primary class constructor.
*
* @access public
* @since 6.0.0
*/
public function __construct() {
$this->title = __( 'Dimensions', 'google-analytics-for-wordpress' );
parent::__construct();
}
protected function get_report_html( $data = array() ) {
return $this->get_upsell_notice();
}
}
report-ecommerce.php 0000644 00000001165 15155041332 0010527 0 ustar 00 <?php
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
final class MonsterInsights_Lite_Report_eCommerce extends MonsterInsights_Report {
public $title;
public $class = 'MonsterInsights_Lite_Report_eCommerce';
public $name = 'ecommerce';
public $version = '1.0.0';
public $level = 'pro';
/**
* Primary class constructor.
*
* @access public
* @since 6.0.0
*/
public function __construct() {
$this->title = __( 'eCommerce', 'google-analytics-for-wordpress' );
parent::__construct();
}
protected function get_report_html( $data = array() ) {
return $this->get_upsell_notice();
}
}
report-forms.php 0000644 00000001145 15155041332 0007714 0 ustar 00 <?php
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
final class MonsterInsights_Lite_Report_Forms extends MonsterInsights_Report {
public $title;
public $class = 'MonsterInsights_Lite_Report_Forms';
public $name = 'forms';
public $version = '1.0.0';
public $level = 'pro';
/**
* Primary class constructor.
*
* @access public
* @since 6.0.0
*/
public function __construct() {
$this->title = __( 'Forms', 'google-analytics-for-wordpress' );
parent::__construct();
}
protected function get_report_html( $data = array() ) {
return $this->get_upsell_notice();
}
}
report-publisher.php 0000644 00000001167 15155041332 0010567 0 ustar 00 <?php
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
final class MonsterInsights_Lite_Report_Publisher extends MonsterInsights_Report {
public $title;
public $class = 'MonsterInsights_Lite_Report_Publisher';
public $name = 'publisher';
public $version = '1.0.0';
public $level = 'plus';
/**
* Primary class constructor.
*
* @access public
* @since 6.0.0
*/
public function __construct() {
$this->title = __( 'Publishers', 'google-analytics-for-wordpress' );
parent::__construct();
}
protected function get_report_html( $data = array() ) {
return $this->get_upsell_notice();
}
}
report-queries.php 0000644 00000001165 15155041332 0010245 0 ustar 00 <?php
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
final class MonsterInsights_Lite_Report_Queries extends MonsterInsights_Report {
public $title;
public $class = 'MonsterInsights_Lite_Report_Queries';
public $name = 'queries';
public $version = '1.0.0';
public $level = 'plus';
/**
* Primary class constructor.
*
* @access public
* @since 6.0.0
*/
public function __construct() {
$this->title = __( 'Search Console', 'google-analytics-for-wordpress' );
parent::__construct();
}
protected function get_report_html( $data = array() ) {
return $this->get_upsell_notice();
}
}
report-realtime.php 0000644 00000001162 15155041332 0010367 0 ustar 00 <?php
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
final class MonsterInsights_Lite_Report_Realtime extends MonsterInsights_Report {
public $title;
public $class = 'MonsterInsights_Lite_Report_Realtime';
public $name = 'queries';
public $version = '1.0.0';
public $level = 'plus';
/**
* Primary class constructor.
*
* @access public
* @since 6.0.0
*/
public function __construct() {
$this->title = __( 'Real Time', 'google-analytics-for-wordpress' );
parent::__construct();
}
protected function get_report_html( $data = array() ) {
return $this->get_upsell_notice();
}
}
report-summaries.php 0000644 00000003434 15155041332 0010576 0 ustar 00 <?php
/**
* Summaries Report
*
* Ensures all of the reports have a uniform class with helper functions.
*
* @since 8.19.0
*
* @package MonsterInsights
* @subpackage Reports
* @author Mahbubur Rahman
*/
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
final class MonsterInsights_Report_Summaries extends MonsterInsights_Report {
public $title;
public $class = 'MonsterInsights_Report_Summaries';
public $name = 'summaries';
public $version = '1.0.0';
public $level = 'basic';
/**
* Primary class constructor.
*
* @access public
* @since 8.19.0
*/
public function __construct() {
$this->title = __( 'Summaries', 'google-analytics-for-wordpress' );
parent::__construct();
}
/**
* Prepare report-specific data for output.
*
* @param array $data The data from the report before it gets sent to the frontend.
*
* @return mixed
*/
public function prepare_report_data( $data ) {
// Escape urls for the top pages report.
if ( ! empty( $data['data']['toppages'] ) ) {
foreach ( $data['data']['toppages'] as $key => $page ) {
$title = $data['data']['toppages'][ $key ]['title'];
$url = '(not set)' === $title ? '' : esc_url( $data['data']['toppages'][ $key ]['hostname'] );
$data['data']['toppages'][ $key ]['hostname'] = $url;
}
}
// Add GA links.
if ( ! empty( $data['data'] ) ) {
$data['data']['galinks'] = array(
'referrals' => 'https://analytics.google.com/analytics/web/#report/trafficsources-referrals/' . MonsterInsights()->auth->get_referral_url() . $this->get_ga_report_range( $data['data'] ),
'topposts' => 'https://analytics.google.com/analytics/web/#/report/content-pages/' . MonsterInsights()->auth->get_referral_url() . $this->get_ga_report_range( $data['data'] ),
);
}
return $data;
}
}
report-year-in-review.php 0000644 00000005635 15155041332 0011441 0 ustar 00 <?php
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
final class MonsterInsights_Lite_Report_YearInReview extends MonsterInsights_Report {
public $title;
public $class = 'MonsterInsights_Lite_Report_YearInReview';
public $name = 'yearinreview';
public $version = '1.0.0';
public $level = 'lite';
/**
* Primary class constructor.
*
* @access public
* @since 7.11.0
*/
public function __construct() {
$this->title = __( 'Year in Review', 'google-analytics-for-wordpress' );
parent::__construct();
}
/**
* Prepare report-specific data for output.
*
* @param array $data The data from the report before it gets sent to the frontend.
*
* @return mixed
*/
public function prepare_report_data( $data ) {
// Add flags to the countries report.
if ( ! empty( $data['data']['countries'] ) ) {
$country_names = monsterinsights_get_country_list( true );
foreach ( $data['data']['countries'] as $key => $country ) {
$data['data']['countries'][ $key ]['name'] = isset( $country_names[ $country['iso'] ] ) ? $country_names[ $country['iso'] ] : $country['iso'];
}
}
// Escape urls for the top pages report.
if ( ! empty( $data['data']['toppages'] ) ) {
foreach ( $data['data']['toppages'] as $key => $page ) {
$title = $data['data']['toppages'][ $key ]['title'];
$url = '(not set)' === $title ? '' : esc_url( $data['data']['toppages'][ $key ]['hostname'] );
$data['data']['toppages'][ $key ]['hostname'] = $url;
}
}
// Add logged in user name
$user_info = wp_get_current_user();
$data['data']['user_name'] = '';
if ( ! empty( $user_info->user_firstname ) ) {
$first_name = $user_info->user_firstname;
$data['data']['user_name'] = $first_name;
}
return $data;
}
/**
* This Class needs a specific start date: first day of the year.
*
* @return string
*/
public function default_start_date() {
return date( 'Y-m-d', strtotime( '01 January 2024' ) ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date -- We want this to depend on the runtime timezone.
}
/**
* The default end date of this report should be -1 day if we are still in the same year.
* But we also need to avoid getting data after January 1st.
*
* @return string
*/
public function default_end_date() {
$current_year = date('Y'); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date -- We want this to depend on the runtime timezone.
// If we are still in 2024 we should get data from yesterday
if ($current_year === '2024') {
return date( 'Y-m-d', strtotime( '-1' ) ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date -- We want this to depend on the runtime timezone.
}
// otherwise let it be a thing of the past.
return date( 'Y-m-d', strtotime( '31 December 2024' ) ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date -- We want this to depend on the runtime timezone.
}
}
class-wc-admin-report.php 0000644 00000057073 15155076532 0011415 0 ustar 00 <?php
/**
* Admin report functionality.
*
* @package WooCommerce\Admin\Reports
*/
use Automattic\WooCommerce\Utilities\ArrayUtil;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Admin Report.
*
* Extended by reports to show charts and stats in admin.
*
* @package WooCommerce\Admin\Reports
* @version 2.1.0
*/
class WC_Admin_Report {
/**
* List of transients name that have been updated and need persisting.
*
* @var array
*/
protected static $transients_to_update = array();
/**
* The list of transients.
*
* @var array
*/
protected static $cached_results = array();
/**
* The chart interval.
*
* @var int
*/
public $chart_interval;
/**
* Group by SQL query.
*
* @var string
*/
public $group_by_query;
/**
* The bar width.
*
* @var int
*/
public $barwidth;
/**
* Group chart item by day or month.
*
* @var string
*/
public $chart_groupby;
/**
* The start date of the report.
*
* @var int timestamp
*/
public $start_date;
/**
* The end date of the report.
*
* @var int timestamp
*/
public $end_date;
/**
* Get report totals such as order totals and discount amounts.
*
* Data example:
*
* '_order_total' => array(
* 'type' => 'meta',
* 'function' => 'SUM',
* 'name' => 'total_sales'
* )
*
* @param array $args arguments for the report.
* @return mixed depending on query_type
*/
public function get_order_report_data( $args = array() ) {
global $wpdb;
$default_args = array(
'data' => array(),
'where' => array(),
'where_meta' => array(),
'query_type' => 'get_row',
'group_by' => '',
'order_by' => '',
'limit' => '',
'filter_range' => false,
'nocache' => false,
'debug' => false,
'order_types' => wc_get_order_types( 'reports' ),
'order_status' => array( 'completed', 'processing', 'on-hold' ),
'parent_order_status' => false,
);
$args = apply_filters( 'woocommerce_reports_get_order_report_data_args', $args );
$args = wp_parse_args( $args, $default_args );
// phpcs:ignore WordPress.PHP.DontExtract.extract_extract
extract( $args );
if ( empty( $data ) ) {
return '';
}
$order_status = apply_filters( 'woocommerce_reports_order_statuses', $order_status );
$query = array();
$select = array();
foreach ( $data as $raw_key => $value ) {
$key = sanitize_key( $raw_key );
$distinct = '';
if ( isset( $value['distinct'] ) ) {
$distinct = 'DISTINCT';
}
switch ( $value['type'] ) {
case 'meta':
$get_key = "meta_{$key}.meta_value";
break;
case 'parent_meta':
$get_key = "parent_meta_{$key}.meta_value";
break;
case 'post_data':
$get_key = "posts.{$key}";
break;
case 'order_item_meta':
$get_key = "order_item_meta_{$key}.meta_value";
break;
case 'order_item':
$get_key = "order_items.{$key}";
break;
}
if ( empty( $get_key ) ) {
// Skip to the next foreach iteration else the query will be invalid.
continue;
}
if ( $value['function'] ) {
$get = "{$value['function']}({$distinct} {$get_key})";
} else {
$get = "{$distinct} {$get_key}";
}
$select[] = "{$get} as {$value['name']}";
}
$query['select'] = 'SELECT ' . implode( ',', $select );
$query['from'] = "FROM {$wpdb->posts} AS posts";
// Joins.
$joins = array();
foreach ( ( $data + $where ) as $raw_key => $value ) {
$join_type = isset( $value['join_type'] ) ? $value['join_type'] : 'INNER';
$type = isset( $value['type'] ) ? $value['type'] : false;
$key = sanitize_key( $raw_key );
switch ( $type ) {
case 'meta':
$joins[ "meta_{$key}" ] = "{$join_type} JOIN {$wpdb->postmeta} AS meta_{$key} ON ( posts.ID = meta_{$key}.post_id AND meta_{$key}.meta_key = '{$raw_key}' )";
break;
case 'parent_meta':
$joins[ "parent_meta_{$key}" ] = "{$join_type} JOIN {$wpdb->postmeta} AS parent_meta_{$key} ON (posts.post_parent = parent_meta_{$key}.post_id) AND (parent_meta_{$key}.meta_key = '{$raw_key}')";
break;
case 'order_item_meta':
$joins['order_items'] = "{$join_type} JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON (posts.ID = order_items.order_id)";
if ( ! empty( $value['order_item_type'] ) ) {
$joins['order_items'] .= " AND (order_items.order_item_type = '{$value['order_item_type']}')";
}
$joins[ "order_item_meta_{$key}" ] = "{$join_type} JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta_{$key} ON " .
"(order_items.order_item_id = order_item_meta_{$key}.order_item_id) " .
" AND (order_item_meta_{$key}.meta_key = '{$raw_key}')";
break;
case 'order_item':
$joins['order_items'] = "{$join_type} JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON posts.ID = order_items.order_id";
break;
}
}
if ( ! empty( $where_meta ) ) {
foreach ( $where_meta as $value ) {
if ( ! is_array( $value ) ) {
continue;
}
$join_type = isset( $value['join_type'] ) ? $value['join_type'] : 'INNER';
$type = isset( $value['type'] ) ? $value['type'] : false;
$key = sanitize_key( is_array( $value['meta_key'] ) ? $value['meta_key'][0] . '_array' : $value['meta_key'] );
if ( 'order_item_meta' === $type ) {
$joins['order_items'] = "{$join_type} JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON posts.ID = order_items.order_id";
$joins[ "order_item_meta_{$key}" ] = "{$join_type} JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta_{$key} ON order_items.order_item_id = order_item_meta_{$key}.order_item_id";
} else {
// If we have a where clause for meta, join the postmeta table.
$joins[ "meta_{$key}" ] = "{$join_type} JOIN {$wpdb->postmeta} AS meta_{$key} ON posts.ID = meta_{$key}.post_id";
}
}
}
if ( ! empty( $parent_order_status ) ) {
$joins['parent'] = "LEFT JOIN {$wpdb->posts} AS parent ON posts.post_parent = parent.ID";
}
$query['join'] = implode( ' ', $joins );
$query['where'] = "
WHERE posts.post_type IN ( '" . implode( "','", $order_types ) . "' )
";
if ( ! empty( $order_status ) ) {
$query['where'] .= "
AND posts.post_status IN ( 'wc-" . implode( "','wc-", $order_status ) . "')
";
}
if ( ! empty( $parent_order_status ) ) {
if ( ! empty( $order_status ) ) {
$query['where'] .= " AND ( parent.post_status IN ( 'wc-" . implode( "','wc-", $parent_order_status ) . "') OR parent.ID IS NULL ) ";
} else {
$query['where'] .= " AND parent.post_status IN ( 'wc-" . implode( "','wc-", $parent_order_status ) . "') ";
}
}
// phpcs:disable WordPress.DateTime.RestrictedFunctions.date_date
if ( $filter_range ) {
$query['where'] .= "
AND posts.post_date >= '" . date( 'Y-m-d H:i:s', $this->start_date ) . "'
AND posts.post_date < '" . date( 'Y-m-d H:i:s', strtotime( '+1 DAY', $this->end_date ) ) . "'
";
}
// phpcs:enable WordPress.DateTime.RestrictedFunctions.date_date
if ( ! empty( $where_meta ) ) {
$relation = isset( $where_meta['relation'] ) ? $where_meta['relation'] : 'AND';
$query['where'] .= ' AND (';
foreach ( $where_meta as $index => $value ) {
if ( ! is_array( $value ) ) {
continue;
}
$key = sanitize_key( is_array( $value['meta_key'] ) ? $value['meta_key'][0] . '_array' : $value['meta_key'] );
if ( strtolower( $value['operator'] ) === 'in' || strtolower( $value['operator'] ) === 'not in' ) {
if ( is_array( $value['meta_value'] ) ) {
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
$value['meta_value'] = implode( "','", $value['meta_value'] );
}
if ( ! empty( $value['meta_value'] ) ) {
$where_value = "{$value['operator']} ('{$value['meta_value']}')";
}
} else {
$where_value = "{$value['operator']} '{$value['meta_value']}'";
}
if ( ! empty( $where_value ) ) {
if ( $index > 0 ) {
$query['where'] .= ' ' . $relation;
}
if ( isset( $value['type'] ) && 'order_item_meta' === $value['type'] ) {
if ( is_array( $value['meta_key'] ) ) {
$query['where'] .= " ( order_item_meta_{$key}.meta_key IN ('" . implode( "','", $value['meta_key'] ) . "')";
} else {
$query['where'] .= " ( order_item_meta_{$key}.meta_key = '{$value['meta_key']}'";
}
$query['where'] .= " AND order_item_meta_{$key}.meta_value {$where_value} )";
} else {
if ( is_array( $value['meta_key'] ) ) {
$query['where'] .= " ( meta_{$key}.meta_key IN ('" . implode( "','", $value['meta_key'] ) . "')";
} else {
$query['where'] .= " ( meta_{$key}.meta_key = '{$value['meta_key']}'";
}
$query['where'] .= " AND meta_{$key}.meta_value {$where_value} )";
}
}
}
$query['where'] .= ')';
}
if ( ! empty( $where ) ) {
foreach ( $where as $value ) {
if ( strtolower( $value['operator'] ) === 'in' || strtolower( $value['operator'] ) === 'not in' ) {
if ( is_array( $value['value'] ) ) {
$value['value'] = implode( "','", $value['value'] );
}
if ( ! empty( $value['value'] ) ) {
$where_value = "{$value['operator']} ('{$value['value']}')";
}
} else {
$where_value = "{$value['operator']} '{$value['value']}'";
}
if ( ! empty( $where_value ) ) {
$query['where'] .= " AND {$value['key']} {$where_value}";
}
}
}
if ( $group_by ) {
$query['group_by'] = "GROUP BY {$group_by}";
}
if ( $order_by ) {
$query['order_by'] = "ORDER BY {$order_by}";
}
if ( $limit ) {
$query['limit'] = "LIMIT {$limit}";
}
$query = apply_filters( 'woocommerce_reports_get_order_report_query', $query );
$query = implode( ' ', $query );
if ( $debug ) {
echo '<pre>';
wc_print_r( $query );
echo '</pre>';
}
if ( $debug || $nocache ) {
self::enable_big_selects();
$result = apply_filters( 'woocommerce_reports_get_order_report_data', $wpdb->$query_type( $query ), $data );
} else {
$query_hash = md5( $query_type . $query );
$result = $this->get_cached_query( $query_hash );
if ( null === $result ) {
self::enable_big_selects();
$result = apply_filters( 'woocommerce_reports_get_order_report_data', $wpdb->$query_type( $query ), $data );
}
$this->set_cached_query( $query_hash, $result );
}
return $result;
}
/**
* Init the static hooks of the class.
*/
protected static function add_update_transients_hook() {
if ( ! has_action( 'shutdown', array( 'WC_Admin_Report', 'maybe_update_transients' ) ) ) {
add_action( 'shutdown', array( 'WC_Admin_Report', 'maybe_update_transients' ) );
}
}
/**
* Enables big mysql selects for reports, just once for this session.
*/
protected static function enable_big_selects() {
static $big_selects = false;
global $wpdb;
if ( ! $big_selects ) {
$wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' );
$big_selects = true;
}
}
/**
* Get the cached query result or null if it's not in the cache.
*
* @param string $query_hash The query hash.
*
* @return mixed
*/
protected function get_cached_query( $query_hash ) {
$class = strtolower( get_class( $this ) );
if ( ! isset( self::$cached_results[ $class ] ) ) {
self::$cached_results[ $class ] = get_transient( strtolower( get_class( $this ) ) );
}
if ( isset( self::$cached_results[ $class ][ $query_hash ] ) ) {
return self::$cached_results[ $class ][ $query_hash ];
}
return null;
}
/**
* Set the cached query result.
*
* @param string $query_hash The query hash.
* @param mixed $data The data to cache.
*/
protected function set_cached_query( $query_hash, $data ) {
$class = strtolower( get_class( $this ) );
if ( ! isset( self::$cached_results[ $class ] ) ) {
self::$cached_results[ $class ] = get_transient( $class );
}
if ( false === self::$cached_results[ $class ] ) {
self::$cached_results[ $class ] = array();
}
self::add_update_transients_hook();
self::$transients_to_update[ $class ] = $class;
self::$cached_results[ $class ][ $query_hash ] = $data;
}
/**
* Function to update the modified transients at the end of the request.
*/
public static function maybe_update_transients() {
foreach ( self::$transients_to_update as $key => $transient_name ) {
set_transient( $transient_name, self::$cached_results[ $transient_name ], DAY_IN_SECONDS );
}
// Transients have been updated reset the list.
self::$transients_to_update = array();
}
/**
* Put data with post_date's into an array of times.
*
* @param array $data array of your data.
* @param string $date_key key for the 'date' field. e.g. 'post_date'.
* @param string $data_key key for the data you are charting.
* @param int $interval interval to use.
* @param string $start_date start date.
* @param string $group_by group by.
* @return array
*/
public function prepare_chart_data( $data, $date_key, $data_key, $interval, $start_date, $group_by ) {
// phpcs:disable WordPress.DateTime.RestrictedFunctions.date_date
$prepared_data = array();
// Ensure all days (or months) have values in this range.
if ( 'day' === $group_by ) {
for ( $i = 0; $i <= $interval; $i ++ ) {
$time = strtotime( date( 'Ymd', strtotime( "+{$i} DAY", $start_date ) ) ) . '000';
if ( ! isset( $prepared_data[ $time ] ) ) {
$prepared_data[ $time ] = array( esc_js( $time ), 0 );
}
}
} else {
$current_yearnum = date( 'Y', $start_date );
$current_monthnum = date( 'm', $start_date );
for ( $i = 0; $i <= $interval; $i ++ ) {
$time = strtotime( $current_yearnum . str_pad( $current_monthnum, 2, '0', STR_PAD_LEFT ) . '01' ) . '000';
if ( ! isset( $prepared_data[ $time ] ) ) {
$prepared_data[ $time ] = array( esc_js( $time ), 0 );
}
$current_monthnum ++;
if ( $current_monthnum > 12 ) {
$current_monthnum = 1;
$current_yearnum ++;
}
}
}
foreach ( $data as $d ) {
switch ( $group_by ) {
case 'day':
$time = strtotime( date( 'Ymd', strtotime( $d->$date_key ) ) ) . '000';
break;
case 'month':
default:
$time = strtotime( date( 'Ym', strtotime( $d->$date_key ) ) . '01' ) . '000';
break;
}
if ( ! isset( $prepared_data[ $time ] ) ) {
continue;
}
if ( $data_key ) {
$prepared_data[ $time ][1] += is_numeric( $d->$data_key ) ? $d->$data_key : 0;
} else {
$prepared_data[ $time ][1] ++;
}
}
return $prepared_data;
// phpcs:enable WordPress.DateTime.RestrictedFunctions.date_date
}
/**
* Prepares a sparkline to show sales in the last X days.
*
* @param int $id ID of the product to show. Blank to get all orders.
* @param int $days Days of stats to get.
* @param string $type Type of sparkline to get. Ignored if ID is not set.
* @return string
*/
public function sales_sparkline( $id = '', $days = 7, $type = 'sales' ) {
// phpcs:disable WordPress.DateTime.RestrictedFunctions.date_date, WordPress.DateTime.CurrentTimeTimestamp.Requested
if ( $id ) {
$meta_key = ( 'sales' === $type ) ? '_line_total' : '_qty';
$data = $this->get_order_report_data(
array(
'data' => array(
'_product_id' => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => '',
'name' => 'product_id',
),
$meta_key => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => 'SUM',
'name' => 'sparkline_value',
),
'post_date' => array(
'type' => 'post_data',
'function' => '',
'name' => 'post_date',
),
),
'where' => array(
array(
'key' => 'post_date',
'value' => date( 'Y-m-d', strtotime( 'midnight -' . ( $days - 1 ) . ' days', current_time( 'timestamp' ) ) ),
'operator' => '>',
),
array(
'key' => 'order_item_meta__product_id.meta_value',
'value' => $id,
'operator' => '=',
),
),
'group_by' => 'YEAR(posts.post_date), MONTH(posts.post_date), DAY(posts.post_date)',
'query_type' => 'get_results',
'filter_range' => false,
)
);
} else {
$data = $this->get_order_report_data(
array(
'data' => array(
'_order_total' => array(
'type' => 'meta',
'function' => 'SUM',
'name' => 'sparkline_value',
),
'post_date' => array(
'type' => 'post_data',
'function' => '',
'name' => 'post_date',
),
),
'where' => array(
array(
'key' => 'post_date',
'value' => date( 'Y-m-d', strtotime( 'midnight -' . ( $days - 1 ) . ' days', current_time( 'timestamp' ) ) ),
'operator' => '>',
),
),
'group_by' => 'YEAR(posts.post_date), MONTH(posts.post_date), DAY(posts.post_date)',
'query_type' => 'get_results',
'filter_range' => false,
)
);
}
$total = 0;
foreach ( $data as $d ) {
$total += $d->sparkline_value;
}
if ( 'sales' === $type ) {
/* translators: 1: total income 2: days */
$tooltip = sprintf( __( 'Sold %1$s worth in the last %2$d days', 'woocommerce' ), wp_strip_all_tags( wc_price( $total ) ), $days );
} else {
/* translators: 1: total items sold 2: days */
$tooltip = sprintf( _n( 'Sold %1$d item in the last %2$d days', 'Sold %1$d items in the last %2$d days', $total, 'woocommerce' ), $total, $days );
}
$sparkline_data = array_values( $this->prepare_chart_data( $data, 'post_date', 'sparkline_value', $days - 1, strtotime( 'midnight -' . ( $days - 1 ) . ' days', current_time( 'timestamp' ) ), 'day' ) );
return '<span class="wc_sparkline ' . ( ( 'sales' === $type ) ? 'lines' : 'bars' ) . ' tips" data-color="#777" data-tip="' . esc_attr( $tooltip ) . '" data-barwidth="' . 60 * 60 * 16 * 1000 . '" data-sparkline="' . wc_esc_json( wp_json_encode( $sparkline_data ) ) . '"></span>';
// phpcs:enable WordPress.DateTime.RestrictedFunctions.date_date, WordPress.DateTime.CurrentTimeTimestamp.Requested
}
/**
* Get the current range and calculate the start and end dates.
*
* @param string $current_range Type of range.
*/
public function calculate_current_range( $current_range ) {
// phpcs:disable WordPress.DateTime.RestrictedFunctions.date_date, WordPress.DateTime.CurrentTimeTimestamp.Requested
// phpcs:disable WordPress.Security.NonceVerification.Recommended
switch ( $current_range ) {
case 'custom':
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
$this->start_date = max( strtotime( '-20 years' ), strtotime( sanitize_text_field( wp_unslash( $_GET['start_date'] ) ) ) );
if ( empty( $_GET['end_date'] ) ) {
$this->end_date = strtotime( 'midnight', current_time( 'timestamp' ) );
} else {
$this->end_date = strtotime( 'midnight', strtotime( sanitize_text_field( wp_unslash( $_GET['end_date'] ) ) ) );
}
$interval = 0;
$min_date = $this->start_date;
// phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
while ( ( $min_date = strtotime( '+1 MONTH', $min_date ) ) <= $this->end_date ) {
$interval ++;
}
// 3 months max for day view
if ( $interval > 3 ) {
$this->chart_groupby = 'month';
} else {
$this->chart_groupby = 'day';
}
break;
case 'year':
$this->start_date = strtotime( date( 'Y-01-01', current_time( 'timestamp' ) ) );
$this->end_date = strtotime( 'midnight', current_time( 'timestamp' ) );
$this->chart_groupby = 'month';
break;
case 'last_month':
$first_day_current_month = strtotime( date( 'Y-m-01', current_time( 'timestamp' ) ) );
$this->start_date = strtotime( date( 'Y-m-01', strtotime( '-1 DAY', $first_day_current_month ) ) );
$this->end_date = strtotime( date( 'Y-m-t', strtotime( '-1 DAY', $first_day_current_month ) ) );
$this->chart_groupby = 'day';
break;
case 'month':
$this->start_date = strtotime( date( 'Y-m-01', current_time( 'timestamp' ) ) );
$this->end_date = strtotime( 'midnight', current_time( 'timestamp' ) );
$this->chart_groupby = 'day';
break;
case '7day':
$this->start_date = strtotime( '-6 days', strtotime( 'midnight', current_time( 'timestamp' ) ) );
$this->end_date = strtotime( 'midnight', current_time( 'timestamp' ) );
$this->chart_groupby = 'day';
break;
}
// Group by.
switch ( $this->chart_groupby ) {
case 'day':
$this->group_by_query = 'YEAR(posts.post_date), MONTH(posts.post_date), DAY(posts.post_date)';
$this->chart_interval = absint( ceil( max( 0, ( $this->end_date - $this->start_date ) / ( 60 * 60 * 24 ) ) ) );
$this->barwidth = 60 * 60 * 24 * 1000;
break;
case 'month':
$this->group_by_query = 'YEAR(posts.post_date), MONTH(posts.post_date)';
$this->chart_interval = 0;
$min_date = strtotime( date( 'Y-m-01', $this->start_date ) );
// phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
while ( ( $min_date = strtotime( '+1 MONTH', $min_date ) ) <= $this->end_date ) {
$this->chart_interval ++;
}
$this->barwidth = 60 * 60 * 24 * 7 * 4 * 1000;
break;
}
// phpcs:enable WordPress.Security.NonceVerification.Recommended
// phpcs:enable WordPress.DateTime.RestrictedFunctions.date_date, WordPress.DateTime.CurrentTimeTimestamp.Requested
}
/**
* Return currency tooltip JS based on WooCommerce currency position settings.
*
* @return string
*/
public function get_currency_tooltip() {
switch ( get_option( 'woocommerce_currency_pos' ) ) {
case 'right':
$currency_tooltip = 'append_tooltip: "' . get_woocommerce_currency_symbol() . '"';
break;
case 'right_space':
$currency_tooltip = 'append_tooltip: " ' . get_woocommerce_currency_symbol() . '"';
break;
case 'left':
$currency_tooltip = 'prepend_tooltip: "' . get_woocommerce_currency_symbol() . '"';
break;
case 'left_space':
default:
$currency_tooltip = 'prepend_tooltip: "' . get_woocommerce_currency_symbol() . ' "';
break;
}
return $currency_tooltip;
}
/**
* Get the main chart.
*/
public function get_main_chart() {}
/**
* Get the legend for the main chart sidebar.
*
* @return array
*/
public function get_chart_legend() {
return array();
}
/**
* Get chart widgets.
*
* @return array
*/
public function get_chart_widgets() {
return array();
}
/**
* Get an export link if needed.
*/
public function get_export_button() {}
/**
* Output the report.
*/
public function output_report() {}
/**
* Check nonce for current range.
*
* @since 3.0.4
* @param string $current_range Current range.
*/
public function check_current_range_nonce( $current_range ) {
if ( 'custom' !== $current_range ) {
return;
}
if ( ! isset( $_GET['wc_reports_nonce'] ) || ! wp_verify_nonce( sanitize_key( $_GET['wc_reports_nonce'] ), 'custom_range' ) ) {
// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotValidated
wp_die(
/* translators: %1$s: open link, %2$s: close link */
sprintf( esc_html__( 'This report link has expired. %1$sClick here to view the filtered report%2$s.', 'woocommerce' ), '<a href="' . esc_url( wp_nonce_url( esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ), 'custom_range', 'wc_reports_nonce' ) ) . '">', '</a>' ),
esc_attr__( 'Confirm navigation', 'woocommerce' )
);
// phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotValidated
exit;
}
}
}
class-wc-report-coupon-usage.php 0000644 00000042723 15155076532 0012726 0 ustar 00 <?php
/**
* Coupon usage report functionality
*
* @package WooCommerce\Admin\Reports
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* WC_Report_Coupon_Usage
*
* @package WooCommerce\Admin\Reports
* @version 2.1.0
*/
class WC_Report_Coupon_Usage extends WC_Admin_Report {
/**
* Chart colors.
*
* @var array
*/
public $chart_colours = array();
/**
* Coupon codes.
*
* @var array
*/
public $coupon_codes = array();
/**
* Constructor.
*/
public function __construct() {
if ( isset( $_GET['coupon_codes'] ) && is_array( $_GET['coupon_codes'] ) ) {
$this->coupon_codes = array_filter( array_map( 'sanitize_text_field', wp_unslash( $_GET['coupon_codes'] ) ) );
} elseif ( isset( $_GET['coupon_codes'] ) ) {
$this->coupon_codes = array_filter( array( sanitize_text_field( wp_unslash( $_GET['coupon_codes'] ) ) ) );
}
}
/**
* Get the legend for the main chart sidebar.
*
* @return array
*/
public function get_chart_legend() {
$legend = array();
$total_discount_query = array(
'data' => array(
'discount_amount' => array(
'type' => 'order_item_meta',
'order_item_type' => 'coupon',
'function' => 'SUM',
'name' => 'discount_amount',
),
),
'where' => array(
array(
'key' => 'order_item_type',
'value' => 'coupon',
'operator' => '=',
),
),
'query_type' => 'get_var',
'filter_range' => true,
'order_types' => wc_get_order_types( 'order-count' ),
);
$total_coupons_query = array(
'data' => array(
'order_item_id' => array(
'type' => 'order_item',
'order_item_type' => 'coupon',
'function' => 'COUNT',
'name' => 'order_coupon_count',
),
),
'where' => array(
array(
'key' => 'order_item_type',
'value' => 'coupon',
'operator' => '=',
),
),
'query_type' => 'get_var',
'filter_range' => true,
'order_types' => wc_get_order_types( 'order-count' ),
);
if ( ! empty( $this->coupon_codes ) ) {
$coupon_code_query = array(
'type' => 'order_item',
'key' => 'order_item_name',
'value' => $this->coupon_codes,
'operator' => 'IN',
);
$total_discount_query['where'][] = $coupon_code_query;
$total_coupons_query['where'][] = $coupon_code_query;
}
$total_discount = $this->get_order_report_data( $total_discount_query );
$total_coupons = absint( $this->get_order_report_data( $total_coupons_query ) );
$legend[] = array(
/* translators: %s: discount amount */
'title' => sprintf( __( '%s discounts in total', 'woocommerce' ), '<strong>' . wc_price( $total_discount ) . '</strong>' ),
'color' => $this->chart_colours['discount_amount'],
'highlight_series' => 1,
);
$legend[] = array(
/* translators: %s: coupons amount */
'title' => sprintf( __( '%s coupons used in total', 'woocommerce' ), '<strong>' . $total_coupons . '</strong>' ),
'color' => $this->chart_colours['coupon_count'],
'highlight_series' => 0,
);
return $legend;
}
/**
* Output the report.
*/
public function output_report() {
$ranges = array(
'year' => __( 'Year', 'woocommerce' ),
'last_month' => __( 'Last month', 'woocommerce' ),
'month' => __( 'This month', 'woocommerce' ),
'7day' => __( 'Last 7 days', 'woocommerce' ),
);
$this->chart_colours = array(
'discount_amount' => '#3498db',
'coupon_count' => '#d4d9dc',
);
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day';
if ( ! in_array( $current_range, array( 'custom', 'year', 'last_month', 'month', '7day' ) ) ) {
$current_range = '7day';
}
$this->check_current_range_nonce( $current_range );
$this->calculate_current_range( $current_range );
include WC()->plugin_path() . '/includes/admin/views/html-report-by-date.php';
}
/**
* Get chart widgets.
*
* @return array
*/
public function get_chart_widgets() {
$widgets = array();
$widgets[] = array(
'title' => '',
'callback' => array( $this, 'coupons_widget' ),
);
return $widgets;
}
/**
* Output coupons widget.
*/
public function coupons_widget() {
?>
<h4 class="section_title"><span><?php esc_html_e( 'Filter by coupon', 'woocommerce' ); ?></span></h4>
<div class="section">
<form method="GET">
<div>
<?php
$used_coupons = $this->get_order_report_data(
array(
'data' => array(
'order_item_name' => array(
'type' => 'order_item',
'order_item_type' => 'coupon',
'function' => '',
'distinct' => true,
'name' => 'order_item_name',
),
),
'where' => array(
array(
'key' => 'order_item_type',
'value' => 'coupon',
'operator' => '=',
),
),
'query_type' => 'get_col',
'filter_range' => false,
)
);
if ( ! empty( $used_coupons ) && is_array( $used_coupons ) ) :
?>
<select id="coupon_codes" name="coupon_codes" class="wc-enhanced-select" data-placeholder="<?php esc_attr_e( 'Choose coupons…', 'woocommerce' ); ?>" style="width:100%;">
<option value=""><?php esc_html_e( 'All coupons', 'woocommerce' ); ?></option>
<?php
foreach ( $used_coupons as $coupon ) {
echo '<option value="' . esc_attr( $coupon ) . '"' . wc_selected( $coupon, $this->coupon_codes ) . '>' . esc_html( $coupon ) . '</option>';
}
?>
</select>
<?php // @codingStandardsIgnoreStart ?>
<button type="submit" class="submit button" value="<?php esc_attr_e( 'Show', 'woocommerce' ); ?>"><?php esc_html_e( 'Show', 'woocommerce' ); ?></button>
<input type="hidden" name="range" value="<?php echo ( ! empty( $_GET['range'] ) ) ? esc_attr( wp_unslash( $_GET['range'] ) ) : ''; ?>" />
<input type="hidden" name="start_date" value="<?php echo ( ! empty( $_GET['start_date'] ) ) ? esc_attr( wp_unslash( $_GET['start_date'] ) ) : ''; ?>" />
<input type="hidden" name="end_date" value="<?php echo ( ! empty( $_GET['end_date'] ) ) ? esc_attr( wp_unslash( $_GET['end_date'] ) ) : ''; ?>" />
<input type="hidden" name="page" value="<?php echo ( ! empty( $_GET['page'] ) ) ? esc_attr( wp_unslash( $_GET['page'] ) ) : ''; ?>" />
<input type="hidden" name="tab" value="<?php echo ( ! empty( $_GET['tab'] ) ) ? esc_attr( wp_unslash( $_GET['tab'] ) ) : ''; ?>" />
<input type="hidden" name="report" value="<?php echo ( ! empty( $_GET['report'] ) ) ? esc_attr( wp_unslash( $_GET['report'] ) ) : ''; ?>" />
<?php // @codingStandardsIgnoreEnd ?>
<?php else : ?>
<span><?php esc_html_e( 'No used coupons found', 'woocommerce' ); ?></span>
<?php endif; ?>
</div>
</form>
</div>
<h4 class="section_title"><span><?php esc_html_e( 'Most popular', 'woocommerce' ); ?></span></h4>
<div class="section">
<table cellspacing="0">
<?php
$most_popular = $this->get_order_report_data(
array(
'data' => array(
'order_item_name' => array(
'type' => 'order_item',
'order_item_type' => 'coupon',
'function' => '',
'name' => 'coupon_code',
),
'order_item_id' => array(
'type' => 'order_item',
'order_item_type' => 'coupon',
'function' => 'COUNT',
'name' => 'coupon_count',
),
),
'where' => array(
array(
'type' => 'order_item',
'key' => 'order_item_type',
'value' => 'coupon',
'operator' => '=',
),
),
'order_by' => 'coupon_count DESC',
'group_by' => 'order_item_name',
'limit' => 12,
'query_type' => 'get_results',
'filter_range' => true,
)
);
if ( ! empty( $most_popular ) && is_array( $most_popular ) ) {
foreach ( $most_popular as $coupon ) {
echo '<tr class="' . ( in_array( $coupon->coupon_code, $this->coupon_codes ) ? 'active' : '' ) . '">
<td class="count" width="1%">' . esc_html( $coupon->coupon_count ) . '</td>
<td class="name"><a href="' . esc_url( add_query_arg( 'coupon_codes', $coupon->coupon_code ) ) . '">' . esc_html( $coupon->coupon_code ) . '</a></td>
</tr>';
}
} else {
echo '<tr><td colspan="2">' . esc_html__( 'No coupons found in range', 'woocommerce' ) . '</td></tr>';
}
?>
</table>
</div>
<h4 class="section_title"><span><?php esc_html_e( 'Most discount', 'woocommerce' ); ?></span></h4>
<div class="section">
<table cellspacing="0">
<?php
$most_discount = $this->get_order_report_data(
array(
'data' => array(
'order_item_name' => array(
'type' => 'order_item',
'order_item_type' => 'coupon',
'function' => '',
'name' => 'coupon_code',
),
'discount_amount' => array(
'type' => 'order_item_meta',
'order_item_type' => 'coupon',
'function' => 'SUM',
'name' => 'discount_amount',
),
),
'where' => array(
array(
'type' => 'order_item',
'key' => 'order_item_type',
'value' => 'coupon',
'operator' => '=',
),
),
'order_by' => 'discount_amount DESC',
'group_by' => 'order_item_name',
'limit' => 12,
'query_type' => 'get_results',
'filter_range' => true,
)
);
if ( ! empty( $most_discount ) && is_array( $most_discount ) ) {
foreach ( $most_discount as $coupon ) {
// @codingStandardsIgnoreStart
echo '<tr class="' . ( in_array( $coupon->coupon_code, $this->coupon_codes ) ? 'active' : '' ) . '">
<td class="count" width="1%">' . wc_price( $coupon->discount_amount ) . '</td>
<td class="name"><a href="' . esc_url( add_query_arg( 'coupon_codes', $coupon->coupon_code ) ) . '">' . esc_html( $coupon->coupon_code ) . '</a></td>
</tr>';
// @codingStandardsIgnoreEnd
}
} else {
echo '<tr><td colspan="3">' . esc_html__( 'No coupons found in range', 'woocommerce' ) . '</td></tr>';
}
?>
</table>
</div>
<script type="text/javascript">
jQuery( '.section_title' ).on( 'click', function() {
var next_section = jQuery( this ).next( '.section' );
if ( jQuery( next_section ).is( ':visible' ) ) {
return false;
}
jQuery( '.section:visible' ).slideUp();
jQuery( '.section_title' ).removeClass( 'open' );
jQuery( this ).addClass( 'open' ).next( '.section' ).slideDown();
return false;
} );
jQuery( '.section' ).slideUp( 100, function() {
<?php if ( empty( $this->coupon_codes ) ) : ?>
jQuery( '.section_title:eq(1)' ).trigger( 'click' );
<?php else : ?>
jQuery( '.section_title:eq(0)' ).trigger( 'click' );
<?php endif; ?>
} );
</script>
<?php
}
/**
* Output an export link.
*/
public function get_export_button() {
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day';
?>
<a
href="#"
download="report-<?php echo esc_attr( $current_range ); ?>-<?php echo esc_attr( date_i18n( 'Y-m-d', current_time( 'timestamp' ) ) ); ?>.csv"
class="export_csv"
data-export="chart"
data-xaxes="<?php esc_attr_e( 'Date', 'woocommerce' ); ?>"
data-groupby="<?php echo esc_attr( $this->chart_groupby ); ?>"
>
<?php esc_html_e( 'Export CSV', 'woocommerce' ); ?>
</a>
<?php
}
/**
* Get the main chart.
*/
public function get_main_chart() {
global $wp_locale;
// Get orders and dates in range - we want the SUM of order totals, COUNT of order items, COUNT of orders, and the date.
$order_coupon_counts_query = array(
'data' => array(
'order_item_name' => array(
'type' => 'order_item',
'order_item_type' => 'coupon',
'function' => 'COUNT',
'name' => 'order_coupon_count',
),
'post_date' => array(
'type' => 'post_data',
'function' => '',
'name' => 'post_date',
),
),
'where' => array(
array(
'key' => 'order_item_type',
'value' => 'coupon',
'operator' => '=',
),
),
'group_by' => $this->group_by_query,
'order_by' => 'post_date ASC',
'query_type' => 'get_results',
'filter_range' => true,
'order_types' => wc_get_order_types( 'order-count' ),
);
$order_discount_amounts_query = array(
'data' => array(
'discount_amount' => array(
'type' => 'order_item_meta',
'order_item_type' => 'coupon',
'function' => 'SUM',
'name' => 'discount_amount',
),
'post_date' => array(
'type' => 'post_data',
'function' => '',
'name' => 'post_date',
),
),
'where' => array(
array(
'key' => 'order_item_type',
'value' => 'coupon',
'operator' => '=',
),
),
'group_by' => $this->group_by_query . ', order_item_name',
'order_by' => 'post_date ASC',
'query_type' => 'get_results',
'filter_range' => true,
'order_types' => wc_get_order_types( 'order-count' ),
);
if ( ! empty( $this->coupon_codes ) ) {
$coupon_code_query = array(
'type' => 'order_item',
'key' => 'order_item_name',
'value' => $this->coupon_codes,
'operator' => 'IN',
);
$order_coupon_counts_query['where'][] = $coupon_code_query;
$order_discount_amounts_query['where'][] = $coupon_code_query;
}
$order_coupon_counts = $this->get_order_report_data( $order_coupon_counts_query );
$order_discount_amounts = $this->get_order_report_data( $order_discount_amounts_query );
// Prepare data for report.
$order_coupon_counts = $this->prepare_chart_data( $order_coupon_counts, 'post_date', 'order_coupon_count', $this->chart_interval, $this->start_date, $this->chart_groupby );
$order_discount_amounts = $this->prepare_chart_data( $order_discount_amounts, 'post_date', 'discount_amount', $this->chart_interval, $this->start_date, $this->chart_groupby );
// Encode in json format.
$chart_data = wp_json_encode(
array(
'order_coupon_counts' => array_values( $order_coupon_counts ),
'order_discount_amounts' => array_values( $order_discount_amounts ),
)
);
?>
<div class="chart-container">
<div class="chart-placeholder main"></div>
</div>
<script type="text/javascript">
var main_chart;
jQuery(function(){
var order_data = JSON.parse( decodeURIComponent( '<?php echo rawurlencode( $chart_data ); ?>' ) );
var drawGraph = function( highlight ) {
var series = [
{
label: "<?php echo esc_js( __( 'Number of coupons used', 'woocommerce' ) ); ?>",
data: order_data.order_coupon_counts,
color: '<?php echo esc_js( $this->chart_colours['coupon_count'] ); ?>',
bars: { fillColor: '<?php echo esc_js( $this->chart_colours['coupon_count'] ); ?>', fill: true, show: true, lineWidth: 0, barWidth: <?php echo esc_js( $this->barwidth ); ?> * 0.5, align: 'center' },
shadowSize: 0,
hoverable: false
},
{
label: "<?php echo esc_js( __( 'Discount amount', 'woocommerce' ) ); ?>",
data: order_data.order_discount_amounts,
yaxis: 2,
color: '<?php echo esc_js( $this->chart_colours['discount_amount'] ); ?>',
points: { show: true, radius: 5, lineWidth: 3, fillColor: '#fff', fill: true },
lines: { show: true, lineWidth: 4, fill: false },
shadowSize: 0,
<?php echo $this->get_currency_tooltip(); ?><?php // @codingStandardsIgnoreLine ?>
}
];
if ( highlight !== 'undefined' && series[ highlight ] ) {
highlight_series = series[ highlight ];
highlight_series.color = '#9c5d90';
if ( highlight_series.bars )
highlight_series.bars.fillColor = '#9c5d90';
if ( highlight_series.lines ) {
highlight_series.lines.lineWidth = 5;
}
}
main_chart = jQuery.plot(
jQuery('.chart-placeholder.main'),
series,
{
legend: {
show: false
},
grid: {
color: '#aaa',
borderColor: 'transparent',
borderWidth: 0,
hoverable: true
},
xaxes: [ {
color: '#aaa',
position: "bottom",
tickColor: 'transparent',
mode: "time",
timeformat: "<?php echo ( 'day' === $this->chart_groupby ) ? '%d %b' : '%b'; ?>",
monthNames: JSON.parse( decodeURIComponent( '<?php echo rawurlencode( wp_json_encode( array_values( $wp_locale->month_abbrev ) ) ); ?>' ) ),
tickLength: 1,
minTickSize: [1, "<?php echo esc_js( $this->chart_groupby ); ?>"],
font: {
color: "#aaa"
}
} ],
yaxes: [
{
min: 0,
minTickSize: 1,
tickDecimals: 0,
color: '#ecf0f1',
font: { color: "#aaa" }
},
{
position: "right",
min: 0,
tickDecimals: 2,
alignTicksWithAxis: 1,
color: 'transparent',
font: { color: "#aaa" }
}
],
}
);
jQuery('.chart-placeholder').trigger( 'resize' );
}
drawGraph();
jQuery('.highlight_series').on( 'mouseenter',
function() {
drawGraph( jQuery(this).data('series') );
} ).on( 'mouseleave',
function() {
drawGraph();
}
);
});
</script>
<?php
}
}
class-wc-report-customer-list.php 0000644 00000021062 15155076532 0013124 0 ustar 00 <?php
/**
* Class WC_Report_Customer_List file.
*
* @package WooCommerce\Reports
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
if ( ! class_exists( 'WP_List_Table' ) ) {
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
}
/**
* WC_Report_Customer_List.
*
* @package WooCommerce\Admin\Reports
* @version 2.1.0
*/
class WC_Report_Customer_List extends WP_List_Table {
/**
* Constructor.
*/
public function __construct() {
parent::__construct(
array(
'singular' => 'customer',
'plural' => 'customers',
'ajax' => false,
)
);
}
/**
* No items found text.
*/
public function no_items() {
esc_html_e( 'No customers found.', 'woocommerce' );
}
/**
* Output the report.
*/
public function output_report() {
$this->prepare_items();
echo '<div id="poststuff" class="woocommerce-reports-wide">';
if ( ! empty( $_GET['link_orders'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'link_orders' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
$linked = wc_update_new_customer_past_orders( absint( $_GET['link_orders'] ) );
/* translators: single or plural number of orders */
echo '<div class="updated"><p>' . sprintf( esc_html( _n( '%s previous order linked', '%s previous orders linked', $linked, 'woocommerce' ), $linked ) ) . '</p></div>';
}
if ( ! empty( $_GET['refresh'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'refresh' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
$user_id = absint( $_GET['refresh'] );
$user = get_user_by( 'id', $user_id );
delete_user_meta( $user_id, '_money_spent' );
delete_user_meta( $user_id, '_order_count' );
delete_user_meta( $user_id, '_last_order' );
/* translators: User display name */
echo '<div class="updated"><p>' . sprintf( esc_html__( 'Refreshed stats for %s', 'woocommerce' ), esc_html( $user->display_name ) ) . '</p></div>';
}
echo '<form method="post" id="woocommerce_customers">';
$this->search_box( __( 'Search customers', 'woocommerce' ), 'customer_search' );
$this->display();
echo '</form>';
echo '</div>';
}
/**
* Get column value.
*
* @param WP_User $user WP User object.
* @param string $column_name Column name.
* @return string
*/
public function column_default( $user, $column_name ) {
switch ( $column_name ) {
case 'customer_name':
if ( $user->last_name && $user->first_name ) {
return $user->last_name . ', ' . $user->first_name;
} else {
return '-';
}
case 'username':
return $user->user_login;
case 'location':
$state_code = get_user_meta( $user->ID, 'billing_state', true );
$country_code = get_user_meta( $user->ID, 'billing_country', true );
$state = isset( WC()->countries->states[ $country_code ][ $state_code ] ) ? WC()->countries->states[ $country_code ][ $state_code ] : $state_code;
$country = isset( WC()->countries->countries[ $country_code ] ) ? WC()->countries->countries[ $country_code ] : $country_code;
$value = '';
if ( $state ) {
$value .= $state . ', ';
}
$value .= $country;
if ( $value ) {
return $value;
} else {
return '-';
}
case 'email':
return '<a href="mailto:' . $user->user_email . '">' . $user->user_email . '</a>';
case 'spent':
return wc_price( wc_get_customer_total_spent( $user->ID ) );
case 'orders':
return wc_get_customer_order_count( $user->ID );
case 'last_order':
$orders = wc_get_orders(
array(
'limit' => 1,
'status' => array_map( 'wc_get_order_status_name', wc_get_is_paid_statuses() ),
'customer' => $user->ID,
)
);
if ( ! empty( $orders ) ) {
$order = $orders[0];
return '<a href="' . admin_url( 'post.php?post=' . $order->get_id() . '&action=edit' ) . '">' . _x( '#', 'hash before order number', 'woocommerce' ) . $order->get_order_number() . '</a> – ' . wc_format_datetime( $order->get_date_created() );
} else {
return '-';
}
break;
case 'wc_actions':
ob_start();
?><p>
<?php
do_action( 'woocommerce_admin_user_actions_start', $user );
$actions = array();
$actions['refresh'] = array(
'url' => wp_nonce_url( add_query_arg( 'refresh', $user->ID ), 'refresh' ),
'name' => __( 'Refresh stats', 'woocommerce' ),
'action' => 'refresh',
);
$actions['edit'] = array(
'url' => admin_url( 'user-edit.php?user_id=' . $user->ID ),
'name' => __( 'Edit', 'woocommerce' ),
'action' => 'edit',
);
$actions['view'] = array(
'url' => admin_url( 'edit.php?post_type=shop_order&_customer_user=' . $user->ID ),
'name' => __( 'View orders', 'woocommerce' ),
'action' => 'view',
);
$orders = wc_get_orders(
array(
'limit' => 1,
'status' => array_map( 'wc_get_order_status_name', wc_get_is_paid_statuses() ),
'customer' => array( array( 0, $user->user_email ) ),
)
);
if ( $orders ) {
$actions['link'] = array(
'url' => wp_nonce_url( add_query_arg( 'link_orders', $user->ID ), 'link_orders' ),
'name' => __( 'Link previous orders', 'woocommerce' ),
'action' => 'link',
);
}
$actions = apply_filters( 'woocommerce_admin_user_actions', $actions, $user );
foreach ( $actions as $action ) {
printf( '<a class="button tips %s" href="%s" data-tip="%s">%s</a>', esc_attr( $action['action'] ), esc_url( $action['url'] ), esc_attr( $action['name'] ), esc_attr( $action['name'] ) );
}
do_action( 'woocommerce_admin_user_actions_end', $user );
?>
</p>
<?php
$user_actions = ob_get_contents();
ob_end_clean();
return $user_actions;
}
return '';
}
/**
* Get columns.
*
* @return array
*/
public function get_columns() {
$columns = array(
'customer_name' => __( 'Name (Last, First)', 'woocommerce' ),
'username' => __( 'Username', 'woocommerce' ),
'email' => __( 'Email', 'woocommerce' ),
'location' => __( 'Location', 'woocommerce' ),
'orders' => __( 'Orders', 'woocommerce' ),
'spent' => __( 'Money spent', 'woocommerce' ),
'last_order' => __( 'Last order', 'woocommerce' ),
'wc_actions' => __( 'Actions', 'woocommerce' ),
);
return $columns;
}
/**
* Order users by name.
*
* @param WP_User_Query $query Query that gets passed through.
* @return WP_User_Query
*/
public function order_by_last_name( $query ) {
global $wpdb;
$s = ! empty( $_REQUEST['s'] ) ? wp_unslash( $_REQUEST['s'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$query->query_from .= " LEFT JOIN {$wpdb->usermeta} as meta2 ON ({$wpdb->users}.ID = meta2.user_id) ";
$query->query_where .= " AND meta2.meta_key = 'last_name' ";
$query->query_orderby = ' ORDER BY meta2.meta_value, user_login ASC ';
if ( $s ) {
$query->query_from .= " LEFT JOIN {$wpdb->usermeta} as meta3 ON ({$wpdb->users}.ID = meta3.user_id)";
$query->query_where .= " AND ( user_login LIKE '%" . esc_sql( str_replace( '*', '', $s ) ) . "%' OR user_nicename LIKE '%" . esc_sql( str_replace( '*', '', $s ) ) . "%' OR meta3.meta_value LIKE '%" . esc_sql( str_replace( '*', '', $s ) ) . "%' ) ";
$query->query_orderby = ' GROUP BY ID ' . $query->query_orderby;
}
return $query;
}
/**
* Prepare customer list items.
*/
public function prepare_items() {
$current_page = absint( $this->get_pagenum() );
$per_page = 20;
/**
* Init column headers.
*/
$this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns() );
add_action( 'pre_user_query', array( $this, 'order_by_last_name' ) );
/**
* Get users.
*/
$admin_users = new WP_User_Query(
array(
'role' => 'administrator',
'fields' => 'ID',
)
);
$manager_users = new WP_User_Query(
array(
'role' => 'shop_manager',
'fields' => 'ID',
)
);
$query = new WP_User_Query(
apply_filters(
'woocommerce_admin_report_customer_list_user_query_args',
array(
'exclude' => array_merge( $admin_users->get_results(), $manager_users->get_results() ),
'number' => $per_page,
'offset' => ( $current_page - 1 ) * $per_page,
)
)
);
$this->items = $query->get_results();
remove_action( 'pre_user_query', array( $this, 'order_by_last_name' ) );
/**
* Pagination.
*/
$this->set_pagination_args(
array(
'total_items' => $query->total_users,
'per_page' => $per_page,
'total_pages' => ceil( $query->total_users / $per_page ),
)
);
}
}
class-wc-report-customers.php 0000644 00000027071 15155076532 0012344 0 ustar 00 <?php
/**
* Class WC_Report_Customers file.
*
* @package WooCommerce\Reports
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* WC_Report_Customers
*
* @package WooCommerce\Admin\Reports
* @version 2.1.0
*/
class WC_Report_Customers extends WC_Admin_Report {
/**
* Chart colors.
*
* @var array
*/
public $chart_colours = array();
/**
* Customers.
*
* @var array
*/
public $customers = array();
/**
* Get the legend for the main chart sidebar.
*
* @return array
*/
public function get_chart_legend() {
$legend = array();
$legend[] = array(
/* translators: %s: signups amount */
'title' => sprintf( __( '%s signups in this period', 'woocommerce' ), '<strong>' . count( $this->customers ) . '</strong>' ),
'color' => $this->chart_colours['signups'],
'highlight_series' => 2,
);
return $legend;
}
/**
* Get chart widgets.
*
* @return array
*/
public function get_chart_widgets() {
$widgets = array();
$widgets[] = array(
'title' => '',
'callback' => array( $this, 'customers_vs_guests' ),
);
return $widgets;
}
/**
* Output customers vs guests chart.
*/
public function customers_vs_guests() {
$customer_order_totals = $this->get_order_report_data(
array(
'data' => array(
'ID' => array(
'type' => 'post_data',
'function' => 'COUNT',
'name' => 'total_orders',
),
),
'where_meta' => array(
array(
'meta_key' => '_customer_user',
'meta_value' => '0',
'operator' => '>',
),
),
'filter_range' => true,
)
);
$guest_order_totals = $this->get_order_report_data(
array(
'data' => array(
'ID' => array(
'type' => 'post_data',
'function' => 'COUNT',
'name' => 'total_orders',
),
),
'where_meta' => array(
array(
'meta_key' => '_customer_user',
'meta_value' => '0',
'operator' => '=',
),
),
'filter_range' => true,
)
);
?>
<div class="chart-container">
<div class="chart-placeholder customers_vs_guests pie-chart" style="height:200px"></div>
<ul class="pie-chart-legend">
<li style="border-color: <?php echo esc_attr( $this->chart_colours['customers'] ); ?>"><?php esc_html_e( 'Customer sales', 'woocommerce' ); ?></li>
<li style="border-color: <?php echo esc_attr( $this->chart_colours['guests'] ); ?>"><?php esc_html_e( 'Guest sales', 'woocommerce' ); ?></li>
</ul>
</div>
<script type="text/javascript">
jQuery(function(){
jQuery.plot(
jQuery('.chart-placeholder.customers_vs_guests'),
[
{
label: '<?php esc_html_e( 'Customer orders', 'woocommerce' ); ?>',
data: "<?php echo esc_html( $customer_order_totals->total_orders ); ?>",
color: '<?php echo esc_html( $this->chart_colours['customers'] ); ?>'
},
{
label: '<?php esc_html_e( 'Guest orders', 'woocommerce' ); ?>',
data: "<?php echo esc_html( $guest_order_totals->total_orders ); ?>",
color: '<?php echo esc_html( $this->chart_colours['guests'] ); ?>'
}
],
{
grid: {
hoverable: true
},
series: {
pie: {
show: true,
radius: 1,
innerRadius: 0.6,
label: {
show: false
}
},
enable_tooltip: true,
append_tooltip: "<?php echo esc_html( ' ' . __( 'orders', 'woocommerce' ) ); ?>",
},
legend: {
show: false
}
}
);
jQuery('.chart-placeholder.customers_vs_guests').trigger( 'resize' );
});
</script>
<?php
}
/**
* Output the report.
*/
public function output_report() {
$ranges = array(
'year' => __( 'Year', 'woocommerce' ),
'last_month' => __( 'Last month', 'woocommerce' ),
'month' => __( 'This month', 'woocommerce' ),
'7day' => __( 'Last 7 days', 'woocommerce' ),
);
$this->chart_colours = array(
'signups' => '#3498db',
'customers' => '#1abc9c',
'guests' => '#8fdece',
);
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day';
if ( ! in_array( $current_range, array( 'custom', 'year', 'last_month', 'month', '7day' ), true ) ) {
$current_range = '7day';
}
$this->check_current_range_nonce( $current_range );
$this->calculate_current_range( $current_range );
$admin_users = new WP_User_Query(
array(
'role' => 'administrator',
'fields' => 'ID',
)
);
$manager_users = new WP_User_Query(
array(
'role' => 'shop_manager',
'fields' => 'ID',
)
);
$users_query = new WP_User_Query(
apply_filters(
'woocommerce_admin_report_customers_user_query_args',
array(
'fields' => array( 'user_registered' ),
'exclude' => array_merge( $admin_users->get_results(), $manager_users->get_results() ),
)
)
);
$this->customers = $users_query->get_results();
foreach ( $this->customers as $key => $customer ) {
if ( strtotime( $customer->user_registered ) < $this->start_date || strtotime( $customer->user_registered ) > $this->end_date ) {
unset( $this->customers[ $key ] );
}
}
include WC()->plugin_path() . '/includes/admin/views/html-report-by-date.php';
}
/**
* Output an export link.
*/
public function get_export_button() {
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day';
?>
<a
href="#"
download="report-<?php echo esc_attr( $current_range ); ?>-<?php echo esc_attr( date_i18n( 'Y-m-d', current_time( 'timestamp' ) ) ); ?>.csv"
class="export_csv"
data-export="chart"
data-xaxes="<?php esc_attr_e( 'Date', 'woocommerce' ); ?>"
data-groupby="<?php echo esc_attr( $this->chart_groupby ); ?>"
>
<?php esc_html_e( 'Export CSV', 'woocommerce' ); ?>
</a>
<?php
}
/**
* Output the main chart.
*/
public function get_main_chart() {
global $wp_locale;
$customer_orders = $this->get_order_report_data(
array(
'data' => array(
'ID' => array(
'type' => 'post_data',
'function' => 'COUNT',
'name' => 'total_orders',
),
'post_date' => array(
'type' => 'post_data',
'function' => '',
'name' => 'post_date',
),
),
'where_meta' => array(
array(
'meta_key' => '_customer_user',
'meta_value' => '0',
'operator' => '>',
),
),
'group_by' => $this->group_by_query,
'order_by' => 'post_date ASC',
'query_type' => 'get_results',
'filter_range' => true,
)
);
$guest_orders = $this->get_order_report_data(
array(
'data' => array(
'ID' => array(
'type' => 'post_data',
'function' => 'COUNT',
'name' => 'total_orders',
),
'post_date' => array(
'type' => 'post_data',
'function' => '',
'name' => 'post_date',
),
),
'where_meta' => array(
array(
'meta_key' => '_customer_user',
'meta_value' => '0',
'operator' => '=',
),
),
'group_by' => $this->group_by_query,
'order_by' => 'post_date ASC',
'query_type' => 'get_results',
'filter_range' => true,
)
);
$signups = $this->prepare_chart_data( $this->customers, 'user_registered', '', $this->chart_interval, $this->start_date, $this->chart_groupby );
$customer_orders = $this->prepare_chart_data( $customer_orders, 'post_date', 'total_orders', $this->chart_interval, $this->start_date, $this->chart_groupby );
$guest_orders = $this->prepare_chart_data( $guest_orders, 'post_date', 'total_orders', $this->chart_interval, $this->start_date, $this->chart_groupby );
$chart_data = wp_json_encode(
array(
'signups' => array_values( $signups ),
'customer_orders' => array_values( $customer_orders ),
'guest_orders' => array_values( $guest_orders ),
)
);
?>
<div class="chart-container">
<div class="chart-placeholder main"></div>
</div>
<script type="text/javascript">
var main_chart;
jQuery(function(){
var chart_data = JSON.parse( decodeURIComponent( '<?php echo rawurlencode( $chart_data ); ?>' ) );
var drawGraph = function( highlight ) {
var series = [
{
label: "<?php echo esc_js( __( 'Customer orders', 'woocommerce' ) ); ?>",
data: chart_data.customer_orders,
color: '<?php echo esc_html( $this->chart_colours['customers'] ); ?>',
bars: { fillColor: '<?php echo esc_html( $this->chart_colours['customers'] ); ?>', fill: true, show: true, lineWidth: 0, barWidth: <?php echo esc_html( $this->barwidth ); ?> * 0.5, align: 'center' },
shadowSize: 0,
enable_tooltip: true,
append_tooltip: "<?php echo esc_html( ' ' . __( 'customer orders', 'woocommerce' ) ); ?>",
stack: true,
},
{
label: "<?php echo esc_js( __( 'Guest orders', 'woocommerce' ) ); ?>",
data: chart_data.guest_orders,
color: '<?php echo esc_html( $this->chart_colours['guests'] ); ?>',
bars: { fillColor: '<?php echo esc_html( $this->chart_colours['guests'] ); ?>', fill: true, show: true, lineWidth: 0, barWidth: <?php echo esc_html( $this->barwidth ); ?> * 0.5, align: 'center' },
shadowSize: 0,
enable_tooltip: true,
append_tooltip: "<?php echo esc_html( ' ' . __( 'guest orders', 'woocommerce' ) ); ?>",
stack: true,
},
{
label: "<?php echo esc_js( __( 'Signups', 'woocommerce' ) ); ?>",
data: chart_data.signups,
color: '<?php echo esc_html( $this->chart_colours['signups'] ); ?>',
points: { show: true, radius: 5, lineWidth: 3, fillColor: '#fff', fill: true },
lines: { show: true, lineWidth: 4, fill: false },
shadowSize: 0,
enable_tooltip: true,
append_tooltip: "<?php echo esc_html( ' ' . __( 'new users', 'woocommerce' ) ); ?>",
stack: false
},
];
if ( highlight !== 'undefined' && series[ highlight ] ) {
highlight_series = series[ highlight ];
highlight_series.color = '#9c5d90';
if ( highlight_series.bars )
highlight_series.bars.fillColor = '#9c5d90';
if ( highlight_series.lines ) {
highlight_series.lines.lineWidth = 5;
}
}
main_chart = jQuery.plot(
jQuery('.chart-placeholder.main'),
series,
{
legend: {
show: false
},
grid: {
color: '#aaa',
borderColor: 'transparent',
borderWidth: 0,
hoverable: true
},
xaxes: [ {
color: '#aaa',
position: "bottom",
tickColor: 'transparent',
mode: "time",
timeformat: "<?php echo ( 'day' === $this->chart_groupby ) ? '%d %b' : '%b'; ?>",
monthNames: JSON.parse( decodeURIComponent( '<?php echo rawurlencode( wp_json_encode( array_values( $wp_locale->month_abbrev ) ) ); ?>' ) ),
tickLength: 1,
minTickSize: [1, "<?php echo esc_html( $this->chart_groupby ); ?>"],
tickSize: [1, "<?php echo esc_html( $this->chart_groupby ); ?>"],
font: {
color: "#aaa"
}
} ],
yaxes: [
{
min: 0,
minTickSize: 1,
tickDecimals: 0,
color: '#ecf0f1',
font: { color: "#aaa" }
}
],
}
);
jQuery('.chart-placeholder').trigger( 'resize' );
}
drawGraph();
jQuery('.highlight_series').on( 'mouseenter',
function() {
drawGraph( jQuery(this).data('series') );
} ).on( 'mouseleave',
function() {
drawGraph();
}
);
});
</script>
<?php
}
}
class-wc-report-downloads.php 0000644 00000024720 15155076532 0012310 0 ustar 00 <?php
/**
* Download report.
*
* @author WooThemes
* @category Admin
* @package WooCommerce\Admin\Reports
* @version 3.3.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'WP_List_Table' ) ) {
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
}
/**
* WC_Report_Downloads.
*/
class WC_Report_Downloads extends WP_List_Table {
/**
* Max items.
*
* @var int
*/
protected $max_items;
/**
* Constructor.
*/
public function __construct() {
parent::__construct(
array(
'singular' => 'download',
'plural' => 'downloads',
'ajax' => false,
)
);
}
/**
* Don't need this.
*
* @param string $position Top or bottom.
*/
public function display_tablenav( $position ) {
if ( 'top' !== $position ) {
parent::display_tablenav( $position );
}
}
/**
* Output the report.
*/
public function output_report() {
$this->prepare_items();
// Subtitle for permission if set.
if ( ! empty( $_GET['permission_id'] ) ) { // WPCS: input var ok.
$permission_id = absint( $_GET['permission_id'] ); // WPCS: input var ok.
// Load the permission, order, etc. so we can render more information.
$permission = null;
$product = null;
try {
$permission = new WC_Customer_Download( $permission_id );
$product = wc_get_product( $permission->product_id );
} catch ( Exception $e ) {
wp_die( sprintf( esc_html__( 'Permission #%d not found.', 'woocommerce' ), esc_html( $permission_id ) ) );
}
}
echo '<h1>' . esc_html__( 'Customer downloads', 'woocommerce' );
$filters = $this->get_filter_vars();
$filter_list = array();
$filter_names = array(
'product_id' => __( 'Product', 'woocommerce' ),
'download_id' => __( 'File ID', 'woocommerce' ),
'permission_id' => __( 'Permission ID', 'woocommerce' ),
'order_id' => __( 'Order', 'woocommerce' ),
'user_id' => __( 'User', 'woocommerce' ),
'user_ip_address' => __( 'IP address', 'woocommerce' ),
);
foreach ( $filters as $key => $value ) {
if ( is_null( $value ) ) {
continue;
}
switch ( $key ) {
case 'order_id':
$order = wc_get_order( $value );
if ( $order ) {
$display_value = _x( '#', 'hash before order number', 'woocommerce' ) . $order->get_order_number();
} else {
break 2;
}
break;
case 'product_id':
$product = wc_get_product( $value );
if ( $product ) {
$display_value = $product->get_formatted_name();
} else {
break 2;
}
break;
default:
$display_value = $value;
break;
}
$filter_list[] = $filter_names[ $key ] . ' ' . $display_value . ' <a href="' . esc_url( remove_query_arg( $key ) ) . '" class="woocommerce-reports-remove-filter">×</a>';
}
echo '</h1>';
echo '<div id="active-filters" class="woocommerce-reports-wide"><h2>';
echo esc_html__( 'Active filters', 'woocommerce' ) . ': ';
echo $filter_list ? wp_kses_post( implode( ', ', $filter_list ) ) : '';
echo '</h2></div>';
echo '<div id="poststuff" class="woocommerce-reports-wide">';
$this->display();
echo '</div>';
}
/**
* Get column value.
*
* @param mixed $item Item being displayed.
* @param string $column_name Column name.
*/
public function column_default( $item, $column_name ) {
$permission = null;
$product = null;
try {
$permission = new WC_Customer_Download( $item->permission_id );
$product = wc_get_product( $permission->product_id );
} catch ( Exception $e ) {
// Ok to continue rendering other information even if permission and/or product is not found.
return;
}
switch ( $column_name ) {
case 'timestamp':
echo esc_html( $item->timestamp );
break;
case 'product':
if ( ! empty( $product ) ) {
edit_post_link( esc_html( $product->get_formatted_name() ), '', '', $product->get_id(), 'view-link' );
echo '<div class="row-actions">';
echo '<a href="' . esc_url( add_query_arg( 'product_id', $product->get_id() ) ) . '">' . esc_html__( 'Filter by product', 'woocommerce' ) . '</a>';
echo '</div>';
}
break;
case 'file':
if ( ! empty( $permission ) && ! empty( $product ) ) {
// File information.
$file = $product->get_file( $permission->get_download_id() );
if ( false === $file ) {
echo esc_html__( 'File does not exist', 'woocommerce' );
} else {
echo esc_html( $file->get_name() . ' - ' . basename( $file->get_file() ) );
echo '<div class="row-actions">';
echo '<a href="' . esc_url( add_query_arg( 'download_id', $permission->get_download_id() ) ) . '">' . esc_html__( 'Filter by file', 'woocommerce' ) . '</a>';
echo '</div>';
}
}
break;
case 'order':
if ( ! empty( $permission ) && ( $order = wc_get_order( $permission->order_id ) ) ) {
edit_post_link( esc_html( _x( '#', 'hash before order number', 'woocommerce' ) . $order->get_order_number() ), '', '', $permission->order_id, 'view-link' );
echo '<div class="row-actions">';
echo '<a href="' . esc_url( add_query_arg( 'order_id', $order->get_id() ) ) . '">' . esc_html__( 'Filter by order', 'woocommerce' ) . '</a>';
echo '</div>';
}
break;
case 'user':
if ( $item->user_id > 0 ) {
$user = get_user_by( 'id', $item->user_id );
if ( ! empty( $user ) ) {
echo '<a href="' . esc_url( get_edit_user_link( $item->user_id ) ) . '">' . esc_html( $user->display_name ) . '</a>';
echo '<div class="row-actions">';
echo '<a href="' . esc_url( add_query_arg( 'user_id', $item->user_id ) ) . '">' . esc_html__( 'Filter by user', 'woocommerce' ) . '</a>';
echo '</div>';
}
} else {
esc_html_e( 'Guest', 'woocommerce' );
}
break;
case 'user_ip_address':
echo esc_html( $item->user_ip_address );
echo '<div class="row-actions">';
echo '<a href="' . esc_url( add_query_arg( 'user_ip_address', $item->user_ip_address ) ) . '">' . esc_html__( 'Filter by IP address', 'woocommerce' ) . '</a>';
echo '</div>';
break;
}
}
/**
* Get columns.
*
* @return array
*/
public function get_columns() {
$columns = array(
'timestamp' => __( 'Timestamp', 'woocommerce' ),
'product' => __( 'Product', 'woocommerce' ),
'file' => __( 'File', 'woocommerce' ),
'order' => __( 'Order', 'woocommerce' ),
'user' => __( 'User', 'woocommerce' ),
'user_ip_address' => __( 'IP address', 'woocommerce' ),
);
return $columns;
}
/**
* Prepare download list items.
*/
public function prepare_items() {
$this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns() );
$current_page = absint( $this->get_pagenum() );
// Allow filtering per_page value, but ensure it's at least 1.
$per_page = max( 1, apply_filters( 'woocommerce_admin_downloads_report_downloads_per_page', 20 ) );
$this->get_items( $current_page, $per_page );
/**
* Pagination.
*/
$this->set_pagination_args(
array(
'total_items' => $this->max_items,
'per_page' => $per_page,
'total_pages' => ceil( $this->max_items / $per_page ),
)
);
}
/**
* No items found text.
*/
public function no_items() {
esc_html_e( 'No customer downloads found.', 'woocommerce' );
}
/**
* Get filters from querystring.
*
* @return object
*/
protected function get_filter_vars() {
$product_id = ! empty( $_GET['product_id'] ) ? absint( wp_unslash( $_GET['product_id'] ) ) : null; // WPCS: input var ok.
$download_id = ! empty( $_GET['download_id'] ) ? wc_clean( wp_unslash( $_GET['download_id'] ) ) : null; // WPCS: input var ok.
$permission_id = ! empty( $_GET['permission_id'] ) ? absint( wp_unslash( $_GET['permission_id'] ) ) : null; // WPCS: input var ok.
$order_id = ! empty( $_GET['order_id'] ) ? absint( wp_unslash( $_GET['order_id'] ) ) : null; // WPCS: input var ok.
$user_id = ! empty( $_GET['user_id'] ) ? absint( wp_unslash( $_GET['user_id'] ) ) : null; // WPCS: input var ok.
$user_ip_address = ! empty( $_GET['user_ip_address'] ) ? wc_clean( wp_unslash( $_GET['user_ip_address'] ) ) : null; // WPCS: input var ok.
return (object) array(
'product_id' => $product_id,
'download_id' => $download_id,
'permission_id' => $permission_id,
'order_id' => $order_id,
'user_id' => $user_id,
'user_ip_address' => $user_ip_address,
);
}
/**
* Get downloads matching criteria.
*
* @param int $current_page Current viewed page.
* @param int $per_page How many results to show per page.
*/
public function get_items( $current_page, $per_page ) {
global $wpdb;
$this->max_items = 0;
$this->items = array();
$filters = $this->get_filter_vars();
// Get downloads from database.
$table = $wpdb->prefix . WC_Customer_Download_Log_Data_Store::get_table_name();
$query_from = " FROM {$table} as downloads ";
if ( ! is_null( $filters->product_id ) || ! is_null( $filters->download_id ) || ! is_null( $filters->order_id ) ) {
$query_from .= " LEFT JOIN {$wpdb->prefix}woocommerce_downloadable_product_permissions as permissions on downloads.permission_id = permissions.permission_id ";
}
$query_from .= ' WHERE 1=1 ';
if ( ! is_null( $filters->product_id ) ) {
$query_from .= $wpdb->prepare( ' AND product_id = %d ', $filters->product_id );
}
if ( ! is_null( $filters->download_id ) ) {
$query_from .= $wpdb->prepare( ' AND download_id = %s ', $filters->download_id );
}
if ( ! is_null( $filters->order_id ) ) {
$query_from .= $wpdb->prepare( ' AND order_id = %d ', $filters->order_id );
}
if ( ! is_null( $filters->permission_id ) ) {
$query_from .= $wpdb->prepare( ' AND downloads.permission_id = %d ', $filters->permission_id );
}
if ( ! is_null( $filters->user_id ) ) {
$query_from .= $wpdb->prepare( ' AND downloads.user_id = %d ', $filters->user_id );
}
if ( ! is_null( $filters->user_ip_address ) ) {
$query_from .= $wpdb->prepare( ' AND user_ip_address = %s ', $filters->user_ip_address );
}
$query_from = apply_filters( 'woocommerce_report_downloads_query_from', $query_from );
$query_order = $wpdb->prepare( 'ORDER BY timestamp DESC LIMIT %d, %d;', ( $current_page - 1 ) * $per_page, $per_page );
$this->items = $wpdb->get_results( "SELECT * {$query_from} {$query_order}" ); // WPCS: cache ok, db call ok, unprepared SQL ok.
$this->max_items = $wpdb->get_var( "SELECT COUNT( DISTINCT download_log_id ) {$query_from};" ); // WPCS: cache ok, db call ok, unprepared SQL ok.
}
}
class-wc-report-low-in-stock.php 0000644 00000003376 15155076532 0012650 0 ustar 00 <?php
/**
* WC_Report_Low_In_Stock.
*
* @package WooCommerce\Admin\Reports
*/
defined( 'ABSPATH' ) || exit;
if ( ! class_exists( 'WC_Report_Stock' ) ) {
require_once dirname( __FILE__ ) . '/class-wc-report-stock.php';
}
/**
* Low stock report class.
*/
class WC_Report_Low_In_Stock extends WC_Report_Stock {
/**
* No items found text.
*/
public function no_items() {
esc_html_e( 'No low in stock products found.', 'woocommerce' );
}
/**
* Get Products matching stock criteria.
*
* @param int $current_page Current page number.
* @param int $per_page How many results to show per page.
*/
public function get_items( $current_page, $per_page ) {
global $wpdb;
$this->max_items = 0;
$this->items = array();
$stock = absint( max( get_option( 'woocommerce_notify_low_stock_amount' ), 1 ) );
$nostock = absint( max( get_option( 'woocommerce_notify_no_stock_amount' ), 0 ) );
$query_from = apply_filters(
'woocommerce_report_low_in_stock_query_from',
$wpdb->prepare(
"
FROM {$wpdb->posts} as posts
INNER JOIN {$wpdb->wc_product_meta_lookup} AS lookup ON posts.ID = lookup.product_id
WHERE 1=1
AND posts.post_type IN ( 'product', 'product_variation' )
AND posts.post_status = 'publish'
AND lookup.stock_quantity <= %d
AND lookup.stock_quantity > %d
",
$stock,
$nostock
)
);
$this->items = $wpdb->get_results( $wpdb->prepare( "SELECT SQL_CALC_FOUND_ROWS posts.ID as id, posts.post_parent as parent {$query_from} ORDER BY posts.post_title DESC LIMIT %d, %d;", ( $current_page - 1 ) * $per_page, $per_page ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$this->max_items = $wpdb->get_var( 'SELECT FOUND_ROWS();' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
}
}
class-wc-report-most-stocked.php 0000644 00000002773 15155076532 0012736 0 ustar 00 <?php
/**
* WC_Report_Most_Stocked.
*
* @package WooCommerce\Admin\Reports
*/
defined( 'ABSPATH' ) || exit;
if ( ! class_exists( 'WC_Report_Stock' ) ) {
require_once dirname( __FILE__ ) . '/class-wc-report-stock.php';
}
/**
* WC_Report_Most_Stocked.
*/
class WC_Report_Most_Stocked extends WC_Report_Stock {
/**
* Get Products matching stock criteria.
*
* @param int $current_page Current page number.
* @param int $per_page How many results to show per page.
*/
public function get_items( $current_page, $per_page ) {
global $wpdb;
$this->max_items = 0;
$this->items = array();
$stock = absint( max( get_option( 'woocommerce_notify_low_stock_amount' ), 0 ) );
$query_from = apply_filters(
'woocommerce_report_most_stocked_query_from',
$wpdb->prepare(
"
FROM {$wpdb->posts} as posts
INNER JOIN {$wpdb->wc_product_meta_lookup} AS lookup ON posts.ID = lookup.product_id
WHERE 1=1
AND posts.post_type IN ( 'product', 'product_variation' )
AND posts.post_status = 'publish'
AND lookup.stock_quantity > %d
",
$stock
)
);
$this->items = $wpdb->get_results( $wpdb->prepare( "SELECT SQL_CALC_FOUND_ROWS posts.ID as id, posts.post_parent as parent {$query_from} ORDER BY lookup.stock_quantity DESC, id ASC LIMIT %d, %d;", ( $current_page - 1 ) * $per_page, $per_page ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$this->max_items = $wpdb->get_var( 'SELECT FOUND_ROWS();' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
}
}
class-wc-report-out-of-stock.php 0000644 00000003173 15155076532 0012647 0 ustar 00 <?php
/**
* WC_Report_Out_Of_Stock.
*
* @package WooCommerce\Admin\Reports
*/
defined( 'ABSPATH' ) || exit;
if ( ! class_exists( 'WC_Report_Stock' ) ) {
require_once dirname( __FILE__ ) . '/class-wc-report-stock.php';
}
/**
* WC_Report_Out_Of_Stock class.
*/
class WC_Report_Out_Of_Stock extends WC_Report_Stock {
/**
* No items found text.
*/
public function no_items() {
esc_html_e( 'No out of stock products found.', 'woocommerce' );
}
/**
* Get Products matching stock criteria.
*
* @param int $current_page Current page number.
* @param int $per_page How many results to show per page.
*/
public function get_items( $current_page, $per_page ) {
global $wpdb;
$this->max_items = 0;
$this->items = array();
$stock = absint( max( get_option( 'woocommerce_notify_no_stock_amount' ), 0 ) );
$query_from = apply_filters(
'woocommerce_report_out_of_stock_query_from',
$wpdb->prepare(
"
FROM {$wpdb->posts} as posts
INNER JOIN {$wpdb->wc_product_meta_lookup} AS lookup ON posts.ID = lookup.product_id
WHERE 1=1
AND posts.post_type IN ( 'product', 'product_variation' )
AND posts.post_status = 'publish'
AND lookup.stock_quantity <= %d
",
$stock
)
);
$this->items = $wpdb->get_results( $wpdb->prepare( "SELECT SQL_CALC_FOUND_ROWS posts.ID as id, posts.post_parent as parent {$query_from} ORDER BY posts.post_title DESC LIMIT %d, %d;", ( $current_page - 1 ) * $per_page, $per_page ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$this->max_items = $wpdb->get_var( 'SELECT FOUND_ROWS();' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
}
}
class-wc-report-sales-by-category.php 0000644 00000032403 15155076532 0013645 0 ustar 00 <?php
/**
* Sales by category report functionality
*
* @package WooCommerce\Admin\Reporting
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* WC_Report_Sales_By_Category
*
* @package WooCommerce\Admin\Reports
* @version 2.1.0
*/
class WC_Report_Sales_By_Category extends WC_Admin_Report {
/**
* Chart colors.
*
* @var array
*/
public $chart_colours = array();
/**
* Categories ids.
*
* @var array
*/
public $show_categories = array();
/**
* Item sales.
*
* @var array
*/
private $item_sales = array();
/**
* Item sales and times.
*
* @var array
*/
private $item_sales_and_times = array();
/**
* Constructor.
*/
public function __construct() {
if ( isset( $_GET['show_categories'] ) ) {
$this->show_categories = is_array( $_GET['show_categories'] ) ? array_map( 'absint', $_GET['show_categories'] ) : array( absint( $_GET['show_categories'] ) );
}
}
/**
* Get all product ids in a category (and its children).
*
* @param int $category_id Category ID.
* @return array
*/
public function get_products_in_category( $category_id ) {
$term_ids = get_term_children( $category_id, 'product_cat' );
$term_ids[] = $category_id;
$product_ids = get_objects_in_term( $term_ids, 'product_cat' );
return array_unique( apply_filters( 'woocommerce_report_sales_by_category_get_products_in_category', $product_ids, $category_id ) );
}
/**
* Get the legend for the main chart sidebar.
*
* @return array
*/
public function get_chart_legend() {
if ( empty( $this->show_categories ) ) {
return array();
}
$legend = array();
$index = 0;
foreach ( $this->show_categories as $category ) {
$category = get_term( $category, 'product_cat' );
$total = 0;
$product_ids = $this->get_products_in_category( $category->term_id );
foreach ( $product_ids as $id ) {
if ( isset( $this->item_sales[ $id ] ) ) {
$total += $this->item_sales[ $id ];
}
}
$legend[] = array(
/* translators: 1: total items sold 2: category name */
'title' => sprintf( __( '%1$s sales in %2$s', 'woocommerce' ), '<strong>' . wc_price( $total ) . '</strong>', $category->name ),
'color' => isset( $this->chart_colours[ $index ] ) ? $this->chart_colours[ $index ] : $this->chart_colours[0],
'highlight_series' => $index,
);
$index++;
}
return $legend;
}
/**
* Output the report.
*/
public function output_report() {
$ranges = array(
'year' => __( 'Year', 'woocommerce' ),
'last_month' => __( 'Last month', 'woocommerce' ),
'month' => __( 'This month', 'woocommerce' ),
'7day' => __( 'Last 7 days', 'woocommerce' ),
);
$this->chart_colours = array( '#3498db', '#34495e', '#1abc9c', '#2ecc71', '#f1c40f', '#e67e22', '#e74c3c', '#2980b9', '#8e44ad', '#2c3e50', '#16a085', '#27ae60', '#f39c12', '#d35400', '#c0392b' );
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day';
if ( ! in_array( $current_range, array( 'custom', 'year', 'last_month', 'month', '7day' ) ) ) {
$current_range = '7day';
}
$this->check_current_range_nonce( $current_range );
$this->calculate_current_range( $current_range );
// Get item sales data.
if ( ! empty( $this->show_categories ) ) {
$order_items = $this->get_order_report_data(
array(
'data' => array(
'_product_id' => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => '',
'name' => 'product_id',
),
'_line_total' => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => 'SUM',
'name' => 'order_item_amount',
),
'post_date' => array(
'type' => 'post_data',
'function' => '',
'name' => 'post_date',
),
),
'group_by' => 'ID, product_id, post_date',
'query_type' => 'get_results',
'filter_range' => true,
)
);
$this->item_sales = array();
$this->item_sales_and_times = array();
if ( is_array( $order_items ) ) {
foreach ( $order_items as $order_item ) {
switch ( $this->chart_groupby ) {
case 'day':
$time = strtotime( gmdate( 'Ymd', strtotime( $order_item->post_date ) ) ) * 1000;
break;
case 'month':
default:
$time = strtotime( gmdate( 'Ym', strtotime( $order_item->post_date ) ) . '01' ) * 1000;
break;
}
$this->item_sales_and_times[ $time ][ $order_item->product_id ] = isset( $this->item_sales_and_times[ $time ][ $order_item->product_id ] ) ? $this->item_sales_and_times[ $time ][ $order_item->product_id ] + $order_item->order_item_amount : $order_item->order_item_amount;
$this->item_sales[ $order_item->product_id ] = isset( $this->item_sales[ $order_item->product_id ] ) ? $this->item_sales[ $order_item->product_id ] + $order_item->order_item_amount : $order_item->order_item_amount;
}
}
}
include WC()->plugin_path() . '/includes/admin/views/html-report-by-date.php';
}
/**
* Get chart widgets.
*
* @return array
*/
public function get_chart_widgets() {
return array(
array(
'title' => __( 'Categories', 'woocommerce' ),
'callback' => array( $this, 'category_widget' ),
),
);
}
/**
* Output category widget.
*/
public function category_widget() {
$categories = get_terms( 'product_cat', array( 'orderby' => 'name' ) );
?>
<form method="GET">
<div>
<select multiple="multiple" data-placeholder="<?php esc_attr_e( 'Select categories…', 'woocommerce' ); ?>" class="wc-enhanced-select" id="show_categories" name="show_categories[]" style="width: 205px;">
<?php
$r = array();
$r['pad_counts'] = 1;
$r['hierarchical'] = 1;
$r['hide_empty'] = 1;
$r['value'] = 'id';
$r['selected'] = $this->show_categories;
include_once WC()->plugin_path() . '/includes/walkers/class-wc-product-cat-dropdown-walker.php';
echo wc_walk_category_dropdown_tree( $categories, 0, $r ); // @codingStandardsIgnoreLine
?>
</select>
<?php // @codingStandardsIgnoreStart ?>
<a href="#" class="select_none"><?php esc_html_e( 'None', 'woocommerce' ); ?></a>
<a href="#" class="select_all"><?php esc_html_e( 'All', 'woocommerce' ); ?></a>
<button type="submit" class="submit button" value="<?php esc_attr_e( 'Show', 'woocommerce' ); ?>"><?php esc_html_e( 'Show', 'woocommerce' ); ?></button>
<input type="hidden" name="range" value="<?php echo ( ! empty( $_GET['range'] ) ) ? esc_attr( wp_unslash( $_GET['range'] ) ) : ''; ?>" />
<input type="hidden" name="start_date" value="<?php echo ( ! empty( $_GET['start_date'] ) ) ? esc_attr( wp_unslash( $_GET['start_date'] ) ) : ''; ?>" />
<input type="hidden" name="end_date" value="<?php echo ( ! empty( $_GET['end_date'] ) ) ? esc_attr( wp_unslash( $_GET['end_date'] ) ) : ''; ?>" />
<input type="hidden" name="page" value="<?php echo ( ! empty( $_GET['page'] ) ) ? esc_attr( wp_unslash( $_GET['page'] ) ) : ''; ?>" />
<input type="hidden" name="tab" value="<?php echo ( ! empty( $_GET['tab'] ) ) ? esc_attr( wp_unslash( $_GET['tab'] ) ) : ''; ?>" />
<input type="hidden" name="report" value="<?php echo ( ! empty( $_GET['report'] ) ) ? esc_attr( wp_unslash( $_GET['report'] ) ) : ''; ?>" />
<?php // @codingStandardsIgnoreEnd ?>
</div>
<script type="text/javascript">
jQuery(function(){
// Select all/None
jQuery( '.chart-widget' ).on( 'click', '.select_all', function() {
jQuery(this).closest( 'div' ).find( 'select option' ).attr( 'selected', 'selected' );
jQuery(this).closest( 'div' ).find('select').trigger( 'change' );
return false;
});
jQuery( '.chart-widget').on( 'click', '.select_none', function() {
jQuery(this).closest( 'div' ).find( 'select option' ).prop( 'selected', false );
jQuery(this).closest( 'div' ).find('select').trigger( 'change' );
return false;
});
});
</script>
</form>
<?php
}
/**
* Output an export link.
*/
public function get_export_button() {
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day';
?>
<a
href="#"
download="report-<?php echo esc_attr( $current_range ); ?>-<?php echo esc_attr( date_i18n( 'Y-m-d', current_time( 'timestamp' ) ) ); ?>.csv"
class="export_csv"
data-export="chart"
data-xaxes="<?php esc_attr_e( 'Date', 'woocommerce' ); ?>"
data-groupby="<?php echo esc_attr( $this->chart_groupby ); ?>"
>
<?php esc_html_e( 'Export CSV', 'woocommerce' ); ?>
</a>
<?php
}
/**
* Get the main chart.
*/
public function get_main_chart() {
global $wp_locale;
if ( empty( $this->show_categories ) ) {
?>
<div class="chart-container">
<p class="chart-prompt"><?php esc_html_e( 'Choose a category to view stats', 'woocommerce' ); ?></p>
</div>
<?php
} else {
$chart_data = array();
$index = 0;
foreach ( $this->show_categories as $category ) {
$category = get_term( $category, 'product_cat' );
$product_ids = $this->get_products_in_category( $category->term_id );
$category_chart_data = array();
for ( $i = 0; $i <= $this->chart_interval; $i ++ ) {
$interval_total = 0;
switch ( $this->chart_groupby ) {
case 'day':
$time = strtotime( gmdate( 'Ymd', strtotime( "+{$i} DAY", $this->start_date ) ) ) * 1000;
break;
case 'month':
default:
$time = strtotime( gmdate( 'Ym', strtotime( "+{$i} MONTH", $this->start_date ) ) . '01' ) * 1000;
break;
}
foreach ( $product_ids as $id ) {
if ( isset( $this->item_sales_and_times[ $time ][ $id ] ) ) {
$interval_total += $this->item_sales_and_times[ $time ][ $id ];
}
}
$category_chart_data[] = array( $time, (float) wc_format_decimal( $interval_total, wc_get_price_decimals() ) );
}
$chart_data[ $category->term_id ]['category'] = $category->name;
$chart_data[ $category->term_id ]['data'] = $category_chart_data;
$index++;
}
?>
<div class="chart-container">
<div class="chart-placeholder main"></div>
</div>
<?php // @codingStandardsIgnoreStart ?>
<script type="text/javascript">
var main_chart;
jQuery(function(){
var drawGraph = function( highlight ) {
var series = [
<?php
$index = 0;
foreach ( $chart_data as $data ) {
$color = isset( $this->chart_colours[ $index ] ) ? $this->chart_colours[ $index ] : $this->chart_colours[0];
$width = $this->barwidth / sizeof( $chart_data );
$offset = ( $width * $index );
$series = $data['data'];
foreach ( $series as $key => $series_data ) {
$series[ $key ][0] = $series_data[0] + $offset;
}
$series = wp_json_encode( $series );
echo '{
label: "' . esc_js( $data['category'] ) . '",
data: JSON.parse( decodeURIComponent( "' . rawurlencode( $series ) . '" ) ),
color: "' . $color . '",
bars: {
fillColor: "' . $color . '",
fill: true,
show: true,
lineWidth: 1,
align: "center",
barWidth: ' . $width * 0.75 . ',
stack: false
},
' . $this->get_currency_tooltip() . ',
enable_tooltip: true,
prepend_label: true
},';
$index++;
}
?>
];
if ( highlight !== 'undefined' && series[ highlight ] ) {
highlight_series = series[ highlight ];
highlight_series.color = '#9c5d90';
if ( highlight_series.bars ) {
highlight_series.bars.fillColor = '#9c5d90';
}
if ( highlight_series.lines ) {
highlight_series.lines.lineWidth = 5;
}
}
main_chart = jQuery.plot(
jQuery('.chart-placeholder.main'),
series,
{
legend: {
show: false
},
grid: {
color: '#aaa',
borderColor: 'transparent',
borderWidth: 0,
hoverable: true
},
xaxes: [ {
color: '#aaa',
reserveSpace: true,
position: "bottom",
tickColor: 'transparent',
mode: "time",
timeformat: "<?php echo ( 'day' === $this->chart_groupby ) ? '%d %b' : '%b'; ?>",
monthNames: JSON.parse( decodeURIComponent( '<?php echo rawurlencode( wp_json_encode( array_values( $wp_locale->month_abbrev ) ) ); ?>' ) ),
tickLength: 1,
minTickSize: [1, "<?php echo $this->chart_groupby; ?>"],
tickSize: [1, "<?php echo $this->chart_groupby; ?>"],
font: {
color: "#aaa"
}
} ],
yaxes: [
{
min: 0,
tickDecimals: 2,
color: 'transparent',
font: { color: "#aaa" }
}
],
}
);
jQuery('.chart-placeholder').trigger( 'resize' );
}
drawGraph();
jQuery('.highlight_series').on( 'mouseenter',
function() {
drawGraph( jQuery(this).data('series') );
} ).on( 'mouseleave',
function() {
drawGraph();
}
);
});
</script>
<?php // @codingStandardsIgnoreEnd ?>
<?php
}
}
}
class-wc-report-sales-by-date.php 0000644 00000076554 15155076532 0012764 0 ustar 00 <?php
/**
* WC_Report_Sales_By_Date
*
* @package WooCommerce\Admin\Reports
* @version 2.1.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* WC_Report_Sales_By_Date
*/
class WC_Report_Sales_By_Date extends WC_Admin_Report {
/**
* Chart colors.
*
* @var array
*/
public $chart_colours = array();
/**
* The report data.
*
* @var stdClass
*/
private $report_data;
/**
* Get report data.
*
* @return stdClass
*/
public function get_report_data() {
if ( empty( $this->report_data ) ) {
$this->query_report_data();
}
return $this->report_data;
}
/**
* Get all data needed for this report and store in the class.
*/
private function query_report_data() {
$this->report_data = new stdClass();
$this->report_data->order_counts = (array) $this->get_order_report_data(
array(
'data' => array(
'ID' => array(
'type' => 'post_data',
'function' => 'COUNT',
'name' => 'count',
'distinct' => true,
),
'post_date' => array(
'type' => 'post_data',
'function' => '',
'name' => 'post_date',
),
),
'group_by' => $this->group_by_query,
'order_by' => 'post_date ASC',
'query_type' => 'get_results',
'filter_range' => true,
'order_types' => wc_get_order_types( 'order-count' ),
'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
)
);
$this->report_data->coupons = (array) $this->get_order_report_data(
array(
'data' => array(
'order_item_name' => array(
'type' => 'order_item',
'function' => '',
'name' => 'order_item_name',
),
'discount_amount' => array(
'type' => 'order_item_meta',
'order_item_type' => 'coupon',
'function' => 'SUM',
'name' => 'discount_amount',
),
'post_date' => array(
'type' => 'post_data',
'function' => '',
'name' => 'post_date',
),
),
'where' => array(
array(
'key' => 'order_items.order_item_type',
'value' => 'coupon',
'operator' => '=',
),
),
'group_by' => $this->group_by_query . ', order_item_name',
'order_by' => 'post_date ASC',
'query_type' => 'get_results',
'filter_range' => true,
'order_types' => wc_get_order_types( 'order-count' ),
'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
)
);
// All items from orders - even those refunded.
$this->report_data->order_items = (array) $this->get_order_report_data(
array(
'data' => array(
'_qty' => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => 'SUM',
'name' => 'order_item_count',
),
'post_date' => array(
'type' => 'post_data',
'function' => '',
'name' => 'post_date',
),
),
'where' => array(
array(
'key' => 'order_items.order_item_type',
'value' => 'line_item',
'operator' => '=',
),
),
'group_by' => $this->group_by_query,
'order_by' => 'post_date ASC',
'query_type' => 'get_results',
'filter_range' => true,
'order_types' => wc_get_order_types( 'order-count' ),
'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
)
);
/**
* Get total of fully refunded items.
*/
$this->report_data->refunded_order_items = absint(
$this->get_order_report_data(
array(
'data' => array(
'_qty' => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => 'SUM',
'name' => 'order_item_count',
),
),
'where' => array(
array(
'key' => 'order_items.order_item_type',
'value' => 'line_item',
'operator' => '=',
),
),
'query_type' => 'get_var',
'filter_range' => true,
'order_types' => wc_get_order_types( 'order-count' ),
'order_status' => array( 'refunded' ),
)
)
);
/**
* Order totals by date. Charts should show GROSS amounts to avoid going -ve.
*/
$this->report_data->orders = (array) $this->get_order_report_data(
array(
'data' => array(
'_order_total' => array(
'type' => 'meta',
'function' => 'SUM',
'name' => 'total_sales',
),
'_order_shipping' => array(
'type' => 'meta',
'function' => 'SUM',
'name' => 'total_shipping',
),
'_order_tax' => array(
'type' => 'meta',
'function' => 'SUM',
'name' => 'total_tax',
),
'_order_shipping_tax' => array(
'type' => 'meta',
'function' => 'SUM',
'name' => 'total_shipping_tax',
),
'post_date' => array(
'type' => 'post_data',
'function' => '',
'name' => 'post_date',
),
),
'group_by' => $this->group_by_query,
'order_by' => 'post_date ASC',
'query_type' => 'get_results',
'filter_range' => true,
'order_types' => wc_get_order_types( 'sales-reports' ),
'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
)
);
/**
* If an order is 100% refunded we should look at the parent's totals, but the refunds dates.
* We also need to ensure each parent order's values are only counted/summed once.
*/
$this->report_data->full_refunds = (array) $this->get_order_report_data(
array(
'data' => array(
'_order_total' => array(
'type' => 'parent_meta',
'function' => '',
'name' => 'total_refund',
),
'_order_shipping' => array(
'type' => 'parent_meta',
'function' => '',
'name' => 'total_shipping',
),
'_order_tax' => array(
'type' => 'parent_meta',
'function' => '',
'name' => 'total_tax',
),
'_order_shipping_tax' => array(
'type' => 'parent_meta',
'function' => '',
'name' => 'total_shipping_tax',
),
'post_date' => array(
'type' => 'post_data',
'function' => '',
'name' => 'post_date',
),
),
'group_by' => 'posts.post_parent',
'query_type' => 'get_results',
'filter_range' => true,
'order_status' => false,
'parent_order_status' => array( 'refunded' ),
)
);
foreach ( $this->report_data->full_refunds as $key => $order ) {
$total_refund = is_numeric( $order->total_refund ) ? $order->total_refund : 0;
$total_shipping = is_numeric( $order->total_shipping ) ? $order->total_shipping : 0;
$total_tax = is_numeric( $order->total_tax ) ? $order->total_tax : 0;
$total_shipping_tax = is_numeric( $order->total_shipping_tax ) ? $order->total_shipping_tax : 0;
$this->report_data->full_refunds[ $key ]->net_refund = $total_refund - ( $total_shipping + $total_tax + $total_shipping_tax );
}
/**
* Partial refunds. This includes line items, shipping and taxes. Not grouped by date.
*/
$this->report_data->partial_refunds = (array) $this->get_order_report_data(
array(
'data' => array(
'ID' => array(
'type' => 'post_data',
'function' => '',
'name' => 'refund_id',
),
'_refund_amount' => array(
'type' => 'meta',
'function' => '',
'name' => 'total_refund',
),
'post_date' => array(
'type' => 'post_data',
'function' => '',
'name' => 'post_date',
),
'order_item_type' => array(
'type' => 'order_item',
'function' => '',
'name' => 'item_type',
'join_type' => 'LEFT',
),
'_order_total' => array(
'type' => 'meta',
'function' => '',
'name' => 'total_sales',
),
'_order_shipping' => array(
'type' => 'meta',
'function' => '',
'name' => 'total_shipping',
'join_type' => 'LEFT',
),
'_order_tax' => array(
'type' => 'meta',
'function' => '',
'name' => 'total_tax',
'join_type' => 'LEFT',
),
'_order_shipping_tax' => array(
'type' => 'meta',
'function' => '',
'name' => 'total_shipping_tax',
'join_type' => 'LEFT',
),
'_qty' => array(
'type' => 'order_item_meta',
'function' => 'SUM',
'name' => 'order_item_count',
'join_type' => 'LEFT',
),
),
'group_by' => 'refund_id',
'order_by' => 'post_date ASC',
'query_type' => 'get_results',
'filter_range' => true,
'order_status' => false,
'parent_order_status' => array( 'completed', 'processing', 'on-hold' ),
)
);
foreach ( $this->report_data->partial_refunds as $key => $order ) {
$this->report_data->partial_refunds[ $key ]->net_refund = (float) $order->total_refund - ( (float) $order->total_shipping + (float) $order->total_tax + (float) $order->total_shipping_tax );
}
/**
* Refund lines - all partial refunds on all order types so we can plot full AND partial refunds on the chart.
*/
$this->report_data->refund_lines = (array) $this->get_order_report_data(
array(
'data' => array(
'ID' => array(
'type' => 'post_data',
'function' => '',
'name' => 'refund_id',
),
'_refund_amount' => array(
'type' => 'meta',
'function' => '',
'name' => 'total_refund',
),
'post_date' => array(
'type' => 'post_data',
'function' => '',
'name' => 'post_date',
),
'order_item_type' => array(
'type' => 'order_item',
'function' => '',
'name' => 'item_type',
'join_type' => 'LEFT',
),
'_order_total' => array(
'type' => 'meta',
'function' => '',
'name' => 'total_sales',
),
'_order_shipping' => array(
'type' => 'meta',
'function' => '',
'name' => 'total_shipping',
'join_type' => 'LEFT',
),
'_order_tax' => array(
'type' => 'meta',
'function' => '',
'name' => 'total_tax',
'join_type' => 'LEFT',
),
'_order_shipping_tax' => array(
'type' => 'meta',
'function' => '',
'name' => 'total_shipping_tax',
'join_type' => 'LEFT',
),
'_qty' => array(
'type' => 'order_item_meta',
'function' => 'SUM',
'name' => 'order_item_count',
'join_type' => 'LEFT',
),
),
'group_by' => 'refund_id',
'order_by' => 'post_date ASC',
'query_type' => 'get_results',
'filter_range' => true,
'order_status' => false,
'parent_order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
)
);
/**
* Total up refunds. Note: when an order is fully refunded, a refund line will be added.
*/
$this->report_data->total_tax_refunded = 0;
$this->report_data->total_shipping_refunded = 0;
$this->report_data->total_shipping_tax_refunded = 0;
$this->report_data->total_refunds = 0;
$this->report_data->refunded_orders = array_merge( $this->report_data->partial_refunds, $this->report_data->full_refunds );
foreach ( $this->report_data->refunded_orders as $key => $value ) {
$this->report_data->total_tax_refunded += floatval( $value->total_tax < 0 ? $value->total_tax * -1 : $value->total_tax );
$this->report_data->total_refunds += floatval( $value->total_refund );
$this->report_data->total_shipping_tax_refunded += floatval( $value->total_shipping_tax < 0 ? $value->total_shipping_tax * -1 : $value->total_shipping_tax );
$this->report_data->total_shipping_refunded += floatval( $value->total_shipping < 0 ? $value->total_shipping * -1 : $value->total_shipping );
// Only applies to partial.
if ( isset( $value->order_item_count ) ) {
$this->report_data->refunded_order_items += floatval( $value->order_item_count < 0 ? $value->order_item_count * -1 : $value->order_item_count );
}
}
// Totals from all orders - including those refunded. Subtract refunded amounts.
$this->report_data->total_tax = wc_format_decimal( array_sum( wp_list_pluck( $this->report_data->orders, 'total_tax' ) ) - $this->report_data->total_tax_refunded, 2 );
$this->report_data->total_shipping = wc_format_decimal( array_sum( wp_list_pluck( $this->report_data->orders, 'total_shipping' ) ) - $this->report_data->total_shipping_refunded, 2 );
$this->report_data->total_shipping_tax = wc_format_decimal( array_sum( wp_list_pluck( $this->report_data->orders, 'total_shipping_tax' ) ) - $this->report_data->total_shipping_tax_refunded, 2 );
// Total the refunds and sales amounts. Sales subtract refunds. Note - total_sales also includes shipping costs.
$this->report_data->total_sales = wc_format_decimal( array_sum( wp_list_pluck( $this->report_data->orders, 'total_sales' ) ) - $this->report_data->total_refunds, 2 );
$this->report_data->net_sales = wc_format_decimal( $this->report_data->total_sales - $this->report_data->total_shipping - max( 0, $this->report_data->total_tax ) - max( 0, $this->report_data->total_shipping_tax ), 2 );
// Calculate average based on net.
$this->report_data->average_sales = wc_format_decimal( $this->report_data->net_sales / ( $this->chart_interval + 1 ), 2 );
$this->report_data->average_total_sales = wc_format_decimal( $this->report_data->total_sales / ( $this->chart_interval + 1 ), 2 );
// Total orders and discounts also includes those which have been refunded at some point.
$this->report_data->total_coupons = number_format( array_sum( wp_list_pluck( $this->report_data->coupons, 'discount_amount' ) ), 2, '.', '' );
$this->report_data->total_refunded_orders = absint( count( $this->report_data->full_refunds ) );
// Total orders in this period, even if refunded.
$this->report_data->total_orders = absint( array_sum( wp_list_pluck( $this->report_data->order_counts, 'count' ) ) );
// Item items ordered in this period, even if refunded.
$this->report_data->total_items = absint( array_sum( wp_list_pluck( $this->report_data->order_items, 'order_item_count' ) ) );
// 3rd party filtering of report data
$this->report_data = apply_filters( 'woocommerce_admin_report_data', $this->report_data );
}
/**
* Get the legend for the main chart sidebar.
*
* @return array
*/
public function get_chart_legend() {
$legend = array();
$data = $this->get_report_data();
switch ( $this->chart_groupby ) {
case 'day':
$average_total_sales_title = sprintf(
/* translators: %s: average total sales */
__( '%s average gross daily sales', 'woocommerce' ),
'<strong>' . wc_price( $data->average_total_sales ) . '</strong>'
);
$average_sales_title = sprintf(
/* translators: %s: average sales */
__( '%s average net daily sales', 'woocommerce' ),
'<strong>' . wc_price( $data->average_sales ) . '</strong>'
);
break;
case 'month':
default:
$average_total_sales_title = sprintf(
/* translators: %s: average total sales */
__( '%s average gross monthly sales', 'woocommerce' ),
'<strong>' . wc_price( $data->average_total_sales ) . '</strong>'
);
$average_sales_title = sprintf(
/* translators: %s: average sales */
__( '%s average net monthly sales', 'woocommerce' ),
'<strong>' . wc_price( $data->average_sales ) . '</strong>'
);
break;
}
$legend[] = array(
'title' => sprintf(
/* translators: %s: total sales */
__( '%s gross sales in this period', 'woocommerce' ),
'<strong>' . wc_price( $data->total_sales ) . '</strong>'
),
'placeholder' => __( 'This is the sum of the order totals after any refunds and including shipping and taxes.', 'woocommerce' ),
'color' => $this->chart_colours['sales_amount'],
'highlight_series' => 6,
);
if ( $data->average_total_sales > 0 ) {
$legend[] = array(
'title' => $average_total_sales_title,
'color' => $this->chart_colours['average'],
'highlight_series' => 2,
);
}
$legend[] = array(
'title' => sprintf(
/* translators: %s: net sales */
__( '%s net sales in this period', 'woocommerce' ),
'<strong>' . wc_price( $data->net_sales ) . '</strong>'
),
'placeholder' => __( 'This is the sum of the order totals after any refunds and excluding shipping and taxes.', 'woocommerce' ),
'color' => $this->chart_colours['net_sales_amount'],
'highlight_series' => 7,
);
if ( $data->average_sales > 0 ) {
$legend[] = array(
'title' => $average_sales_title,
'color' => $this->chart_colours['net_average'],
'highlight_series' => 3,
);
}
$legend[] = array(
'title' => sprintf(
/* translators: %s: total orders */
__( '%s orders placed', 'woocommerce' ),
'<strong>' . $data->total_orders . '</strong>'
),
'color' => $this->chart_colours['order_count'],
'highlight_series' => 1,
);
$legend[] = array(
'title' => sprintf(
/* translators: %s: total items */
__( '%s items purchased', 'woocommerce' ),
'<strong>' . $data->total_items . '</strong>'
),
'color' => $this->chart_colours['item_count'],
'highlight_series' => 0,
);
$legend[] = array(
'title' => sprintf(
/* translators: 1: total refunds 2: total refunded orders 3: refunded items */
_n( '%1$s refunded %2$d order (%3$d item)', '%1$s refunded %2$d orders (%3$d items)', $this->report_data->total_refunded_orders, 'woocommerce' ),
'<strong>' . wc_price( $data->total_refunds ) . '</strong>',
$this->report_data->total_refunded_orders,
$this->report_data->refunded_order_items
),
'color' => $this->chart_colours['refund_amount'],
'highlight_series' => 8,
);
$legend[] = array(
'title' => sprintf(
/* translators: %s: total shipping */
__( '%s charged for shipping', 'woocommerce' ),
'<strong>' . wc_price( $data->total_shipping ) . '</strong>'
),
'color' => $this->chart_colours['shipping_amount'],
'highlight_series' => 5,
);
$legend[] = array(
'title' => sprintf(
/* translators: %s: total coupons */
__( '%s worth of coupons used', 'woocommerce' ),
'<strong>' . wc_price( $data->total_coupons ) . '</strong>'
),
'color' => $this->chart_colours['coupon_amount'],
'highlight_series' => 4,
);
return $legend;
}
/**
* Output the report.
*/
public function output_report() {
$ranges = array(
'year' => __( 'Year', 'woocommerce' ),
'last_month' => __( 'Last month', 'woocommerce' ),
'month' => __( 'This month', 'woocommerce' ),
'7day' => __( 'Last 7 days', 'woocommerce' ),
);
$this->chart_colours = array(
'sales_amount' => '#b1d4ea',
'net_sales_amount' => '#3498db',
'average' => '#b1d4ea',
'net_average' => '#3498db',
'order_count' => '#dbe1e3',
'item_count' => '#ecf0f1',
'shipping_amount' => '#5cc488',
'coupon_amount' => '#f1c40f',
'refund_amount' => '#e74c3c',
);
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( ! in_array( $current_range, array( 'custom', 'year', 'last_month', 'month', '7day' ), true ) ) {
$current_range = '7day';
}
$this->check_current_range_nonce( $current_range );
$this->calculate_current_range( $current_range );
include WC()->plugin_path() . '/includes/admin/views/html-report-by-date.php';
}
/**
* Output an export link.
*/
public function get_export_button() {
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
?>
<a
href="#"
download="report-<?php echo esc_attr( $current_range ); ?>-<?php echo esc_attr( date_i18n( 'Y-m-d', current_time( 'timestamp' ) ) ); ?>.csv"
class="export_csv"
data-export="chart"
data-xaxes="<?php esc_attr_e( 'Date', 'woocommerce' ); ?>"
data-exclude_series="2"
data-groupby="<?php echo esc_attr( $this->chart_groupby ); ?>"
>
<?php esc_html_e( 'Export CSV', 'woocommerce' ); ?>
</a>
<?php
}
/**
* Round our totals correctly.
*
* @param array|string $amount Chart total.
*
* @return array|string
*/
private function round_chart_totals( $amount ) {
if ( is_array( $amount ) ) {
return array( $amount[0], wc_format_decimal( $amount[1], wc_get_price_decimals() ) );
} else {
return wc_format_decimal( $amount, wc_get_price_decimals() );
}
}
/**
* Get the main chart.
*/
public function get_main_chart() {
global $wp_locale;
// Prepare data for report.
$data = array(
'order_counts' => $this->prepare_chart_data( $this->report_data->order_counts, 'post_date', 'count', $this->chart_interval, $this->start_date, $this->chart_groupby ),
'order_item_counts' => $this->prepare_chart_data( $this->report_data->order_items, 'post_date', 'order_item_count', $this->chart_interval, $this->start_date, $this->chart_groupby ),
'order_amounts' => $this->prepare_chart_data( $this->report_data->orders, 'post_date', 'total_sales', $this->chart_interval, $this->start_date, $this->chart_groupby ),
'coupon_amounts' => $this->prepare_chart_data( $this->report_data->coupons, 'post_date', 'discount_amount', $this->chart_interval, $this->start_date, $this->chart_groupby ),
'shipping_amounts' => $this->prepare_chart_data( $this->report_data->orders, 'post_date', 'total_shipping', $this->chart_interval, $this->start_date, $this->chart_groupby ),
'refund_amounts' => $this->prepare_chart_data( $this->report_data->refund_lines, 'post_date', 'total_refund', $this->chart_interval, $this->start_date, $this->chart_groupby ),
'net_refund_amounts' => $this->prepare_chart_data( $this->report_data->refunded_orders, 'post_date', 'net_refund', $this->chart_interval, $this->start_date, $this->chart_groupby ),
'shipping_tax_amounts' => $this->prepare_chart_data( $this->report_data->orders, 'post_date', 'total_shipping_tax', $this->chart_interval, $this->start_date, $this->chart_groupby ),
'tax_amounts' => $this->prepare_chart_data( $this->report_data->orders, 'post_date', 'total_tax', $this->chart_interval, $this->start_date, $this->chart_groupby ),
'net_order_amounts' => array(),
'gross_order_amounts' => array(),
);
foreach ( $data['order_amounts'] as $order_amount_key => $order_amount_value ) {
$data['gross_order_amounts'][ $order_amount_key ] = $order_amount_value;
$data['gross_order_amounts'][ $order_amount_key ][1] -= $data['refund_amounts'][ $order_amount_key ][1];
$data['net_order_amounts'][ $order_amount_key ] = $order_amount_value;
// Subtract the sum of the values from net order amounts.
$data['net_order_amounts'][ $order_amount_key ][1] -=
$data['net_refund_amounts'][ $order_amount_key ][1] +
$data['shipping_amounts'][ $order_amount_key ][1] +
$data['shipping_tax_amounts'][ $order_amount_key ][1] +
$data['tax_amounts'][ $order_amount_key ][1];
}
// 3rd party filtering of report data.
$data = apply_filters( 'woocommerce_admin_report_chart_data', $data );
// Encode in json format.
$chart_data = wp_json_encode(
array(
'order_counts' => array_values( $data['order_counts'] ),
'order_item_counts' => array_values( $data['order_item_counts'] ),
'order_amounts' => array_map( array( $this, 'round_chart_totals' ), array_values( $data['order_amounts'] ) ),
'gross_order_amounts' => array_map( array( $this, 'round_chart_totals' ), array_values( $data['gross_order_amounts'] ) ),
'net_order_amounts' => array_map( array( $this, 'round_chart_totals' ), array_values( $data['net_order_amounts'] ) ),
'shipping_amounts' => array_map( array( $this, 'round_chart_totals' ), array_values( $data['shipping_amounts'] ) ),
'coupon_amounts' => array_map( array( $this, 'round_chart_totals' ), array_values( $data['coupon_amounts'] ) ),
'refund_amounts' => array_map( array( $this, 'round_chart_totals' ), array_values( $data['refund_amounts'] ) ),
)
);
?>
<div class="chart-container">
<div class="chart-placeholder main"></div>
</div>
<script type="text/javascript">
var main_chart;
jQuery(function(){
var order_data = JSON.parse( decodeURIComponent( '<?php echo rawurlencode( $chart_data ); ?>' ) );
var drawGraph = function( highlight ) {
var series = [
{
label: "<?php echo esc_js( __( 'Number of items sold', 'woocommerce' ) ); ?>",
data: order_data.order_item_counts,
color: '<?php echo esc_js( $this->chart_colours['item_count'] ); ?>',
bars: { fillColor: '<?php echo esc_js( $this->chart_colours['item_count'] ); ?>', fill: true, show: true, lineWidth: 0, barWidth: <?php echo esc_js( $this->barwidth ); ?> * 0.5, align: 'center' },
shadowSize: 0,
hoverable: false
},
{
label: "<?php echo esc_js( __( 'Number of orders', 'woocommerce' ) ); ?>",
data: order_data.order_counts,
color: '<?php echo esc_js( $this->chart_colours['order_count'] ); ?>',
bars: { fillColor: '<?php echo esc_js( $this->chart_colours['order_count'] ); ?>', fill: true, show: true, lineWidth: 0, barWidth: <?php echo esc_js( $this->barwidth ); ?> * 0.5, align: 'center' },
shadowSize: 0,
hoverable: false
},
{
label: "<?php echo esc_js( __( 'Average gross sales amount', 'woocommerce' ) ); ?>",
data: [ [ <?php echo esc_js( min( array_keys( $data['order_amounts'] ) ) ); ?>, <?php echo esc_js( $this->report_data->average_total_sales ); ?> ], [ <?php echo esc_js( max( array_keys( $data['order_amounts'] ) ) ); ?>, <?php echo esc_js( $this->report_data->average_total_sales ); ?> ] ],
yaxis: 2,
color: '<?php echo esc_js( $this->chart_colours['average'] ); ?>',
points: { show: false },
lines: { show: true, lineWidth: 2, fill: false },
shadowSize: 0,
hoverable: false
},
{
label: "<?php echo esc_js( __( 'Average net sales amount', 'woocommerce' ) ); ?>",
data: [ [ <?php echo esc_js( min( array_keys( $data['order_amounts'] ) ) ); ?>, <?php echo esc_js( $this->report_data->average_sales ); ?> ], [ <?php echo esc_js( max( array_keys( $data['order_amounts'] ) ) ); ?>, <?php echo esc_js( $this->report_data->average_sales ); ?> ] ],
yaxis: 2,
color: '<?php echo esc_js( $this->chart_colours['net_average'] ); ?>',
points: { show: false },
lines: { show: true, lineWidth: 2, fill: false },
shadowSize: 0,
hoverable: false
},
{
label: "<?php echo esc_js( __( 'Coupon amount', 'woocommerce' ) ); ?>",
data: order_data.coupon_amounts,
yaxis: 2,
color: '<?php echo esc_js( $this->chart_colours['coupon_amount'] ); ?>',
points: { show: true, radius: 5, lineWidth: 2, fillColor: '#fff', fill: true },
lines: { show: true, lineWidth: 2, fill: false },
shadowSize: 0,
<?php echo $this->get_currency_tooltip(); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?>
},
{
label: "<?php echo esc_js( __( 'Shipping amount', 'woocommerce' ) ); ?>",
data: order_data.shipping_amounts,
yaxis: 2,
color: '<?php echo esc_js( $this->chart_colours['shipping_amount'] ); ?>',
points: { show: true, radius: 5, lineWidth: 2, fillColor: '#fff', fill: true },
lines: { show: true, lineWidth: 2, fill: false },
shadowSize: 0,
prepend_tooltip: "<?php echo get_woocommerce_currency_symbol(); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?>"
},
{
label: "<?php echo esc_js( __( 'Gross sales amount', 'woocommerce' ) ); ?>",
data: order_data.gross_order_amounts,
yaxis: 2,
color: '<?php echo esc_js( $this->chart_colours['sales_amount'] ); ?>',
points: { show: true, radius: 5, lineWidth: 2, fillColor: '#fff', fill: true },
lines: { show: true, lineWidth: 2, fill: false },
shadowSize: 0,
<?php echo $this->get_currency_tooltip(); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?>
},
{
label: "<?php echo esc_js( __( 'Net sales amount', 'woocommerce' ) ); ?>",
data: order_data.net_order_amounts,
yaxis: 2,
color: '<?php echo esc_js( $this->chart_colours['net_sales_amount'] ); ?>',
points: { show: true, radius: 6, lineWidth: 4, fillColor: '#fff', fill: true },
lines: { show: true, lineWidth: 5, fill: false },
shadowSize: 0,
<?php echo $this->get_currency_tooltip(); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?>
},
{
label: "<?php echo esc_js( __( 'Refund amount', 'woocommerce' ) ); ?>",
data: order_data.refund_amounts,
yaxis: 2,
color: '<?php echo esc_js( $this->chart_colours['refund_amount'] ); ?>',
points: { show: true, radius: 5, lineWidth: 2, fillColor: '#fff', fill: true },
lines: { show: true, lineWidth: 2, fill: false },
shadowSize: 0,
prepend_tooltip: "<?php echo get_woocommerce_currency_symbol(); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?>"
},
];
if ( highlight !== 'undefined' && series[ highlight ] ) {
highlight_series = series[ highlight ];
highlight_series.color = '#9c5d90';
if ( highlight_series.bars ) {
highlight_series.bars.fillColor = '#9c5d90';
}
if ( highlight_series.lines ) {
highlight_series.lines.lineWidth = 5;
}
}
main_chart = jQuery.plot(
jQuery('.chart-placeholder.main'),
series,
{
legend: {
show: false
},
grid: {
color: '#aaa',
borderColor: 'transparent',
borderWidth: 0,
hoverable: true
},
xaxes: [ {
color: '#aaa',
position: "bottom",
tickColor: 'transparent',
mode: "time",
timeformat: "<?php echo ( 'day' === $this->chart_groupby ) ? '%d %b' : '%b'; ?>",
monthNames: JSON.parse( decodeURIComponent( '<?php echo rawurlencode( wp_json_encode( array_values( $wp_locale->month_abbrev ) ) ); ?>' ) ),
tickLength: 1,
minTickSize: [1, "<?php echo esc_js( $this->chart_groupby ); ?>"],
font: {
color: "#aaa"
}
} ],
yaxes: [
{
min: 0,
minTickSize: 1,
tickDecimals: 0,
color: '#d4d9dc',
font: { color: "#aaa" }
},
{
position: "right",
min: 0,
tickDecimals: 2,
alignTicksWithAxis: 1,
color: 'transparent',
font: { color: "#aaa" }
}
],
}
);
jQuery('.chart-placeholder').trigger( 'resize' );
}
drawGraph();
jQuery('.highlight_series').on( 'mouseenter',
function() {
drawGraph( jQuery(this).data('series') );
} ).on( 'mouseleave',
function() {
drawGraph();
}
);
});
</script>
<?php
}
}
class-wc-report-sales-by-product.php 0000644 00000050515 15155076532 0013514 0 ustar 00 <?php
/**
* Sales By Product Reporting
*
* @package WooCommerce\Admin\Reporting
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* WC_Report_Sales_By_Product
*
* @package WooCommerce\Admin\Reports
* @version 2.1.0
*/
class WC_Report_Sales_By_Product extends WC_Admin_Report {
/**
* Chart colors.
*
* @var array
*/
public $chart_colours = array();
/**
* Product ids.
*
* @var array
*/
public $product_ids = array();
/**
* Product ids with titles.
*
* @var array
*/
public $product_ids_titles = array();
/**
* Constructor.
*/
public function __construct() {
// @codingStandardsIgnoreStart
if ( isset( $_GET['product_ids'] ) && is_array( $_GET['product_ids'] ) ) {
$this->product_ids = array_filter( array_map( 'absint', $_GET['product_ids'] ) );
} elseif ( isset( $_GET['product_ids'] ) ) {
$this->product_ids = array_filter( array( absint( $_GET['product_ids'] ) ) );
}
// @codingStandardsIgnoreEnd
}
/**
* Get the legend for the main chart sidebar.
*
* @return array
*/
public function get_chart_legend() {
if ( empty( $this->product_ids ) ) {
return array();
}
$legend = array();
$total_sales = $this->get_order_report_data(
array(
'data' => array(
'_line_total' => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => 'SUM',
'name' => 'order_item_amount',
),
),
'where_meta' => array(
'relation' => 'OR',
array(
'type' => 'order_item_meta',
'meta_key' => array( '_product_id', '_variation_id' ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
'meta_value' => $this->product_ids, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
'operator' => 'IN',
),
),
'query_type' => 'get_var',
'filter_range' => true,
'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
)
);
$total_items = absint(
$this->get_order_report_data(
array(
'data' => array(
'_qty' => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => 'SUM',
'name' => 'order_item_count',
),
),
'where_meta' => array(
'relation' => 'OR',
array(
'type' => 'order_item_meta',
'meta_key' => array( '_product_id', '_variation_id' ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
'meta_value' => $this->product_ids, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
'operator' => 'IN',
),
),
'query_type' => 'get_var',
'filter_range' => true,
'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
)
)
);
$legend[] = array(
/* translators: %s: total items sold */
'title' => sprintf( __( '%s sales for the selected items', 'woocommerce' ), '<strong>' . wc_price( $total_sales ) . '</strong>' ),
'color' => $this->chart_colours['sales_amount'],
'highlight_series' => 1,
);
$legend[] = array(
/* translators: %s: total items purchased */
'title' => sprintf( __( '%s purchases for the selected items', 'woocommerce' ), '<strong>' . ( $total_items ) . '</strong>' ),
'color' => $this->chart_colours['item_count'],
'highlight_series' => 0,
);
return $legend;
}
/**
* Output the report.
*/
public function output_report() {
$ranges = array(
'year' => __( 'Year', 'woocommerce' ),
'last_month' => __( 'Last month', 'woocommerce' ),
'month' => __( 'This month', 'woocommerce' ),
'7day' => __( 'Last 7 days', 'woocommerce' ),
);
$this->chart_colours = array(
'sales_amount' => '#3498db',
'item_count' => '#d4d9dc',
);
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day'; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( ! in_array( $current_range, array( 'custom', 'year', 'last_month', 'month', '7day' ), true ) ) {
$current_range = '7day';
}
$this->check_current_range_nonce( $current_range );
$this->calculate_current_range( $current_range );
include WC()->plugin_path() . '/includes/admin/views/html-report-by-date.php';
}
/**
* Get chart widgets.
*
* @return array
*/
public function get_chart_widgets() {
$widgets = array();
if ( ! empty( $this->product_ids ) ) {
$widgets[] = array(
'title' => __( 'Showing reports for:', 'woocommerce' ),
'callback' => array( $this, 'current_filters' ),
);
}
$widgets[] = array(
'title' => '',
'callback' => array( $this, 'products_widget' ),
);
return $widgets;
}
/**
* Output current filters.
*/
public function current_filters() {
$this->product_ids_titles = array();
foreach ( $this->product_ids as $product_id ) {
$product = wc_get_product( $product_id );
if ( $product ) {
$this->product_ids_titles[] = $product->get_formatted_name();
} else {
$this->product_ids_titles[] = '#' . $product_id;
}
}
echo '<p><strong>' . wp_kses_post( implode( ', ', $this->product_ids_titles ) ) . '</strong></p>';
echo '<p><a class="button" href="' . esc_url( remove_query_arg( 'product_ids' ) ) . '">' . esc_html__( 'Reset', 'woocommerce' ) . '</a></p>';
}
/**
* Output products widget.
*/
public function products_widget() {
?>
<h4 class="section_title"><span><?php esc_html_e( 'Product search', 'woocommerce' ); ?></span></h4>
<div class="section">
<form method="GET">
<div>
<?php // @codingStandardsIgnoreStart ?>
<select class="wc-product-search" style="width:203px;" multiple="multiple" id="product_ids" name="product_ids[]" data-placeholder="<?php esc_attr_e( 'Search for a product…', 'woocommerce' ); ?>" data-action="woocommerce_json_search_products_and_variations"></select>
<button type="submit" class="submit button" value="<?php esc_attr_e( 'Show', 'woocommerce' ); ?>"><?php esc_html_e( 'Show', 'woocommerce' ); ?></button>
<input type="hidden" name="range" value="<?php echo ( ! empty( $_GET['range'] ) ) ? esc_attr( $_GET['range'] ) : ''; ?>" />
<input type="hidden" name="start_date" value="<?php echo ( ! empty( $_GET['start_date'] ) ) ? esc_attr( $_GET['start_date'] ) : ''; ?>" />
<input type="hidden" name="end_date" value="<?php echo ( ! empty( $_GET['end_date'] ) ) ? esc_attr( $_GET['end_date'] ) : ''; ?>" />
<input type="hidden" name="page" value="<?php echo ( ! empty( $_GET['page'] ) ) ? esc_attr( $_GET['page'] ) : ''; ?>" />
<input type="hidden" name="tab" value="<?php echo ( ! empty( $_GET['tab'] ) ) ? esc_attr( $_GET['tab'] ) : ''; ?>" />
<input type="hidden" name="report" value="<?php echo ( ! empty( $_GET['report'] ) ) ? esc_attr( $_GET['report'] ) : ''; ?>" />
<?php wp_nonce_field( 'custom_range', 'wc_reports_nonce', false ); ?>
<?php // @codingStandardsIgnoreEnd ?>
</div>
</form>
</div>
<h4 class="section_title"><span><?php esc_html_e( 'Top sellers', 'woocommerce' ); ?></span></h4>
<div class="section">
<table cellspacing="0">
<?php
$top_sellers = $this->get_order_report_data(
array(
'data' => array(
'_product_id' => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => '',
'name' => 'product_id',
),
'_qty' => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => 'SUM',
'name' => 'order_item_qty',
),
),
'order_by' => 'order_item_qty DESC',
'group_by' => 'product_id',
'limit' => 12,
'query_type' => 'get_results',
'filter_range' => true,
'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
)
);
if ( $top_sellers ) {
// @codingStandardsIgnoreStart
foreach ( $top_sellers as $product ) {
echo '<tr class="' . ( in_array( $product->product_id, $this->product_ids ) ? 'active' : '' ) . '">
<td class="count">' . esc_html( $product->order_item_qty ) . '</td>
<td class="name"><a href="' . esc_url( add_query_arg( 'product_ids', $product->product_id ) ) . '">' . esc_html( get_the_title( $product->product_id ) ) . '</a></td>
<td class="sparkline">' . $this->sales_sparkline( $product->product_id, 7, 'count' ) . '</td>
</tr>';
}
// @codingStandardsIgnoreEnd
} else {
echo '<tr><td colspan="3">' . esc_html__( 'No products found in range', 'woocommerce' ) . '</td></tr>';
}
?>
</table>
</div>
<h4 class="section_title"><span><?php esc_html_e( 'Top freebies', 'woocommerce' ); ?></span></h4>
<div class="section">
<table cellspacing="0">
<?php
$top_freebies = $this->get_order_report_data(
array(
'data' => array(
'_product_id' => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => '',
'name' => 'product_id',
),
'_qty' => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => 'SUM',
'name' => 'order_item_qty',
),
),
'where_meta' => array(
array(
'type' => 'order_item_meta',
'meta_key' => '_line_subtotal', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
'meta_value' => '0', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
'operator' => '=',
),
),
'order_by' => 'order_item_qty DESC',
'group_by' => 'product_id',
'limit' => 12,
'query_type' => 'get_results',
'filter_range' => true,
)
);
if ( $top_freebies ) {
// @codingStandardsIgnoreStart
foreach ( $top_freebies as $product ) {
echo '<tr class="' . ( in_array( $product->product_id, $this->product_ids ) ? 'active' : '' ) . '">
<td class="count">' . esc_html( $product->order_item_qty ) . '</td>
<td class="name"><a href="' . esc_url( add_query_arg( 'product_ids', $product->product_id ) ) . '">' . esc_html( get_the_title( $product->product_id ) ) . '</a></td>
<td class="sparkline">' . $this->sales_sparkline( $product->product_id, 7, 'count' ) . '</td>
</tr>';
}
// @codingStandardsIgnoreEnd
} else {
echo '<tr><td colspan="3">' . esc_html__( 'No products found in range', 'woocommerce' ) . '</td></tr>';
}
?>
</table>
</div>
<h4 class="section_title"><span><?php esc_html_e( 'Top earners', 'woocommerce' ); ?></span></h4>
<div class="section">
<table cellspacing="0">
<?php
$top_earners = $this->get_order_report_data(
array(
'data' => array(
'_product_id' => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => '',
'name' => 'product_id',
),
'_line_total' => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => 'SUM',
'name' => 'order_item_total',
),
),
'order_by' => 'order_item_total DESC',
'group_by' => 'product_id',
'limit' => 12,
'query_type' => 'get_results',
'filter_range' => true,
'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
)
);
if ( $top_earners ) {
// @codingStandardsIgnoreStart
foreach ( $top_earners as $product ) {
echo '<tr class="' . ( in_array( $product->product_id, $this->product_ids ) ? 'active' : '' ) . '">
<td class="count">' . wc_price( $product->order_item_total ) . '</td>
<td class="name"><a href="' . esc_url( add_query_arg( 'product_ids', $product->product_id ) ) . '">' . esc_html( get_the_title( $product->product_id ) ) . '</a></td>
<td class="sparkline">' . $this->sales_sparkline( $product->product_id, 7, 'sales' ) . '</td>
</tr>';
}
// @codingStandardsIgnoreEnd
} else {
echo '<tr><td colspan="3">' . esc_html__( 'No products found in range', 'woocommerce' ) . '</td></tr>';
}
?>
</table>
</div>
<script type="text/javascript">
jQuery( '.section_title' ).on( 'click', function() {
var next_section = jQuery( this ).next( '.section' );
if ( jQuery( next_section ).is( ':visible' ) ) {
return false;
}
jQuery( '.section:visible' ).slideUp();
jQuery( '.section_title' ).removeClass( 'open' );
jQuery( this ).addClass( 'open' ).next( '.section' ).slideDown();
return false;
} );
jQuery( '.section' ).slideUp( 100, function() {
<?php if ( empty( $this->product_ids ) ) : ?>
jQuery( '.section_title:eq(1)' ).trigger( 'click' );
<?php endif; ?>
} );
</script>
<?php
}
/**
* Output an export link.
*/
public function get_export_button() {
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day'; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
?>
<a
href="#"
download="report-<?php echo esc_attr( $current_range ); ?>-<?php echo esc_html( date_i18n( 'Y-m-d', current_time( 'timestamp' ) ) ); ?>.csv"
class="export_csv"
data-export="chart"
data-xaxes="<?php esc_attr_e( 'Date', 'woocommerce' ); ?>"
data-groupby="<?php echo $this->chart_groupby; ?>"<?php // @codingStandardsIgnoreLine ?>
>
<?php esc_html_e( 'Export CSV', 'woocommerce' ); ?>
</a>
<?php
}
/**
* Get the main chart.
*/
public function get_main_chart() {
global $wp_locale;
if ( empty( $this->product_ids ) ) {
?>
<div class="chart-container">
<p class="chart-prompt"><?php esc_html_e( 'Choose a product to view stats', 'woocommerce' ); ?></p>
</div>
<?php
} else {
// Get orders and dates in range - we want the SUM of order totals, COUNT of order items, COUNT of orders, and the date.
$order_item_counts = $this->get_order_report_data(
array(
'data' => array(
'_qty' => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => 'SUM',
'name' => 'order_item_count',
),
'post_date' => array(
'type' => 'post_data',
'function' => '',
'name' => 'post_date',
),
'_product_id' => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => '',
'name' => 'product_id',
),
),
'where_meta' => array(
'relation' => 'OR',
array(
'type' => 'order_item_meta',
'meta_key' => array( '_product_id', '_variation_id' ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
'meta_value' => $this->product_ids, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
'operator' => 'IN',
),
),
'group_by' => 'product_id,' . $this->group_by_query,
'order_by' => 'post_date ASC',
'query_type' => 'get_results',
'filter_range' => true,
'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
)
);
$order_item_amounts = $this->get_order_report_data(
array(
'data' => array(
'_line_total' => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => 'SUM',
'name' => 'order_item_amount',
),
'post_date' => array(
'type' => 'post_data',
'function' => '',
'name' => 'post_date',
),
'_product_id' => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => '',
'name' => 'product_id',
),
),
'where_meta' => array(
'relation' => 'OR',
array(
'type' => 'order_item_meta',
'meta_key' => array( '_product_id', '_variation_id' ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
'meta_value' => $this->product_ids, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
'operator' => 'IN',
),
),
'group_by' => 'product_id, ' . $this->group_by_query,
'order_by' => 'post_date ASC',
'query_type' => 'get_results',
'filter_range' => true,
'order_status' => array( 'completed', 'processing', 'on-hold', 'refunded' ),
)
);
// Prepare data for report.
$order_item_counts = $this->prepare_chart_data( $order_item_counts, 'post_date', 'order_item_count', $this->chart_interval, $this->start_date, $this->chart_groupby );
$order_item_amounts = $this->prepare_chart_data( $order_item_amounts, 'post_date', 'order_item_amount', $this->chart_interval, $this->start_date, $this->chart_groupby );
// Encode in json format.
$chart_data = wp_json_encode(
array(
'order_item_counts' => array_values( $order_item_counts ),
'order_item_amounts' => array_values( $order_item_amounts ),
)
);
?>
<div class="chart-container">
<div class="chart-placeholder main"></div>
</div>
<?php // @codingStandardsIgnoreStart ?>
<script type="text/javascript">
var main_chart;
jQuery(function(){
var order_data = JSON.parse( decodeURIComponent( '<?php echo rawurlencode( $chart_data ); ?>' ) );
var drawGraph = function( highlight ) {
var series = [
{
label: "<?php echo esc_js( __( 'Number of items sold', 'woocommerce' ) ) ?>",
data: order_data.order_item_counts,
color: '<?php echo $this->chart_colours['item_count']; ?>',
bars: { fillColor: '<?php echo $this->chart_colours['item_count']; ?>', fill: true, show: true, lineWidth: 0, barWidth: <?php echo $this->barwidth; ?> * 0.5, align: 'center' },
shadowSize: 0,
hoverable: false
},
{
label: "<?php echo esc_js( __( 'Sales amount', 'woocommerce' ) ) ?>",
data: order_data.order_item_amounts,
yaxis: 2,
color: '<?php echo $this->chart_colours['sales_amount']; ?>',
points: { show: true, radius: 5, lineWidth: 3, fillColor: '#fff', fill: true },
lines: { show: true, lineWidth: 4, fill: false },
shadowSize: 0,
<?php echo $this->get_currency_tooltip(); ?>
}
];
if ( highlight !== 'undefined' && series[ highlight ] ) {
highlight_series = series[ highlight ];
highlight_series.color = '#9c5d90';
if ( highlight_series.bars )
highlight_series.bars.fillColor = '#9c5d90';
if ( highlight_series.lines ) {
highlight_series.lines.lineWidth = 5;
}
}
main_chart = jQuery.plot(
jQuery('.chart-placeholder.main'),
series,
{
legend: {
show: false
},
grid: {
color: '#aaa',
borderColor: 'transparent',
borderWidth: 0,
hoverable: true
},
xaxes: [ {
color: '#aaa',
position: "bottom",
tickColor: 'transparent',
mode: "time",
timeformat: "<?php echo ( 'day' === $this->chart_groupby ) ? '%d %b' : '%b'; ?>",
monthNames: JSON.parse( decodeURIComponent( '<?php echo rawurlencode( wp_json_encode( array_values( $wp_locale->month_abbrev ) ) ); ?>' ) ),
tickLength: 1,
minTickSize: [1, "<?php echo $this->chart_groupby; ?>"],
font: {
color: "#aaa"
}
} ],
yaxes: [
{
min: 0,
minTickSize: 1,
tickDecimals: 0,
color: '#ecf0f1',
font: { color: "#aaa" }
},
{
position: "right",
min: 0,
tickDecimals: 2,
alignTicksWithAxis: 1,
color: 'transparent',
font: { color: "#aaa" }
}
],
}
);
jQuery('.chart-placeholder').trigger( 'resize' );
}
drawGraph();
jQuery('.highlight_series').on( 'mouseenter',
function() {
drawGraph( jQuery(this).data('series') );
} ).on( 'mouseleave',
function() {
drawGraph();
}
);
});
</script>
<?php
// @codingStandardsIgnoreEnd
}
}
}
class-wc-report-stock.php 0000644 00000011007 15155076532 0011433 0 ustar 00 <?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
if ( ! class_exists( 'WP_List_Table' ) ) {
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
}
/**
* WC_Report_Stock.
*
* @author WooThemes
* @category Admin
* @package WooCommerce\Admin\Reports
* @version 2.1.0
*/
class WC_Report_Stock extends WP_List_Table {
/**
* Max items.
*
* @var int
*/
protected $max_items;
/**
* Constructor.
*/
public function __construct() {
parent::__construct(
array(
'singular' => 'stock',
'plural' => 'stock',
'ajax' => false,
)
);
}
/**
* No items found text.
*/
public function no_items() {
_e( 'No products found.', 'woocommerce' );
}
/**
* Don't need this.
*
* @param string $position
*/
public function display_tablenav( $position ) {
if ( 'top' !== $position ) {
parent::display_tablenav( $position );
}
}
/**
* Output the report.
*/
public function output_report() {
$this->prepare_items();
echo '<div id="poststuff" class="woocommerce-reports-wide">';
$this->display();
echo '</div>';
}
/**
* Get column value.
*
* @param mixed $item
* @param string $column_name
*/
public function column_default( $item, $column_name ) {
global $product;
if ( ! $product || $product->get_id() !== $item->id ) {
$product = wc_get_product( $item->id );
}
if ( ! $product ) {
return;
}
switch ( $column_name ) {
case 'product':
if ( $sku = $product->get_sku() ) {
echo esc_html( $sku ) . ' - ';
}
echo esc_html( $product->get_name() );
// Get variation data.
if ( $product->is_type( 'variation' ) ) {
echo '<div class="description">' . wp_kses_post( wc_get_formatted_variation( $product, true ) ) . '</div>';
}
break;
case 'parent':
if ( $item->parent ) {
echo esc_html( get_the_title( $item->parent ) );
} else {
echo '-';
}
break;
case 'stock_status':
if ( $product->is_on_backorder() ) {
$stock_html = '<mark class="onbackorder">' . __( 'On backorder', 'woocommerce' ) . '</mark>';
} elseif ( $product->is_in_stock() ) {
$stock_html = '<mark class="instock">' . __( 'In stock', 'woocommerce' ) . '</mark>';
} else {
$stock_html = '<mark class="outofstock">' . __( 'Out of stock', 'woocommerce' ) . '</mark>';
}
echo apply_filters( 'woocommerce_admin_stock_html', $stock_html, $product );
break;
case 'stock_level':
echo esc_html( $product->get_stock_quantity() );
break;
case 'wc_actions':
?><p>
<?php
$actions = array();
$action_id = $product->is_type( 'variation' ) ? $item->parent : $item->id;
$actions['edit'] = array(
'url' => admin_url( 'post.php?post=' . $action_id . '&action=edit' ),
'name' => __( 'Edit', 'woocommerce' ),
'action' => 'edit',
);
if ( $product->is_visible() ) {
$actions['view'] = array(
'url' => get_permalink( $action_id ),
'name' => __( 'View', 'woocommerce' ),
'action' => 'view',
);
}
$actions = apply_filters( 'woocommerce_admin_stock_report_product_actions', $actions, $product );
foreach ( $actions as $action ) {
printf(
'<a class="button tips %1$s" href="%2$s" data-tip="%3$s">%4$s</a>',
esc_attr( $action['action'] ),
esc_url( $action['url'] ),
sprintf( esc_attr__( '%s product', 'woocommerce' ), $action['name'] ),
esc_html( $action['name'] )
);
}
?>
</p>
<?php
break;
}
}
/**
* Get columns.
*
* @return array
*/
public function get_columns() {
$columns = array(
'product' => __( 'Product', 'woocommerce' ),
'parent' => __( 'Parent', 'woocommerce' ),
'stock_level' => __( 'Units in stock', 'woocommerce' ),
'stock_status' => __( 'Stock status', 'woocommerce' ),
'wc_actions' => __( 'Actions', 'woocommerce' ),
);
return $columns;
}
/**
* Prepare customer list items.
*/
public function prepare_items() {
$this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns() );
$current_page = absint( $this->get_pagenum() );
$per_page = apply_filters( 'woocommerce_admin_stock_report_products_per_page', 20 );
$this->get_items( $current_page, $per_page );
/**
* Pagination.
*/
$this->set_pagination_args(
array(
'total_items' => $this->max_items,
'per_page' => $per_page,
'total_pages' => ceil( $this->max_items / $per_page ),
)
);
}
}
class-wc-report-taxes-by-code.php 0000644 00000020703 15155076532 0012757 0 ustar 00 <?php
/**
* Taxes by tax code report.
*
* @package WooCommerce\Admin\Reports
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* WC_Report_Taxes_By_Code
*
* @package WooCommerce\Admin\Reports
* @version 2.1.0
*/
class WC_Report_Taxes_By_Code extends WC_Admin_Report {
/**
* Get the legend for the main chart sidebar.
*
* @return array
*/
public function get_chart_legend() {
return array();
}
/**
* Output an export link.
*/
public function get_export_button() {
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : 'last_month';
?>
<a
href="#"
download="report-<?php echo esc_attr( $current_range ); ?>-<?php echo esc_attr( date_i18n( 'Y-m-d', current_time( 'timestamp' ) ) ); ?>.csv"
class="export_csv"
data-export="table"
>
<?php esc_html_e( 'Export CSV', 'woocommerce' ); ?>
</a>
<?php
}
/**
* Output the report.
*/
public function output_report() {
$ranges = array(
'year' => __( 'Year', 'woocommerce' ),
'last_month' => __( 'Last month', 'woocommerce' ),
'month' => __( 'This month', 'woocommerce' ),
);
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : 'last_month';
if ( ! in_array( $current_range, array( 'custom', 'year', 'last_month', 'month', '7day' ) ) ) {
$current_range = 'last_month';
}
$this->check_current_range_nonce( $current_range );
$this->calculate_current_range( $current_range );
$hide_sidebar = true;
include WC()->plugin_path() . '/includes/admin/views/html-report-by-date.php';
}
/**
* Get the main chart.
*/
public function get_main_chart() {
global $wpdb;
$query_data = array(
'order_item_name' => array(
'type' => 'order_item',
'function' => '',
'name' => 'tax_rate',
),
'tax_amount' => array(
'type' => 'order_item_meta',
'order_item_type' => 'tax',
'function' => '',
'name' => 'tax_amount',
),
'shipping_tax_amount' => array(
'type' => 'order_item_meta',
'order_item_type' => 'tax',
'function' => '',
'name' => 'shipping_tax_amount',
),
'rate_id' => array(
'type' => 'order_item_meta',
'order_item_type' => 'tax',
'function' => '',
'name' => 'rate_id',
),
'ID' => array(
'type' => 'post_data',
'function' => '',
'name' => 'post_id',
),
);
$query_where = array(
array(
'key' => 'order_item_type',
'value' => 'tax',
'operator' => '=',
),
array(
'key' => 'order_item_name',
'value' => '',
'operator' => '!=',
),
);
// We exclude on-hold orders as they are still pending payment.
$tax_rows_orders = $this->get_order_report_data(
array(
'data' => $query_data,
'where' => $query_where,
'order_by' => 'posts.post_date ASC',
'query_type' => 'get_results',
'filter_range' => true,
'order_types' => wc_get_order_types( 'sales-reports' ),
'order_status' => array( 'completed', 'processing', 'refunded' ),
)
);
$tax_rows_partial_refunds = $this->get_order_report_data(
array(
'data' => $query_data,
'where' => $query_where,
'order_by' => 'posts.post_date ASC',
'query_type' => 'get_results',
'filter_range' => true,
'order_types' => array( 'shop_order_refund' ),
'parent_order_status' => array( 'completed', 'processing' ), // Partial refunds inside refunded orders should be ignored.
)
);
$tax_rows_full_refunds = $this->get_order_report_data(
array(
'data' => $query_data,
'where' => $query_where,
'order_by' => 'posts.post_date ASC',
'query_type' => 'get_results',
'filter_range' => true,
'order_types' => array( 'shop_order_refund' ),
'parent_order_status' => array( 'refunded' ),
)
);
// Merge.
$tax_rows = array();
// Initialize an associative array to store unique post_ids.
$unique_post_ids = array();
foreach ( $tax_rows_orders + $tax_rows_partial_refunds as $tax_row ) {
$key = $tax_row->tax_rate;
$tax_rows[ $key ] = isset( $tax_rows[ $key ] ) ? $tax_rows[ $key ] : (object) array(
'tax_amount' => 0,
'shipping_tax_amount' => 0,
'total_orders' => 0,
);
$tax_rows[ $key ]->tax_rate = $tax_row->tax_rate;
$tax_rows[ $key ]->tax_amount += wc_round_tax_total( $tax_row->tax_amount );
$tax_rows[ $key ]->shipping_tax_amount += wc_round_tax_total( $tax_row->shipping_tax_amount );
if ( ! isset( $unique_post_ids[ $key ] ) || ! in_array( $tax_row->post_id, $unique_post_ids[ $key ], true ) ) {
$unique_post_ids[ $key ] = isset( $unique_post_ids[ $key ] ) ? $unique_post_ids[ $key ] : array();
$unique_post_ids[ $key ][] = $tax_row->post_id;
$tax_rows[ $key ]->total_orders += 1;
}
}
foreach ( $tax_rows_full_refunds as $tax_row ) {
$key = $tax_row->tax_rate;
$tax_rows[ $key ] = isset( $tax_rows[ $key ] ) ? $tax_rows[ $key ] : (object) array(
'tax_amount' => 0,
'shipping_tax_amount' => 0,
'total_orders' => 0,
);
$tax_rows[ $key ]->tax_rate = $tax_row->tax_rate;
$tax_rows[ $key ]->tax_amount += wc_round_tax_total( $tax_row->tax_amount );
$tax_rows[ $key ]->shipping_tax_amount += wc_round_tax_total( $tax_row->shipping_tax_amount );
}
?>
<table class="widefat">
<thead>
<tr>
<th><?php esc_html_e( 'Tax', 'woocommerce' ); ?></th>
<th><?php esc_html_e( 'Rate', 'woocommerce' ); ?></th>
<th class="total_row"><?php esc_html_e( 'Number of orders', 'woocommerce' ); ?></th>
<th class="total_row"><?php esc_html_e( 'Tax amount', 'woocommerce' ); ?> <?php echo wc_help_tip( __( 'This is the sum of the "Tax rows" tax amount within your orders.', 'woocommerce' ) ); ?></th>
<th class="total_row"><?php esc_html_e( 'Shipping tax amount', 'woocommerce' ); ?> <?php echo wc_help_tip( __( 'This is the sum of the "Tax rows" shipping tax amount within your orders.', 'woocommerce' ) ); ?></th>
<th class="total_row"><?php esc_html_e( 'Total tax', 'woocommerce' ); ?> <?php echo wc_help_tip( __( 'This is the total tax for the rate (shipping tax + product tax).', 'woocommerce' ) ); ?></th>
</tr>
</thead>
<?php if ( ! empty( $tax_rows ) ) : ?>
<tbody>
<?php
foreach ( $tax_rows as $rate_id => $tax_row ) {
$rate = $wpdb->get_var( $wpdb->prepare( "SELECT tax_rate FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %d;", $rate_id ) );
?>
<tr>
<th scope="row"><?php echo wp_kses_post( apply_filters( 'woocommerce_reports_taxes_tax_rate', $tax_row->tax_rate, $rate_id, $tax_row ) ); ?></th>
<td><?php echo wp_kses_post( apply_filters( 'woocommerce_reports_taxes_rate', $rate, $rate_id, $tax_row ) ); ?>%</td>
<td class="total_row"><?php echo esc_html( $tax_row->total_orders ); ?></td>
<td class="total_row"><?php echo wc_price( $tax_row->tax_amount ); // phpcs:ignore ?></td>
<td class="total_row"><?php echo wc_price( $tax_row->shipping_tax_amount ); // phpcs:ignore ?></td>
<td class="total_row"><?php echo wc_price( $tax_row->tax_amount + $tax_row->shipping_tax_amount ); // phpcs:ignore ?></td>
</tr>
<?php
}
?>
</tbody>
<tfoot>
<tr>
<th scope="row" colspan="3"><?php esc_html_e( 'Total', 'woocommerce' ); ?></th>
<th class="total_row"><?php echo wc_price( wc_round_tax_total( array_sum( wp_list_pluck( (array) $tax_rows, 'tax_amount' ) ) ) ); // phpcs:ignore ?></th>
<th class="total_row"><?php echo wc_price( wc_round_tax_total( array_sum( wp_list_pluck( (array) $tax_rows, 'shipping_tax_amount' ) ) ) ); // phpcs:ignore ?></th>
<th class="total_row"><strong><?php echo wc_price( wc_round_tax_total( array_sum( wp_list_pluck( (array) $tax_rows, 'tax_amount' ) ) + array_sum( wp_list_pluck( (array) $tax_rows, 'shipping_tax_amount' ) ) ) ); // phpcs:ignore ?></strong></th>
</tr>
</tfoot>
<?php else : ?>
<tbody>
<tr>
<td><?php esc_html_e( 'No taxes found in this period', 'woocommerce' ); ?></td>
</tr>
</tbody>
<?php endif; ?>
</table>
<?php
}
}
class-wc-report-taxes-by-date.php 0000644 00000022317 15155076532 0012765 0 ustar 00 <?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/**
* WC_Report_Taxes_By_Date
*
* @author WooThemes
* @category Admin
* @package WooCommerce\Admin\Reports
* @version 2.1.0
*/
class WC_Report_Taxes_By_Date extends WC_Admin_Report {
/**
* Get the legend for the main chart sidebar.
*
* @return array
*/
public function get_chart_legend() {
return array();
}
/**
* Output an export link.
*/
public function get_export_button() {
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( $_GET['range'] ) : 'last_month';
?>
<a
href="#"
download="report-<?php echo esc_attr( $current_range ); ?>-<?php echo date_i18n( 'Y-m-d', current_time( 'timestamp' ) ); ?>.csv"
class="export_csv"
data-export="table"
>
<?php _e( 'Export CSV', 'woocommerce' ); ?>
</a>
<?php
}
/**
* Output the report.
*/
public function output_report() {
$ranges = array(
'year' => __( 'Year', 'woocommerce' ),
'last_month' => __( 'Last month', 'woocommerce' ),
'month' => __( 'This month', 'woocommerce' ),
);
$current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( $_GET['range'] ) : 'last_month';
if ( ! in_array( $current_range, array( 'custom', 'year', 'last_month', 'month', '7day' ) ) ) {
$current_range = 'last_month';
}
$this->check_current_range_nonce( $current_range );
$this->calculate_current_range( $current_range );
$hide_sidebar = true;
include WC()->plugin_path() . '/includes/admin/views/html-report-by-date.php';
}
/**
* Get the main chart.
*/
public function get_main_chart() {
$query_data = array(
'_order_tax' => array(
'type' => 'meta',
'function' => 'SUM',
'name' => 'tax_amount',
),
'_order_shipping_tax' => array(
'type' => 'meta',
'function' => 'SUM',
'name' => 'shipping_tax_amount',
),
'_order_total' => array(
'type' => 'meta',
'function' => 'SUM',
'name' => 'total_sales',
),
'_order_shipping' => array(
'type' => 'meta',
'function' => 'SUM',
'name' => 'total_shipping',
),
'ID' => array(
'type' => 'post_data',
'function' => 'COUNT',
'name' => 'total_orders',
'distinct' => true,
),
'post_date' => array(
'type' => 'post_data',
'function' => '',
'name' => 'post_date',
),
);
// We exclude on-hold orders are they are still pending payment.
$tax_rows_orders = $this->get_order_report_data(
array(
'data' => $query_data,
'group_by' => $this->group_by_query,
'order_by' => 'post_date ASC',
'query_type' => 'get_results',
'filter_range' => true,
'order_types' => wc_get_order_types( 'sales-reports' ),
'order_status' => array( 'completed', 'processing', 'refunded' ),
)
);
$tax_rows_full_refunds = $this->get_order_report_data(
array(
'data' => array(
'ID' => array(
'type' => 'post_data',
'distinct' => true,
'function' => '',
'name' => 'ID',
),
'post_parent' => array(
'type' => 'post_data',
'function' => '',
'name' => 'post_parent',
),
'post_date' => array(
'type' => 'post_data',
'function' => '',
'name' => 'post_date',
),
),
'query_type' => 'get_results',
'filter_range' => true,
'order_types' => array( 'shop_order_refund' ),
'parent_order_status' => array( 'refunded' ),
)
);
$tax_rows_partial_refunds = $this->get_order_report_data(
array(
'data' => $query_data,
'group_by' => $this->group_by_query,
'order_by' => 'post_date ASC',
'query_type' => 'get_results',
'filter_range' => true,
'order_types' => array( 'shop_order_refund' ),
'parent_order_status' => array( 'completed', 'processing' ), // Partial refunds inside refunded orders should be ignored.
)
);
$tax_rows = array();
foreach ( $tax_rows_orders + $tax_rows_partial_refunds as $tax_row ) {
$key = date( ( 'month' === $this->chart_groupby ) ? 'Ym' : 'Ymd', strtotime( $tax_row->post_date ) );
$tax_rows[ $key ] = isset( $tax_rows[ $key ] ) ? $tax_rows[ $key ] : (object) array(
'tax_amount' => 0,
'shipping_tax_amount' => 0,
'total_sales' => 0,
'total_shipping' => 0,
'total_orders' => 0,
);
}
foreach ( $tax_rows_orders as $tax_row ) {
$key = date( ( 'month' === $this->chart_groupby ) ? 'Ym' : 'Ymd', strtotime( $tax_row->post_date ) );
$tax_rows[ $key ]->total_orders += $tax_row->total_orders;
$tax_rows[ $key ]->tax_amount += $tax_row->tax_amount;
$tax_rows[ $key ]->shipping_tax_amount += $tax_row->shipping_tax_amount;
$tax_rows[ $key ]->total_sales += $tax_row->total_sales;
$tax_rows[ $key ]->total_shipping += $tax_row->total_shipping;
}
foreach ( $tax_rows_partial_refunds as $tax_row ) {
$key = date( ( 'month' === $this->chart_groupby ) ? 'Ym' : 'Ymd', strtotime( $tax_row->post_date ) );
$tax_rows[ $key ]->tax_amount += $tax_row->tax_amount;
$tax_rows[ $key ]->shipping_tax_amount += $tax_row->shipping_tax_amount;
$tax_rows[ $key ]->total_sales += $tax_row->total_sales;
$tax_rows[ $key ]->total_shipping += $tax_row->total_shipping;
}
foreach ( $tax_rows_full_refunds as $tax_row ) {
$key = date( ( 'month' === $this->chart_groupby ) ? 'Ym' : 'Ymd', strtotime( $tax_row->post_date ) );
$tax_rows[ $key ] = isset( $tax_rows[ $key ] ) ? $tax_rows[ $key ] : (object) array(
'tax_amount' => 0,
'shipping_tax_amount' => 0,
'total_sales' => 0,
'total_shipping' => 0,
'total_orders' => 0,
);
$parent_order = wc_get_order( $tax_row->post_parent );
if ( $parent_order ) {
$tax_rows[ $key ]->tax_amount += $parent_order->get_cart_tax() * -1;
$tax_rows[ $key ]->shipping_tax_amount += $parent_order->get_shipping_tax() * -1;
$tax_rows[ $key ]->total_sales += $parent_order->get_total() * -1;
$tax_rows[ $key ]->total_shipping += $parent_order->get_shipping_total() * -1;
}
}
?>
<table class="widefat">
<thead>
<tr>
<th><?php _e( 'Period', 'woocommerce' ); ?></th>
<th class="total_row"><?php _e( 'Number of orders', 'woocommerce' ); ?></th>
<th class="total_row"><?php _e( 'Total sales', 'woocommerce' ); ?> <?php echo wc_help_tip( __( "This is the sum of the 'Order total' field within your orders.", 'woocommerce' ) ); ?></th>
<th class="total_row"><?php _e( 'Total shipping', 'woocommerce' ); ?> <?php echo wc_help_tip( __( "This is the sum of the 'Shipping total' field within your orders.", 'woocommerce' ) ); ?></th>
<th class="total_row"><?php _e( 'Total tax', 'woocommerce' ); ?> <?php echo wc_help_tip( __( 'This is the total tax for the rate (shipping tax + product tax).', 'woocommerce' ) ); ?></th>
<th class="total_row"><?php _e( 'Net profit', 'woocommerce' ); ?> <?php echo wc_help_tip( __( 'Total sales minus shipping and tax.', 'woocommerce' ) ); ?></th>
</tr>
</thead>
<?php if ( ! empty( $tax_rows ) ) : ?>
<tbody>
<?php
foreach ( $tax_rows as $date => $tax_row ) {
$gross = $tax_row->total_sales - $tax_row->total_shipping;
$total_tax = $tax_row->tax_amount + $tax_row->shipping_tax_amount;
?>
<tr>
<th scope="row">
<?php echo ( 'month' === $this->chart_groupby ) ? date_i18n( 'F', strtotime( $date . '01' ) ) : date_i18n( get_option( 'date_format' ), strtotime( $date ) ); ?>
</th>
<td class="total_row"><?php echo $tax_row->total_orders; ?></td>
<td class="total_row"><?php echo wc_price( $gross ); ?></td>
<td class="total_row"><?php echo wc_price( $tax_row->total_shipping ); ?></td>
<td class="total_row"><?php echo wc_price( $total_tax ); ?></td>
<td class="total_row"><?php echo wc_price( $gross - $total_tax ); ?></td>
</tr>
<?php
}
?>
</tbody>
<tfoot>
<?php
$gross = array_sum( wp_list_pluck( (array) $tax_rows, 'total_sales' ) ) - array_sum( wp_list_pluck( (array) $tax_rows, 'total_shipping' ) );
$total_tax = array_sum( wp_list_pluck( (array) $tax_rows, 'tax_amount' ) ) + array_sum( wp_list_pluck( (array) $tax_rows, 'shipping_tax_amount' ) );
?>
<tr>
<th scope="row"><?php _e( 'Totals', 'woocommerce' ); ?></th>
<th class="total_row"><?php echo array_sum( wp_list_pluck( (array) $tax_rows, 'total_orders' ) ); ?></th>
<th class="total_row"><?php echo wc_price( $gross ); ?></th>
<th class="total_row"><?php echo wc_price( array_sum( wp_list_pluck( (array) $tax_rows, 'total_shipping' ) ) ); ?></th>
<th class="total_row"><?php echo wc_price( $total_tax ); ?></th>
<th class="total_row"><?php echo wc_price( $gross - $total_tax ); ?></th>
</tr>
</tfoot>
<?php else : ?>
<tbody>
<tr>
<td><?php _e( 'No taxes found in this period', 'woocommerce' ); ?></td>
</tr>
</tbody>
<?php endif; ?>
</table>
<?php
}
}