PageRenderTime 72ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/admin/woocommerce-admin-reports.php

https://github.com/CammoKing/woocommerce
PHP | 2510 lines | 1986 code | 392 blank | 132 comment | 193 complexity | 42ef7da663d4de377015480463bbc989 MD5 | raw file
Possible License(s): GPL-3.0
  1. <?php
  2. /**
  3. * Admin Reports
  4. *
  5. * Functions used for displaying sales and customer reports in admin.
  6. *
  7. * @author WooThemes
  8. * @category Admin
  9. * @package WooCommerce/Admin/Reports
  10. * @version 1.7
  11. */
  12. if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
  13. /**
  14. * Reports page
  15. *
  16. * Handles the display of the reports page in admin.
  17. *
  18. * @access public
  19. * @return void
  20. */
  21. function woocommerce_reports() {
  22. $current_tab = isset( $_GET['tab'] ) ? sanitize_title( urldecode( $_GET['tab'] ) ) : 'sales';
  23. $current_chart = isset( $_GET['chart'] ) ? absint( urldecode( $_GET['chart'] ) ) : 0;
  24. $charts = apply_filters( 'woocommerce_reports_charts', array(
  25. 'sales' => array(
  26. 'title' => __( 'Sales', 'woocommerce' ),
  27. 'charts' => array(
  28. array(
  29. 'title' => __( 'Overview', 'woocommerce' ),
  30. 'description' => '',
  31. 'hide_title' => true,
  32. 'function' => 'woocommerce_sales_overview'
  33. ),
  34. array(
  35. 'title' => __( 'Sales by day', 'woocommerce' ),
  36. 'description' => '',
  37. 'function' => 'woocommerce_daily_sales'
  38. ),
  39. array(
  40. 'title' => __( 'Sales by month', 'woocommerce' ),
  41. 'description' => '',
  42. 'function' => 'woocommerce_monthly_sales'
  43. ),
  44. array(
  45. 'title' => __( 'Taxes by month', 'woocommerce' ),
  46. 'description' => '',
  47. 'function' => 'woocommerce_monthly_taxes'
  48. ),
  49. array(
  50. 'title' => __( 'Product Sales', 'woocommerce' ),
  51. 'description' => '',
  52. 'function' => 'woocommerce_product_sales'
  53. ),
  54. array(
  55. 'title' => __( 'Top sellers', 'woocommerce' ),
  56. 'description' => '',
  57. 'function' => 'woocommerce_top_sellers'
  58. ),
  59. array(
  60. 'title' => __( 'Top earners', 'woocommerce' ),
  61. 'description' => '',
  62. 'function' => 'woocommerce_top_earners'
  63. ),
  64. array(
  65. 'title' => __( 'Sales by category', 'woocommerce' ),
  66. 'description' => '',
  67. 'function' => 'woocommerce_category_sales'
  68. ),
  69. array(
  70. 'title' => __( 'Sales by coupon', 'woocommerce' ),
  71. 'description' => '',
  72. 'function' => 'woocommerce_coupon_sales'
  73. )
  74. )
  75. ),
  76. 'customers' => array(
  77. 'title' => __( 'Customers', 'woocommerce' ),
  78. 'charts' => array(
  79. array(
  80. 'title' => __( 'Overview', 'woocommerce' ),
  81. 'description' => '',
  82. 'hide_title' => true,
  83. 'function' => 'woocommerce_customer_overview'
  84. ),
  85. )
  86. ),
  87. 'stock' => array(
  88. 'title' => __( 'Stock', 'woocommerce' ),
  89. 'charts' => array(
  90. array(
  91. 'title' => __( 'Overview', 'woocommerce' ),
  92. 'description' => '',
  93. 'hide_title' => true,
  94. 'function' => 'woocommerce_stock_overview'
  95. ),
  96. )
  97. )
  98. ) );
  99. ?>
  100. <div class="wrap woocommerce">
  101. <div class="icon32 icon32-woocommerce-reports" id="icon-woocommerce"><br /></div><h2 class="nav-tab-wrapper woo-nav-tab-wrapper">
  102. <?php
  103. foreach ( $charts as $key => $chart ) {
  104. echo '<a href="'.admin_url( 'admin.php?page=woocommerce_reports&tab=' . urlencode( $key ) ).'" class="nav-tab ';
  105. if ( $current_tab == $key ) echo 'nav-tab-active';
  106. echo '">' . esc_html( $chart[ 'title' ] ) . '</a>';
  107. }
  108. ?>
  109. <?php do_action('woocommerce_reports_tabs'); ?>
  110. </h2>
  111. <?php if ( sizeof( $charts[ $current_tab ]['charts'] ) > 1 ) {
  112. ?>
  113. <ul class="subsubsub">
  114. <li><?php
  115. $links = array();
  116. foreach ( $charts[ $current_tab ]['charts'] as $key => $chart ) {
  117. $link = '<a href="admin.php?page=woocommerce_reports&tab=' . urlencode( $current_tab ) . '&amp;chart=' . urlencode( $key ) . '" class="';
  118. if ( $key == $current_chart ) $link .= 'current';
  119. $link .= '">' . $chart['title'] . '</a>';
  120. $links[] = $link;
  121. }
  122. echo implode(' | </li><li>', $links);
  123. ?></li>
  124. </ul>
  125. <br class="clear" />
  126. <?php
  127. }
  128. if ( isset( $charts[ $current_tab ][ 'charts' ][ $current_chart ] ) ) {
  129. $chart = $charts[ $current_tab ][ 'charts' ][ $current_chart ];
  130. if ( ! isset( $chart['hide_title'] ) || $chart['hide_title'] != true )
  131. echo '<h3>' . $chart['title'] . '</h3>';
  132. if ( $chart['description'] )
  133. echo '<p>' . $chart['description'] . '</p>';
  134. $func = $chart['function'];
  135. if ( $func && function_exists( $func ) )
  136. $func();
  137. }
  138. ?>
  139. </div>
  140. <?php
  141. }
  142. /**
  143. * Output JavaScript for highlighting weekends on charts.
  144. *
  145. * @access public
  146. * @return void
  147. */
  148. function woocommerce_weekend_area_js() {
  149. ?>
  150. function weekendAreas(axes) {
  151. var markings = [];
  152. var d = new Date(axes.xaxis.min);
  153. // go to the first Saturday
  154. d.setUTCDate(d.getUTCDate() - ((d.getUTCDay() + 1) % 7))
  155. d.setUTCSeconds(0);
  156. d.setUTCMinutes(0);
  157. d.setUTCHours(0);
  158. var i = d.getTime();
  159. do {
  160. markings.push({ xaxis: { from: i, to: i + 2 * 24 * 60 * 60 * 1000 } });
  161. i += 7 * 24 * 60 * 60 * 1000;
  162. } while (i < axes.xaxis.max);
  163. return markings;
  164. }
  165. <?php
  166. }
  167. /**
  168. * Output JavaScript for chart tooltips.
  169. *
  170. * @access public
  171. * @return void
  172. */
  173. function woocommerce_tooltip_js() {
  174. ?>
  175. function showTooltip(x, y, contents) {
  176. jQuery('<div id="tooltip">' + contents + '</div>').css( {
  177. position: 'absolute',
  178. display: 'none',
  179. top: y + 5,
  180. left: x + 5,
  181. padding: '5px 10px',
  182. border: '3px solid #3da5d5',
  183. background: '#288ab7'
  184. }).appendTo("body").fadeIn(200);
  185. }
  186. var previousPoint = null;
  187. jQuery("#placeholder").bind("plothover", function (event, pos, item) {
  188. if (item) {
  189. if (previousPoint != item.dataIndex) {
  190. previousPoint = item.dataIndex;
  191. jQuery("#tooltip").remove();
  192. if (item.series.label=="<?php echo esc_js( __( 'Sales amount', 'woocommerce' ) ) ?>") {
  193. var y = item.datapoint[1].toFixed(2);
  194. showTooltip(item.pageX, item.pageY, item.series.label + " - " + "<?php echo get_woocommerce_currency_symbol(); ?>" + y);
  195. } else if (item.series.label=="<?php echo esc_js( __( 'Number of sales', 'woocommerce' ) ) ?>") {
  196. var y = item.datapoint[1];
  197. showTooltip(item.pageX, item.pageY, item.series.label + " - " + y);
  198. } else {
  199. var y = item.datapoint[1];
  200. showTooltip(item.pageX, item.pageY, y);
  201. }
  202. }
  203. }
  204. else {
  205. jQuery("#tooltip").remove();
  206. previousPoint = null;
  207. }
  208. });
  209. <?php
  210. }
  211. /**
  212. * Output Javascript for date ranges.
  213. *
  214. * @access public
  215. * @return void
  216. */
  217. function woocommerce_datepicker_js() {
  218. global $woocommerce;
  219. ?>
  220. var dates = jQuery( "#from, #to" ).datepicker({
  221. defaultDate: "",
  222. dateFormat: "yy-mm-dd",
  223. numberOfMonths: 1,
  224. minDate: "-12M",
  225. maxDate: "+0D",
  226. showButtonPanel: true,
  227. showOn: "button",
  228. buttonImage: "<?php echo $woocommerce->plugin_url(); ?>/assets/images/calendar.png",
  229. buttonImageOnly: true,
  230. onSelect: function( selectedDate ) {
  231. var option = this.id == "from" ? "minDate" : "maxDate",
  232. instance = jQuery( this ).data( "datepicker" ),
  233. date = jQuery.datepicker.parseDate(
  234. instance.settings.dateFormat ||
  235. jQuery.datepicker._defaults.dateFormat,
  236. selectedDate, instance.settings );
  237. dates.not( this ).datepicker( "option", option, date );
  238. }
  239. });
  240. <?php
  241. }
  242. /**
  243. * Output the sales overview chart.
  244. *
  245. * @access public
  246. * @return void
  247. */
  248. function woocommerce_sales_overview() {
  249. global $start_date, $end_date, $woocommerce, $wpdb, $wp_locale;
  250. $total_sales = $total_orders = $order_items = $discount_total = $shipping_total = 0;
  251. $order_totals = $wpdb->get_row( $wpdb->prepare( "
  252. SELECT SUM(meta.meta_value) AS total_sales, COUNT(posts.ID) AS total_orders FROM {$wpdb->posts} AS posts
  253. LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id
  254. LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID
  255. LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
  256. LEFT JOIN {$wpdb->terms} AS term USING( term_id )
  257. WHERE meta.meta_key = '_order_total'
  258. AND posts.post_type = 'shop_order'
  259. AND posts.post_status = 'publish'
  260. AND tax.taxonomy = 'shop_order_status'
  261. AND term.slug IN ('" . implode( "','", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "')
  262. " ) );
  263. $total_sales = $order_totals->total_sales;
  264. $total_orders = absint( $order_totals->total_orders );
  265. $discount_total = $wpdb->get_var( $wpdb->prepare( "
  266. SELECT SUM(meta.meta_value) AS total_sales FROM {$wpdb->posts} AS posts
  267. LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id
  268. LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID
  269. LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
  270. LEFT JOIN {$wpdb->terms} AS term USING( term_id )
  271. WHERE meta.meta_key IN ('_order_discount', '_cart_discount')
  272. AND posts.post_type = 'shop_order'
  273. AND posts.post_status = 'publish'
  274. AND tax.taxonomy = 'shop_order_status'
  275. AND term.slug IN ('" . implode( "','", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "')
  276. " ) );
  277. $shipping_total = $wpdb->get_var( $wpdb->prepare( "
  278. SELECT SUM(meta.meta_value) AS total_sales FROM {$wpdb->posts} AS posts
  279. LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id
  280. LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID
  281. LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
  282. LEFT JOIN {$wpdb->terms} AS term USING( term_id )
  283. WHERE meta.meta_key = '_order_shipping'
  284. AND posts.post_type = 'shop_order'
  285. AND posts.post_status = 'publish'
  286. AND tax.taxonomy = 'shop_order_status'
  287. AND term.slug IN ('" . implode( "','", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "')
  288. " ) );
  289. $order_items = absint( $wpdb->get_var( $wpdb->prepare( "
  290. SELECT SUM( order_item_meta.meta_value )
  291. FROM {$wpdb->prefix}woocommerce_order_items as order_items
  292. LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id
  293. LEFT JOIN {$wpdb->posts} AS posts ON order_items.order_id = posts.ID
  294. LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID = rel.object_ID
  295. LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
  296. LEFT JOIN {$wpdb->terms} AS term USING( term_id )
  297. WHERE term.slug IN ('" . implode( "','", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "')
  298. AND posts.post_status = 'publish'
  299. AND tax.taxonomy = 'shop_order_status'
  300. AND order_items.order_item_type = 'line_item'
  301. AND order_item_meta.meta_key = '_qty'
  302. " ) ) );
  303. ?>
  304. <div id="poststuff" class="woocommerce-reports-wrap">
  305. <div class="woocommerce-reports-sidebar">
  306. <div class="postbox">
  307. <h3><span><?php _e( 'Total sales', 'woocommerce' ); ?></span></h3>
  308. <div class="inside">
  309. <p class="stat"><?php if ($total_sales>0) echo woocommerce_price($total_sales); else _e( 'n/a', 'woocommerce' ); ?></p>
  310. </div>
  311. </div>
  312. <div class="postbox">
  313. <h3><span><?php _e( 'Total orders', 'woocommerce' ); ?></span></h3>
  314. <div class="inside">
  315. <p class="stat"><?php if ( $total_orders > 0 ) echo $total_orders . ' (' . $order_items . ' ' . __( 'items', 'woocommerce' ) . ')'; else _e( 'n/a', 'woocommerce' ); ?></p>
  316. </div>
  317. </div>
  318. <div class="postbox">
  319. <h3><span><?php _e( 'Average order total', 'woocommerce' ); ?></span></h3>
  320. <div class="inside">
  321. <p class="stat"><?php if ($total_orders>0) echo woocommerce_price($total_sales/$total_orders); else _e( 'n/a', 'woocommerce' ); ?></p>
  322. </div>
  323. </div>
  324. <div class="postbox">
  325. <h3><span><?php _e( 'Average order items', 'woocommerce' ); ?></span></h3>
  326. <div class="inside">
  327. <p class="stat"><?php if ($total_orders>0) echo number_format($order_items/$total_orders, 2); else _e( 'n/a', 'woocommerce' ); ?></p>
  328. </div>
  329. </div>
  330. <div class="postbox">
  331. <h3><span><?php _e( 'Discounts used', 'woocommerce' ); ?></span></h3>
  332. <div class="inside">
  333. <p class="stat"><?php if ($discount_total>0) echo woocommerce_price($discount_total); else _e( 'n/a', 'woocommerce' ); ?></p>
  334. </div>
  335. </div>
  336. <div class="postbox">
  337. <h3><span><?php _e( 'Total shipping costs', 'woocommerce' ); ?></span></h3>
  338. <div class="inside">
  339. <p class="stat"><?php if ($shipping_total>0) echo woocommerce_price($shipping_total); else _e( 'n/a', 'woocommerce' ); ?></p>
  340. </div>
  341. </div>
  342. </div>
  343. <div class="woocommerce-reports-main">
  344. <div class="postbox">
  345. <h3><span><?php _e( 'This month\'s sales', 'woocommerce' ); ?></span></h3>
  346. <div class="inside chart">
  347. <div id="placeholder" style="width:100%; overflow:hidden; height:568px; position:relative;"></div>
  348. </div>
  349. </div>
  350. </div>
  351. </div>
  352. <?php
  353. $start_date = strtotime( date('Ymd', strtotime( date('Ym', current_time('timestamp') ) . '01' ) ) );
  354. $end_date = strtotime( date('Ymd', current_time( 'timestamp' ) ) );
  355. // Blank date ranges to begin
  356. $order_counts = $order_amounts = array();
  357. $count = 0;
  358. $days = ( $end_date - $start_date ) / ( 60 * 60 * 24 );
  359. if ( $days == 0 )
  360. $days = 1;
  361. while ( $count < $days ) {
  362. $time = strtotime( date( 'Ymd', strtotime( '+ ' . $count . ' DAY', $start_date ) ) ) . '000';
  363. $order_counts[ $time ] = $order_amounts[ $time ] = 0;
  364. $count++;
  365. }
  366. // Get order ids and dates in range
  367. $orders = $wpdb->get_results( $wpdb->prepare( "
  368. SELECT posts.ID, posts.post_date FROM {$wpdb->posts} AS posts
  369. LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID = rel.object_ID
  370. LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
  371. LEFT JOIN {$wpdb->terms} AS term USING( term_id )
  372. WHERE posts.post_type = 'shop_order'
  373. AND posts.post_status = 'publish'
  374. AND tax.taxonomy = 'shop_order_status'
  375. AND term.slug IN ('" . implode( "','", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "')
  376. AND post_date > '" . date('Y-m-d', $start_date ) . "'
  377. AND post_date < '" . date('Y-m-d', strtotime('+1 day', $end_date ) ) . "'
  378. ORDER BY post_date ASC
  379. " ) );
  380. if ( $orders ) {
  381. foreach ( $orders as $order ) {
  382. $order_total = get_post_meta( $order->ID, '_order_total', true );
  383. $time = strtotime( date( 'Ymd', strtotime( $order->post_date ) ) ) . '000';
  384. if ( isset( $order_counts[ $time ] ) )
  385. $order_counts[ $time ]++;
  386. else
  387. $order_counts[ $time ] = 1;
  388. if ( isset( $order_amounts[ $time ] ) )
  389. $order_amounts[ $time ] = $order_amounts[ $time ] + $order_total;
  390. else
  391. $order_amounts[ $time ] = floatval( $order_total );
  392. }
  393. }
  394. $order_counts_array = $order_amounts_array = array();
  395. foreach ( $order_counts as $key => $count )
  396. $order_counts_array[] = array( esc_js( $key ), esc_js( $count ) );
  397. foreach ( $order_amounts as $key => $amount )
  398. $order_amounts_array[] = array( esc_js( $key ), esc_js( $amount ) );
  399. $order_data = array( 'order_counts' => $order_counts_array, 'order_amounts' => $order_amounts_array );
  400. $chart_data = json_encode( $order_data );
  401. ?>
  402. <script type="text/javascript">
  403. jQuery(function(){
  404. var order_data = jQuery.parseJSON( '<?php echo $chart_data; ?>' );
  405. var d = order_data.order_counts;
  406. var d2 = order_data.order_amounts;
  407. for (var i = 0; i < d.length; ++i) d[i][0] += 60 * 60 * 1000;
  408. for (var i = 0; i < d2.length; ++i) d2[i][0] += 60 * 60 * 1000;
  409. var placeholder = jQuery("#placeholder");
  410. var plot = jQuery.plot(placeholder, [ { label: "<?php echo esc_js( __( 'Number of sales', 'woocommerce' ) ) ?>", data: d }, { label: "<?php echo esc_js( __( 'Sales amount', 'woocommerce' ) ) ?>", data: d2, yaxis: 2 } ], {
  411. series: {
  412. lines: { show: true, fill: true },
  413. points: { show: true }
  414. },
  415. grid: {
  416. show: true,
  417. aboveData: false,
  418. color: '#aaa',
  419. backgroundColor: '#fff',
  420. borderWidth: 2,
  421. borderColor: '#aaa',
  422. clickable: false,
  423. hoverable: true,
  424. markings: weekendAreas
  425. },
  426. xaxis: {
  427. mode: "time",
  428. timeformat: "%d %b",
  429. monthNames: <?php echo json_encode( array_values( $wp_locale->month_abbrev ) ) ?>,
  430. tickLength: 1,
  431. minTickSize: [1, "day"]
  432. },
  433. yaxes: [ { min: 0, tickSize: 10, tickDecimals: 0 }, { position: "right", min: 0, tickDecimals: 2 } ],
  434. colors: ["#8a4b75", "#47a03e"]
  435. });
  436. placeholder.resize();
  437. <?php woocommerce_weekend_area_js(); ?>
  438. <?php woocommerce_tooltip_js(); ?>
  439. });
  440. </script>
  441. <?php
  442. }
  443. /**
  444. * Output the daily sales chart.
  445. *
  446. * @access public
  447. * @return void
  448. */
  449. function woocommerce_daily_sales() {
  450. global $start_date, $end_date, $woocommerce, $wpdb, $wp_locale;
  451. $start_date = isset( $_POST['start_date'] ) ? $_POST['start_date'] : '';
  452. $end_date = isset( $_POST['end_date'] ) ? $_POST['end_date'] : '';
  453. if ( ! $start_date)
  454. $start_date = date( 'Ymd', strtotime( date('Ym', current_time( 'timestamp' ) ) . '01' ) );
  455. if ( ! $end_date)
  456. $end_date = date( 'Ymd', current_time( 'timestamp' ) );
  457. $start_date = strtotime( $start_date );
  458. $end_date = strtotime( $end_date );
  459. $total_sales = $total_orders = $order_items = 0;
  460. // Blank date ranges to begin
  461. $order_counts = $order_amounts = array();
  462. $count = 0;
  463. $days = ( $end_date - $start_date ) / ( 60 * 60 * 24 );
  464. if ( $days == 0 )
  465. $days = 1;
  466. while ( $count < $days ) {
  467. $time = strtotime( date( 'Ymd', strtotime( '+ ' . $count . ' DAY', $start_date ) ) ) . '000';
  468. $order_counts[ $time ] = $order_amounts[ $time ] = 0;
  469. $count++;
  470. }
  471. // Get order ids and dates in range
  472. $orders = $wpdb->get_results( $wpdb->prepare( "
  473. SELECT posts.ID, posts.post_date, meta.meta_value AS total_sales FROM {$wpdb->posts} AS posts
  474. LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id
  475. LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID = rel.object_ID
  476. LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
  477. LEFT JOIN {$wpdb->terms} AS term USING( term_id )
  478. WHERE meta.meta_key = '_order_total'
  479. AND posts.post_type = 'shop_order'
  480. AND posts.post_status = 'publish'
  481. AND tax.taxonomy = 'shop_order_status'
  482. AND term.slug IN ('" . implode( "','", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "')
  483. AND post_date > '" . date('Y-m-d', $start_date ) . "'
  484. AND post_date < '" . date('Y-m-d', strtotime('+1 day', $end_date ) ) . "'
  485. GROUP BY posts.ID
  486. ORDER BY post_date ASC
  487. " ) );
  488. if ( $orders ) {
  489. $total_orders = sizeof( $orders );
  490. foreach ( $orders as $order ) {
  491. // get order timestamp
  492. $time = strtotime( date( 'Ymd', strtotime( $order->post_date ) ) ) . '000';
  493. // Add order total
  494. $total_sales += $order->total_sales;
  495. // Get items
  496. $order_items += absint( $wpdb->get_var( $wpdb->prepare( "
  497. SELECT SUM( order_item_meta.meta_value )
  498. FROM {$wpdb->prefix}woocommerce_order_items as order_items
  499. LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id
  500. WHERE order_id = %d
  501. AND order_items.order_item_type = 'line_item'
  502. AND order_item_meta.meta_key = '_qty'
  503. ", $order->ID ) ) );
  504. // Set times
  505. if ( isset( $order_counts[ $time ] ) )
  506. $order_counts[ $time ]++;
  507. else
  508. $order_counts[ $time ] = 1;
  509. if ( isset( $order_amounts[ $time ] ) )
  510. $order_amounts[ $time ] = $order_amounts[ $time ] + $order->total_sales;
  511. else
  512. $order_amounts[ $time ] = floatval( $order->total_sales );
  513. }
  514. }
  515. ?>
  516. <form method="post" action="">
  517. <p><label for="from"><?php _e( 'From:', 'woocommerce' ); ?></label> <input type="text" name="start_date" id="from" readonly="readonly" value="<?php echo esc_attr( date('Y-m-d', $start_date) ); ?>" /> <label for="to"><?php _e( 'To:', 'woocommerce' ); ?></label> <input type="text" name="end_date" id="to" readonly="readonly" value="<?php echo esc_attr( date('Y-m-d', $end_date) ); ?>" /> <input type="submit" class="button" value="<?php _e( 'Show', 'woocommerce' ); ?>" /></p>
  518. </form>
  519. <div id="poststuff" class="woocommerce-reports-wrap">
  520. <div class="woocommerce-reports-sidebar">
  521. <div class="postbox">
  522. <h3><span><?php _e( 'Total sales in range', 'woocommerce' ); ?></span></h3>
  523. <div class="inside">
  524. <p class="stat"><?php if ( $total_sales > 0 ) echo woocommerce_price( $total_sales ); else _e( 'n/a', 'woocommerce' ); ?></p>
  525. </div>
  526. </div>
  527. <div class="postbox">
  528. <h3><span><?php _e( 'Total orders in range', 'woocommerce' ); ?></span></h3>
  529. <div class="inside">
  530. <p class="stat"><?php if ( $total_orders > 0 ) echo $total_orders . ' (' . $order_items . ' ' . __( 'items', 'woocommerce' ) . ')'; else _e( 'n/a', 'woocommerce' ); ?></p>
  531. </div>
  532. </div>
  533. <div class="postbox">
  534. <h3><span><?php _e( 'Average order total in range', 'woocommerce' ); ?></span></h3>
  535. <div class="inside">
  536. <p class="stat"><?php if ( $total_orders > 0 ) echo woocommerce_price( $total_sales / $total_orders ); else _e( 'n/a', 'woocommerce' ); ?></p>
  537. </div>
  538. </div>
  539. <div class="postbox">
  540. <h3><span><?php _e( 'Average order items in range', 'woocommerce' ); ?></span></h3>
  541. <div class="inside">
  542. <p class="stat"><?php if ( $total_orders > 0 ) echo number_format( $order_items / $total_orders, 2 ); else _e( 'n/a', 'woocommerce' ); ?></p>
  543. </div>
  544. </div>
  545. </div>
  546. <div class="woocommerce-reports-main">
  547. <div class="postbox">
  548. <h3><span><?php _e( 'Sales in range', 'woocommerce' ); ?></span></h3>
  549. <div class="inside chart">
  550. <div id="placeholder" style="width:100%; overflow:hidden; height:568px; position:relative;"></div>
  551. </div>
  552. </div>
  553. </div>
  554. </div>
  555. <?php
  556. $order_counts_array = $order_amounts_array = array();
  557. foreach ( $order_counts as $key => $count )
  558. $order_counts_array[] = array( esc_js( $key ), esc_js( $count ) );
  559. foreach ( $order_amounts as $key => $amount )
  560. $order_amounts_array[] = array( esc_js( $key ), esc_js( $amount ) );
  561. $order_data = array( 'order_counts' => $order_counts_array, 'order_amounts' => $order_amounts_array );
  562. $chart_data = json_encode($order_data);
  563. ?>
  564. <script type="text/javascript">
  565. jQuery(function(){
  566. var order_data = jQuery.parseJSON( '<?php echo $chart_data; ?>' );
  567. var d = order_data.order_counts;
  568. var d2 = order_data.order_amounts;
  569. for (var i = 0; i < d.length; ++i) d[i][0] += 60 * 60 * 1000;
  570. for (var i = 0; i < d2.length; ++i) d2[i][0] += 60 * 60 * 1000;
  571. var placeholder = jQuery("#placeholder");
  572. var plot = jQuery.plot(placeholder, [ { label: "<?php echo esc_js( __( 'Number of sales', 'woocommerce' ) ) ?>", data: d }, { label: "<?php echo esc_js( __( 'Sales amount', 'woocommerce' ) ) ?>", data: d2, yaxis: 2 } ], {
  573. series: {
  574. lines: { show: true, fill: true },
  575. points: { show: true }
  576. },
  577. grid: {
  578. show: true,
  579. aboveData: false,
  580. color: '#aaa',
  581. backgroundColor: '#fff',
  582. borderWidth: 2,
  583. borderColor: '#aaa',
  584. clickable: false,
  585. hoverable: true,
  586. markings: weekendAreas
  587. },
  588. xaxis: {
  589. mode: "time",
  590. timeformat: "%d %b",
  591. monthNames: <?php echo json_encode( array_values( $wp_locale->month_abbrev ) ) ?>,
  592. tickLength: 1,
  593. minTickSize: [1, "day"]
  594. },
  595. yaxes: [ { min: 0, tickSize: 10, tickDecimals: 0 }, { position: "right", min: 0, tickDecimals: 2 } ],
  596. colors: ["#8a4b75", "#47a03e"]
  597. });
  598. placeholder.resize();
  599. <?php woocommerce_weekend_area_js(); ?>
  600. <?php woocommerce_tooltip_js(); ?>
  601. <?php woocommerce_datepicker_js(); ?>
  602. });
  603. </script>
  604. <?php
  605. }
  606. /**
  607. * Output the monthly sales chart.
  608. *
  609. * @access public
  610. * @return void
  611. */
  612. function woocommerce_monthly_sales() {
  613. global $start_date, $end_date, $woocommerce, $wpdb, $wp_locale;
  614. $first_year = $wpdb->get_var( $wpdb->prepare( "SELECT post_date FROM $wpdb->posts WHERE post_date != 0 ORDER BY post_date ASC LIMIT 1;" ) );
  615. $first_year = $first_year ? date( 'Y', strtotime( $first_year ) ) : date('Y');
  616. $current_year = isset( $_POST['show_year'] ) ? $_POST['show_year'] : date( 'Y', current_time( 'timestamp' ) );
  617. $start_date = strtotime( $current_year . '0101' );
  618. $total_sales = $total_orders = $order_items = 0;
  619. $order_counts = $order_amounts = array();
  620. for ( $count = 0; $count < 12; $count++ ) {
  621. $time = strtotime( date('Ym', strtotime( '+ ' . $count . ' MONTH', $start_date ) ) . '01' ) . '000';
  622. if ( $time > current_time( 'timestamp' ) . '000' )
  623. continue;
  624. $month = date( 'Ym', strtotime(date('Ym', strtotime('+ '.$count.' MONTH', $start_date)).'01') );
  625. $months_orders = $wpdb->get_row( $wpdb->prepare( "
  626. SELECT SUM(meta.meta_value) AS total_sales, COUNT(posts.ID) AS total_orders FROM {$wpdb->posts} AS posts
  627. LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id
  628. LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID
  629. LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
  630. LEFT JOIN {$wpdb->terms} AS term USING( term_id )
  631. WHERE meta.meta_key = '_order_total'
  632. AND posts.post_type = 'shop_order'
  633. AND posts.post_status = 'publish'
  634. AND tax.taxonomy = 'shop_order_status'
  635. AND term.slug IN ('" . implode( "','", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "')
  636. AND '{$month}' = date_format(posts.post_date,'%%Y%%m')
  637. " ) );
  638. $order_counts[ $time ] = (int) $months_orders->total_orders;
  639. $order_amounts[ $time ] = (float) $months_orders->total_sales;
  640. $total_orders += (int) $months_orders->total_orders;
  641. $total_sales += (float) $months_orders->total_sales;
  642. // Count order items
  643. $order_items += absint( $wpdb->get_var( $wpdb->prepare( "
  644. SELECT SUM( order_item_meta.meta_value )
  645. FROM {$wpdb->prefix}woocommerce_order_items as order_items
  646. LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id
  647. LEFT JOIN {$wpdb->posts} AS posts ON order_items.order_id = posts.ID
  648. LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID = rel.object_ID
  649. LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
  650. LEFT JOIN {$wpdb->terms} AS term USING( term_id )
  651. WHERE term.slug IN ('" . implode( "','", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "')
  652. AND posts.post_status = 'publish'
  653. AND tax.taxonomy = 'shop_order_status'
  654. AND '{$month}' = date_format( posts.post_date, '%%Y%%m' )
  655. AND order_items.order_item_type = 'line_item'
  656. AND order_item_meta.meta_key = '_qty'
  657. " ) ) );
  658. }
  659. ?>
  660. <form method="post" action="">
  661. <p><label for="show_year"><?php _e( 'Year:', 'woocommerce' ); ?></label>
  662. <select name="show_year" id="show_year">
  663. <?php
  664. for ( $i = $first_year; $i <= date( 'Y' ); $i++ )
  665. printf('<option value="%s" %s>%s</option>', $i, selected( $current_year, $i, false ), $i );
  666. ?>
  667. </select> <input type="submit" class="button" value="<?php _e( 'Show', 'woocommerce' ); ?>" /></p>
  668. </form>
  669. <div id="poststuff" class="woocommerce-reports-wrap">
  670. <div class="woocommerce-reports-sidebar">
  671. <div class="postbox">
  672. <h3><span><?php _e( 'Total sales for year', 'woocommerce' ); ?></span></h3>
  673. <div class="inside">
  674. <p class="stat"><?php if ($total_sales>0) echo woocommerce_price($total_sales); else _e( 'n/a', 'woocommerce' ); ?></p>
  675. </div>
  676. </div>
  677. <div class="postbox">
  678. <h3><span><?php _e( 'Total orders for year', 'woocommerce' ); ?></span></h3>
  679. <div class="inside">
  680. <p class="stat"><?php if ( $total_orders > 0 ) echo $total_orders . ' (' . $order_items . ' ' . __( 'items', 'woocommerce' ) . ')'; else _e( 'n/a', 'woocommerce' ); ?></p>
  681. </div>
  682. </div>
  683. <div class="postbox">
  684. <h3><span><?php _e( 'Average order total for year', 'woocommerce' ); ?></span></h3>
  685. <div class="inside">
  686. <p class="stat"><?php if ($total_orders>0) echo woocommerce_price($total_sales/$total_orders); else _e( 'n/a', 'woocommerce' ); ?></p>
  687. </div>
  688. </div>
  689. <div class="postbox">
  690. <h3><span><?php _e( 'Average order items for year', 'woocommerce' ); ?></span></h3>
  691. <div class="inside">
  692. <p class="stat"><?php if ($total_orders>0) echo number_format($order_items/$total_orders, 2); else _e( 'n/a', 'woocommerce' ); ?></p>
  693. </div>
  694. </div>
  695. </div>
  696. <div class="woocommerce-reports-main">
  697. <div class="postbox">
  698. <h3><span><?php _e( 'Monthly sales for year', 'woocommerce' ); ?></span></h3>
  699. <div class="inside chart">
  700. <div id="placeholder" style="width:100%; overflow:hidden; height:568px; position:relative;"></div>
  701. </div>
  702. </div>
  703. </div>
  704. </div>
  705. <?php
  706. $order_counts_array = $order_amounts_array = array();
  707. foreach ( $order_counts as $key => $count )
  708. $order_counts_array[] = array( esc_js( $key ), esc_js( $count ) );
  709. foreach ( $order_amounts as $key => $amount )
  710. $order_amounts_array[] = array( esc_js( $key ), esc_js( $amount ) );
  711. $order_data = array( 'order_counts' => $order_counts_array, 'order_amounts' => $order_amounts_array );
  712. $chart_data = json_encode( $order_data );
  713. ?>
  714. <script type="text/javascript">
  715. jQuery(function(){
  716. var order_data = jQuery.parseJSON( '<?php echo $chart_data; ?>' );
  717. var d = order_data.order_counts;
  718. var d2 = order_data.order_amounts;
  719. var placeholder = jQuery("#placeholder");
  720. var plot = jQuery.plot(placeholder, [ { label: "<?php echo esc_js( __( 'Number of sales', 'woocommerce' ) ) ?>", data: d }, { label: "<?php echo esc_js( __( 'Sales amount', 'woocommerce' ) ) ?>", data: d2, yaxis: 2 } ], {
  721. series: {
  722. lines: { show: true, fill: true },
  723. points: { show: true, align: "left" }
  724. },
  725. grid: {
  726. show: true,
  727. aboveData: false,
  728. color: '#aaa',
  729. backgroundColor: '#fff',
  730. borderWidth: 2,
  731. borderColor: '#aaa',
  732. clickable: false,
  733. hoverable: true
  734. },
  735. xaxis: {
  736. mode: "time",
  737. timeformat: "%b %y",
  738. monthNames: <?php echo json_encode( array_values( $wp_locale->month_abbrev ) ) ?>,
  739. tickLength: 1,
  740. minTickSize: [1, "month"]
  741. },
  742. yaxes: [ { min: 0, tickSize: 10, tickDecimals: 0 }, { position: "right", min: 0, tickDecimals: 2 } ],
  743. colors: ["#8a4b75", "#47a03e"]
  744. });
  745. placeholder.resize();
  746. <?php woocommerce_tooltip_js(); ?>
  747. });
  748. </script>
  749. <?php
  750. }
  751. /**
  752. * Output the top sellers chart.
  753. *
  754. * @access public
  755. * @return void
  756. */
  757. function woocommerce_top_sellers() {
  758. global $start_date, $end_date, $woocommerce, $wpdb;
  759. $start_date = isset( $_POST['start_date'] ) ? $_POST['start_date'] : '';
  760. $end_date = isset( $_POST['end_date'] ) ? $_POST['end_date'] : '';
  761. if ( ! $start_date )
  762. $start_date = date( 'Ymd', strtotime( date( 'Ym', current_time( 'timestamp' ) ) . '01' ) );
  763. if ( ! $end_date )
  764. $end_date = date( 'Ymd', current_time( 'timestamp' ) );
  765. $start_date = strtotime( $start_date );
  766. $end_date = strtotime( $end_date );
  767. // Get order ids and dates in range
  768. $order_items = $wpdb->get_results( $wpdb->prepare( "
  769. SELECT order_item_meta_2.meta_value as product_id, SUM( order_item_meta.meta_value ) as item_quantity FROM {$wpdb->prefix}woocommerce_order_items as order_items
  770. LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id
  771. LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta_2 ON order_items.order_item_id = order_item_meta_2.order_item_id
  772. LEFT JOIN {$wpdb->posts} AS posts ON order_items.order_id = posts.ID
  773. LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID = rel.object_ID
  774. LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
  775. LEFT JOIN {$wpdb->terms} AS term USING( term_id )
  776. WHERE posts.post_type = 'shop_order'
  777. AND posts.post_status = 'publish'
  778. AND tax.taxonomy = 'shop_order_status'
  779. AND term.slug IN ('" . implode( "','", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "')
  780. AND post_date > '" . date('Y-m-d', $start_date ) . "'
  781. AND post_date < '" . date('Y-m-d', strtotime('+1 day', $end_date ) ) . "'
  782. AND order_items.order_item_type = 'line_item'
  783. AND order_item_meta.meta_key = '_qty'
  784. AND order_item_meta_2.meta_key = '_product_id'
  785. GROUP BY order_item_meta_2.meta_value
  786. " ) );
  787. $found_products = array();
  788. if ( $order_items ) {
  789. foreach ( $order_items as $order_item ) {
  790. $found_products[ $order_item->product_id ] = $order_item->item_quantity;
  791. }
  792. }
  793. asort( $found_products );
  794. $found_products = array_reverse( $found_products, true );
  795. $found_products = array_slice( $found_products, 0, 25, true );
  796. reset( $found_products );
  797. ?>
  798. <form method="post" action="">
  799. <p><label for="from"><?php _e( 'From:', 'woocommerce' ); ?></label> <input type="text" name="start_date" id="from" readonly="readonly" value="<?php echo esc_attr( date('Y-m-d', $start_date) ); ?>" /> <label for="to"><?php _e( 'To:', 'woocommerce' ); ?></label> <input type="text" name="end_date" id="to" readonly="readonly" value="<?php echo esc_attr( date('Y-m-d', $end_date) ); ?>" /> <input type="submit" class="button" value="<?php _e( 'Show', 'woocommerce' ); ?>" /></p>
  800. </form>
  801. <table class="bar_chart">
  802. <thead>
  803. <tr>
  804. <th><?php _e( 'Product', 'woocommerce' ); ?></th>
  805. <th><?php _e( 'Sales', 'woocommerce' ); ?></th>
  806. </tr>
  807. </thead>
  808. <tbody>
  809. <?php
  810. $max_sales = current( $found_products );
  811. foreach ( $found_products as $product_id => $sales ) {
  812. $width = $sales > 0 ? ( $sales / $max_sales ) * 100 : 0;
  813. $product_title = get_the_title( $product_id );
  814. if ( $product_title ) {
  815. $product_name = '<a href="' . get_permalink( $product_id ) . '">'. __( $product_title ) .'</a>';
  816. $orders_link = admin_url( 'edit.php?s&post_status=all&post_type=shop_order&action=-1&s=' . urlencode( $product_title ) . '&shop_order_status=completed,processing,on-hold' );
  817. } else {
  818. $product_name = __( 'Product does not exist', 'woocommerce' );
  819. $orders_link = admin_url( 'edit.php?s&post_status=all&post_type=shop_order&action=-1&s=&shop_order_status=completed,processing,on-hold' );
  820. }
  821. echo '<tr><th>' . $product_name . '</th><td width="1%"><span>' . esc_html( $sales ) . '</span></td><td class="bars"><a href="' . esc_url( $orders_link ) . '" style="width:' . esc_attr( $width ) . '%">&nbsp;</a></td></tr>';
  822. }
  823. ?>
  824. </tbody>
  825. </table>
  826. <script type="text/javascript">
  827. jQuery(function(){
  828. <?php woocommerce_datepicker_js(); ?>
  829. });
  830. </script>
  831. <?php
  832. }
  833. /**
  834. * Output the top earners chart.
  835. *
  836. * @access public
  837. * @return void
  838. */
  839. function woocommerce_top_earners() {
  840. global $start_date, $end_date, $woocommerce, $wpdb;
  841. $start_date = isset( $_POST['start_date'] ) ? $_POST['start_date'] : '';
  842. $end_date = isset( $_POST['end_date'] ) ? $_POST['end_date'] : '';
  843. if ( ! $start_date )
  844. $start_date = date( 'Ymd', strtotime( date('Ym', current_time( 'timestamp' ) ) . '01' ) );
  845. if ( ! $end_date )
  846. $end_date = date( 'Ymd', current_time( 'timestamp' ) );
  847. $start_date = strtotime( $start_date );
  848. $end_date = strtotime( $end_date );
  849. // Get order ids and dates in range
  850. $order_items = $wpdb->get_results( $wpdb->prepare( "
  851. SELECT order_item_meta_2.meta_value as product_id, SUM( order_item_meta.meta_value ) as line_total FROM {$wpdb->prefix}woocommerce_order_items as order_items
  852. LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id
  853. LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta_2 ON order_items.order_item_id = order_item_meta_2.order_item_id
  854. LEFT JOIN {$wpdb->posts} AS posts ON order_items.order_id = posts.ID
  855. LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID = rel.object_ID
  856. LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
  857. LEFT JOIN {$wpdb->terms} AS term USING( term_id )
  858. WHERE posts.post_type = 'shop_order'
  859. AND posts.post_status = 'publish'
  860. AND tax.taxonomy = 'shop_order_status'
  861. AND term.slug IN ('" . implode( "','", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "')
  862. AND post_date > '" . date('Y-m-d', $start_date ) . "'
  863. AND post_date < '" . date('Y-m-d', strtotime('+1 day', $end_date ) ) . "'
  864. AND order_items.order_item_type = 'line_item'
  865. AND order_item_meta.meta_key = '_line_total'
  866. AND order_item_meta_2.meta_key = '_product_id'
  867. GROUP BY order_item_meta_2.meta_value
  868. " ) );
  869. $found_products = array();
  870. if ( $order_items ) {
  871. foreach ( $order_items as $order_item ) {
  872. $found_products[ $order_item->product_id ] = $order_item->line_total;
  873. }
  874. }
  875. asort( $found_products );
  876. $found_products = array_reverse( $found_products, true );
  877. $found_products = array_slice( $found_products, 0, 25, true );
  878. reset( $found_products );
  879. ?>
  880. <form method="post" action="">
  881. <p><label for="from"><?php _e( 'From:', 'woocommerce' ); ?></label> <input type="text" name="start_date" id="from" readonly="readonly" value="<?php echo esc_attr( date('Y-m-d', $start_date) ); ?>" /> <label for="to"><?php _e( 'To:', 'woocommerce' ); ?></label> <input type="text" name="end_date" id="to" readonly="readonly" value="<?php echo esc_attr( date('Y-m-d', $end_date) ); ?>" /> <input type="submit" class="button" value="<?php _e( 'Show', 'woocommerce' ); ?>" /></p>
  882. </form>
  883. <table class="bar_chart">
  884. <thead>
  885. <tr>
  886. <th><?php _e( 'Product', 'woocommerce' ); ?></th>
  887. <th colspan="2"><?php _e( 'Sales', 'woocommerce' ); ?></th>
  888. </tr>
  889. </thead>
  890. <tbody>
  891. <?php
  892. $max_sales = current( $found_products );
  893. foreach ( $found_products as $product_id => $sales ) {
  894. $width = $sales > 0 ? ( round( $sales ) / round( $max_sales ) ) * 100 : 0;
  895. $product_title = get_the_title( $product_id );
  896. if ( $product_title ) {
  897. $product_name = '<a href="'.get_permalink( $product_id ).'">'. __( $product_title ) .'</a>';
  898. $orders_link = admin_url( 'edit.php?s&post_status=all&post_type=shop_order&action=-1&s=' . urlencode( $product_title ) . '&shop_order_status=completed,processing,on-hold' );
  899. } else {
  900. $product_name = __( 'Product no longer exists', 'woocommerce' );
  901. $orders_link = admin_url( 'edit.php?s&post_status=all&post_type=shop_order&action=-1&s=&shop_order_status=completed,processing,on-hold' );
  902. }
  903. echo '<tr><th>' . $product_name . '</th><td width="1%"><span>' . woocommerce_price( $sales ) . '</span></td><td class="bars"><a href="' . esc_url( $orders_link ) . '" style="width:' . esc_attr( $width ) . '%">&nbsp;</a></td></tr>';
  904. }
  905. ?>
  906. </tbody>
  907. </table>
  908. <script type="text/javascript">
  909. jQuery(function(){
  910. <?php woocommerce_datepicker_js(); ?>
  911. });
  912. </script>
  913. <?php
  914. }
  915. /**
  916. * Output the product sales chart for single products.
  917. *
  918. * @access public
  919. * @return void
  920. */
  921. function woocommerce_product_sales() {
  922. global $wpdb, $woocommerce;
  923. $chosen_product_ids = ( isset( $_POST['product_ids'] ) ) ? array_map( 'absint', (array) $_POST['product_ids'] ) : '';
  924. if ( $chosen_product_ids && is_array( $chosen_product_ids ) ) {
  925. $start_date = date( 'Ym', strtotime( '-12 MONTHS', current_time('timestamp') ) ) . '01';
  926. $end_date = date( 'Ymd', current_time( 'timestamp' ) );
  927. $max_sales = $max_totals = 0;
  928. $product_sales = $product_totals = array();
  929. // Get titles and ID's related to product
  930. $chosen_product_titles = array();
  931. $children_ids = array();
  932. foreach ( $chosen_product_ids as $product_id ) {
  933. $children = (array) get_posts( 'post_parent=' . $product_id . '&fields=ids&post_status=any&numberposts=-1' );
  934. $children_ids = $children_ids + $children;
  935. $chosen_product_titles[] = get_the_title( $product_id );
  936. }
  937. // Get order items
  938. $order_items = $wpdb->get_results( $wpdb->prepare( "
  939. SELECT order_item_meta_2.meta_value as product_id, posts.post_date, SUM( order_item_meta.meta_value ) as item_quantity, SUM( order_item_meta_3.meta_value ) as line_total
  940. FROM {$wpdb->prefix}woocommerce_order_items as order_items
  941. LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id
  942. LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta_2 ON order_items.order_item_id = order_item_meta_2.order_item_id
  943. LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta_3 ON order_items.order_item_id = order_item_meta_3.order_item_id
  944. LEFT JOIN {$wpdb->posts} AS posts ON order_items.order_id = posts.ID
  945. LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID = rel.object_ID
  946. LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
  947. LEFT JOIN {$wpdb->terms} AS term USING( term_id )
  948. WHERE posts.post_type = 'shop_order'
  949. AND order_item_meta_2.meta_value IN ('" . implode( "','", array_merge( $chosen_product_ids, $children_ids ) ) . "')
  950. AND posts.post_status = 'publish'
  951. AND tax.taxonomy = 'shop_order_status'
  952. AND term.slug IN ('" . implode( "','", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "')
  953. AND order_items.order_item_type = 'line_item'
  954. AND order_item_meta.meta_key = '_qty'
  955. AND order_item_meta_2.meta_key = '_product_id'
  956. AND order_item_meta_3.meta_key = '_line_total'
  957. GROUP BY order_items.order_id
  958. ORDER BY posts.post_date ASC
  959. " ) );
  960. $found_products = array();
  961. if ( $order_items ) {
  962. foreach ( $order_items as $order_item ) {
  963. if ( $order_item->line_total == 0 && $order_item->item_quantity == 0 )
  964. continue;
  965. // Get date
  966. $date = date( 'Ym', strtotime( $order_item->post_date ) );
  967. // Set values
  968. $product_sales[ $date ] = isset( $product_sales[ $date ] ) ? $product_sales[ $date ] + $order_item->item_quantity : $order_item->item_quantity;
  969. $product_totals[ $date ] = isset( $product_totals[ $date ] ) ? $product_totals[ $date ] + $order_item->line_total : $order_item->line_total;
  970. if ( $product_sales[ $date ] > $max_sales )
  971. $max_sales = $product_sales[ $date ];
  972. if ( $product_totals[ $date ] > $max_totals )
  973. $max_totals = $product_totals[ $date ];
  974. }
  975. }
  976. ?>
  977. <h4><?php printf( __( 'Sales for %s:', 'woocommerce' ), implode( ', ', $chosen_product_titles ) ); ?></h4>
  978. <table class="bar_chart">
  979. <thead>
  980. <tr>
  981. <th><?php _e( 'Month', 'woocommerce' ); ?></th>
  982. <th colspan="2"><?php _e( 'Sales', 'woocommerce' ); ?></th>
  983. </tr>
  984. </thead>
  985. <tbody>
  986. <?php
  987. if ( sizeof( $product_sales ) > 0 ) {
  988. foreach ( $product_sales as $date => $sales ) {
  989. $width = ($sales>0) ? (round($sales) / round($max_sales)) * 100 : 0;
  990. $width2 = ($product_totals[$date]>0) ? (round($product_totals[$date]) / round($max_totals)) * 100 : 0;
  991. $orders_link = admin_url( 'edit.php?s&post_status=all&post_type=shop_order&action=-1&s=' . urlencode( implode( ' ', $chosen_product_titles ) ) . '&m=' . date( 'Ym', strtotime( $date . '01' ) ) . '&shop_order_status=completed,processing,on-hold' );
  992. echo '<tr><th><a href="' . esc_url( $orders_link ) . '">' . date_i18n( 'F', strtotime( $date . '01' ) ) . '</a></th>
  993. <td width="1%"><span>' . esc_html( $sales ) . '</span><span class="alt">' . woocommerce_price( $product_totals[ $date ] ) . '</span></td>
  994. <td class="bars">
  995. <span style="width:' . esc_attr( $width ) . '%">&nbsp;</span>
  996. <span class="alt" style="width:' . esc_attr( $width2 ) . '%">&nbsp;</span>
  997. </td></tr>';
  998. }
  999. } else {
  1000. echo '<tr><td colspan="3">' . __( 'No sales :(', 'woocommerce' ) . '</td></tr>';
  1001. }
  1002. ?>
  1003. </tbody>
  1004. </table>
  1005. <?php
  1006. } else {
  1007. ?>
  1008. <form method="post" action="">
  1009. <p><select id="product_ids" name="product_ids[]" class="ajax_chosen_select_products" multiple="multiple" data-placeholder="<?php _e( 'Search for a product&hellip;', 'woocommerce' ); ?>" style="width: 400px;"></select> <input type="submit" style="vertical-align: top;" class="button" value="<?php _e( 'Show', 'woocommerce' ); ?>" /></p>
  1010. <script type="text/javascript">
  1011. jQuery(function(){
  1012. // Ajax Chosen Product Selectors
  1013. jQuery("select.ajax_chosen_select_products").ajaxChosen({
  1014. method: 'GET',
  1015. url: '<?php echo admin_url('admin-ajax.php'); ?>',
  1016. dataType: 'json',
  1017. afterTypeDelay: 100,
  1018. data: {
  1019. action: 'woocommerce_json_search_products',
  1020. security: '<?php echo wp_create_nonce("search-products"); ?>'
  1021. }
  1022. }, function (data) {
  1023. var terms = {};
  1024. jQuery.each(data, function (i, val) {
  1025. terms[i] = val;
  1026. });
  1027. return terms;
  1028. });
  1029. });
  1030. </script>
  1031. </form>
  1032. <?php
  1033. }
  1034. }
  1035. /**
  1036. * Output the customer overview stats.
  1037. *
  1038. * @access public
  1039. * @return void
  1040. */
  1041. function woocommerce_customer_overview() {
  1042. global $start_date, $end_date, $woocommerce, $wpdb, $wp_locale;
  1043. $total_customers = 0;
  1044. $total_customer_sales = 0;
  1045. $total_guest_sales = 0;
  1046. $total_customer_orders = 0;
  1047. $total_guest_orders = 0;
  1048. $users_query = new WP_User_Query( array(
  1049. 'fields' => array('user_registered'),
  1050. 'role' => 'customer'
  1051. ) );
  1052. $customers = $users_query->get_results();
  1053. $total_customers = (int) sizeof($customers);
  1054. $customer_orders = $wpdb->get_row( $wpdb->prepare( "
  1055. SELECT SUM(meta.meta_value) AS total_sales, COUNT(posts.ID) AS total_orders FROM {$wpdb->posts} AS posts
  1056. LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id
  1057. LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID
  1058. LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
  1059. LEFT JOIN {$wpdb->terms} AS term USING( term_id )
  1060. WHERE meta.meta_key = '_order_total'
  1061. AND posts.post_type = 'shop_order'
  1062. AND posts.post_status = 'publish'
  1063. AND tax.taxonomy = 'shop_order_status'
  1064. AND term.slug IN ('" . implode( "','", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "')
  1065. AND posts.ID IN (
  1066. SELECT post_id FROM {$wpdb->postmeta}
  1067. WHERE meta_key = '_customer_user'
  1068. AND meta_value > 0
  1069. )
  1070. " ) );
  1071. $total_customer_sales = $customer_orders->total_sales;
  1072. $total_customer_orders = absint( $customer_orders->total_orders );
  1073. $guest_orders = $wpdb->get_row( $wpdb->prepare( "
  1074. SELECT SUM(meta.meta_value) AS total_sales, COUNT(posts.ID) AS total_orders FROM {$wpdb->posts} AS posts
  1075. LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id
  1076. LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID
  1077. LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
  1078. LEFT JOIN {$wpdb->terms} AS term USING( term_id )
  1079. WHERE meta.meta_key = '_order_total'
  1080. AND posts.post_type = 'shop_order'
  1081. AND posts.post_status = 'publish'
  1082. AND tax.taxonomy = 'shop_order_status'
  1083. AND term.slug IN ('" . implode( "','", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "')
  1084. AND posts.ID IN (
  1085. SELECT post_id FROM {$wpdb->postmeta}
  1086. WHERE meta_key = '_customer_user'
  1087. AND meta_value = 0
  1088. )
  1089. " ) );
  1090. $total_guest_sales = $guest_orders->total_sales;
  1091. $total_guest_orders = absint( $guest_orders->total_orders );
  1092. ?>
  1093. <div id="poststuff" class="woocommerce-reports-wrap">
  1094. <div class="woocommerce-reports-sidebar">
  1095. <div class="postbox">
  1096. <h3><span><?php _e( 'Total customers', 'woocommerce' ); ?></span></h3>
  1097. <div class="inside">
  1098. <p class="stat"><?php if ($total_customers>0) echo $total_customers; else _e( 'n/a', 'woocommerce' ); ?></p>
  1099. </div>
  1100. </div>
  1101. <div class="postbox">
  1102. <h3><span><?php _e( 'Total customer sales', 'woocommerce' ); ?></span></h3>
  1103. <div class="inside">
  1104. <p class="stat"><?php if ($total_customer_sales>0) echo woocommerce_price($total_customer_sales); else _e( 'n/a', 'woocommerce' ); ?></p>
  1105. </div>
  1106. </div>
  1107. <div class="postbox">
  1108. <h3><span><?php _e( 'Total guest sales', 'woocommerce' ); ?></span></h3>
  1109. <div class="inside">
  1110. <p class="stat"><?php if ($total_guest_sales>0) echo woocommerce_price($total_guest_sales); else _e( 'n/a', 'woocommerce' ); ?></p>
  1111. </div>
  1112. </div>
  1113. <div class="postbox">
  1114. <h3><span><?php _e( 'Total customer orders', 'woocommerce' ); ?></span></h3>
  1115. <div class="inside">
  1116. <p class="stat"><?php if ($total_customer_orders>0) echo $total_customer_orders; else _e( 'n/a', 'woocommerce' ); ?></p>
  1117. </div>
  1118. </div>
  1119. <div class="postbox">
  1120. <h3><span><?php _e( 'Total guest orders', 'woocommerce' ); ?></span></h3>
  1121. <div class="inside">
  1122. <p class="stat"><?php if ($total_guest_orders>0) echo $total_guest_orders; else _e( 'n/a', 'woocommerce' ); ?></p>
  1123. </div>
  1124. </div>
  1125. <div class="postbox">
  1126. <h3><span><?php _e( 'Average orders per customer', 'woocommerce' ); ?></span></h3>
  1127. <div class="inside">
  1128. <p class="stat"><?php if ($total_customer_orders>0 && $total_customers>0) echo number_format($total_customer_orders/$total_customers, 2); else _e( 'n/a', 'woocommerce' ); ?></p>
  1129. </div>
  1130. </div>
  1131. </div>
  1132. <div class="woocommerce-reports-main">
  1133. <div class="postbox">
  1134. <h3><span><?php _e( 'Signups per day', 'woocommerce' ); ?></span></h3>
  1135. <div class="inside chart">
  1136. <div id="placeholder" style="width:100%; overflow:hidden; height:568px; position:relative;"></div>
  1137. </div>
  1138. </div>
  1139. </div>
  1140. </div>
  1141. <?php
  1142. $start_date = strtotime('-30 days', current_time('timestamp'));
  1143. $end_date = current_time('timestamp');
  1144. $signups = array();
  1145. // Blank date ranges to begin
  1146. $count = 0;
  1147. $days = ($end_date - $start_date) / (60 * 60 * 24);
  1148. if ($days==0) $days = 1;
  1149. while ($count < $days) :
  1150. $time = strtotime(date('Ymd', strtotime('+ '.$count.' DAY', $start_date))).'000';
  1151. $signups[ $time ] = 0;
  1152. $count++;
  1153. endwhile;
  1154. foreach ($customers as $customer) :
  1155. if (strtotime($customer->user_registered) > $start_date) :
  1156. $time = strtotime(date('Ymd', strtotime($customer->user_registered))).'000';
  1157. if (isset($signups[ $time ])) :
  1158. $signups[ $time ]++;
  1159. else :
  1160. $signups[ $time ] = 1;
  1161. endif;
  1162. endif;
  1163. endforeach;
  1164. $signups_array = array();
  1165. foreach ($signups as $key => $count) :
  1166. $signups_array[] = array( esc_js( $key ), esc_js( $count ) );
  1167. endforeach;
  1168. $chart_data = json_encode($signups_array);
  1169. ?>
  1170. <script type="text/javascript">
  1171. jQuery(function(){
  1172. var d = jQuery.parseJSON( '<?php echo $chart_data; ?>' );
  1173. for (var i = 0; i < d.length; ++i) d[i][0] += 60 * 60 * 1000;
  1174. var placeholder = jQuery("#placeholder");
  1175. var plot = jQuery.plot(placeholder, [ { data: d } ], {
  1176. series: {
  1177. bars: {
  1178. barWidth: 60 * 60 * 24 * 1000,
  1179. align: "center",
  1180. show: true
  1181. }
  1182. },
  1183. grid: {
  1184. show: true,
  1185. aboveData: false,
  1186. color: '#aaa',
  1187. backgroundColor: '#fff',
  1188. borderWidth: 2,
  1189. borderColor: '#aaa',
  1190. clickable: false,
  1191. hoverable: true,
  1192. markings: weekendAreas
  1193. },
  1194. xaxis: {
  1195. mode: "time",
  1196. timeformat: "%d %b",
  1197. monthNames: <?php echo json_encode( array_values( $wp_locale->month_abbrev ) ) ?>,
  1198. tickLength: 1,
  1199. minTickSize: [1, "day"]
  1200. },
  1201. yaxes: [ { position: "right", min: 0, tickSize: 1, tickDecimals: 0 } ],
  1202. colors: ["#8a4b75"]
  1203. });
  1204. placeholder.resize();
  1205. <?php woocommerce_weekend_area_js(); ?>
  1206. });
  1207. </script>
  1208. <?php
  1209. }
  1210. /**
  1211. * Output the stock overview stats.
  1212. *
  1213. * @access public
  1214. * @return void
  1215. */
  1216. function woocommerce_stock_overview() {
  1217. global $start_date, $end_date, $woocommerce, $wpdb;
  1218. // Low/No stock lists
  1219. $lowstockamount = get_option('woocommerce_notify_low_stock_amount');
  1220. if (!is_numeric($lowstockamount)) $lowstockamount = 1;
  1221. $nostockamount = get_option('woocommerce_notify_no_stock_amount');
  1222. if (!is_numeric($nostockamount)) $nostockamount = 0;
  1223. $outofstock = array();
  1224. $lowinstock = array();
  1225. // Get low in stock simple/downloadable/virtual products. Grouped don't have stock. Variations need a separate query.
  1226. $args = array(
  1227. 'post_type' => 'product',
  1228. 'post_status' => 'publish',
  1229. 'posts_per_page' => -1,
  1230. 'meta_query' => array(
  1231. array(
  1232. 'key' => '_manage_stock',
  1233. 'value' => 'yes'
  1234. ),
  1235. array(
  1236. 'key' => '_stock',
  1237. 'value' => $lowstockamount,
  1238. 'compare' => '<=',
  1239. 'type' => 'NUMERIC'
  1240. )
  1241. ),
  1242. 'tax_query' => array(
  1243. array(
  1244. 'taxonomy' => 'product_type',
  1245. 'field' => 'slug',
  1246. 'terms' => array('simple'),
  1247. 'operator' => 'IN'
  1248. )
  1249. )
  1250. );
  1251. $low_stock_products = (array) get_posts($args);
  1252. // Get low stock product variations
  1253. $args = array(
  1254. 'post_type' => 'product_variation',
  1255. 'post_status' => 'publish',
  1256. 'posts_per_page' => -1,
  1257. 'meta_query' => array(
  1258. array(
  1259. 'key' => '_stock',
  1260. 'value' => $lowstockamount,
  1261. 'compare' => '<=',
  1262. 'type' => 'NUMERIC'
  1263. ),
  1264. array(
  1265. 'key' => '_stock',
  1266. 'value' => array('', false, null),
  1267. 'compare' => 'NOT IN'
  1268. )
  1269. )
  1270. );
  1271. $low_stock_variations = (array) get_posts($args);
  1272. // Finally, get low stock variable products (where stock is set for the parent)
  1273. $args = array(
  1274. 'post_type' => array('product'),
  1275. 'post_status' => 'publish',
  1276. 'posts_per_page' => -1,
  1277. 'meta_query' => array(
  1278. 'relation' => 'AND',
  1279. array(
  1280. 'key' => '_manage_stock',
  1281. 'value' => 'yes'
  1282. ),
  1283. array(
  1284. 'key' => '_stock',
  1285. 'value' => $lowstockamount,
  1286. 'compare' => '<=',
  1287. 'type' => 'NUMERIC'
  1288. )
  1289. ),
  1290. 'tax_query' => array(
  1291. array(
  1292. 'taxonomy' => 'product_type',
  1293. 'field' => 'slug',
  1294. 'terms' => array('variable'),
  1295. 'operator' => 'IN'
  1296. )
  1297. )
  1298. );
  1299. $low_stock_variable_products = (array) get_posts($args);
  1300. // Merge results
  1301. $low_in_stock = array_merge($low_stock_products, $low_stock_variations, $low_stock_variable_products);
  1302. ?>
  1303. <div id="poststuff" class="woocommerce-reports-wrap halved">
  1304. <div class="woocommerce-reports-left">
  1305. <div class="postbox">
  1306. <h3><span><?php _e( 'Low stock', 'woocommerce' ); ?></span></h3>
  1307. <div class="inside">
  1308. <?php
  1309. if ( $low_in_stock ) {
  1310. echo '<ul class="stock_list">';
  1311. foreach ( $low_in_stock as $product ) {
  1312. $stock = (int) get_post_meta( $product->ID, '_stock', true );
  1313. $sku = get_post_meta( $product->ID, '_sku', true );
  1314. if ( $stock <= $nostockamount ) continue;
  1315. $title = esc_html__( $product->post_title );
  1316. if ( $sku )
  1317. $title .= ' (' . __( 'SKU', 'woocommerce' ) . ': ' . esc_html( $sku ) . ')';
  1318. if ( $product->post_type=='product' )
  1319. $product_url = admin_url( 'post.php?post=' . $product->ID . '&action=edit' );
  1320. else
  1321. $product_url = admin_url( 'post.php?post=' . $product->post_parent . '&action=edit' );
  1322. printf( '<li><a href="%s"><small>' . _n('%d in stock', '%d in stock', $stock, 'woocommerce') . '</small> %s</a></li>', $product_url, $stock, $title );
  1323. }
  1324. echo '</ul>';
  1325. } else {
  1326. echo '<p>'.__( 'No products are low in stock.', 'woocommerce' ).'</p>';
  1327. }
  1328. ?>
  1329. </div>
  1330. </div>
  1331. </div>
  1332. <div class="woocommerce-reports-right">
  1333. <div class="postbox">
  1334. <h3><span><?php _e( 'Out of stock', 'woocommerce' ); ?></span></h3>
  1335. <div class="inside">
  1336. <?php
  1337. if ( $low_in_stock ) {
  1338. echo '<ul class="stock_list">';
  1339. foreach ( $low_in_stock as $product ) {
  1340. $stock = (int) get_post_meta( $product->ID, '_stock', true );
  1341. $sku = get_post_meta( $product->ID, '_sku', true );
  1342. if ( $stock > $nostockamount ) continue;
  1343. $title = esc_html__( $product->post_title );
  1344. if ( $sku )
  1345. $title .= ' (' . __( 'SKU', 'woocommerce' ) . ': ' . esc_html( $sku ) . ')';
  1346. if ( $product->post_type=='product' )
  1347. $product_url = admin_url( 'post.php?post=' . $product->ID . '&action=edit' );
  1348. else
  1349. $product_url = admin_url( 'post.php?post=' . $product->post_parent . '&action=edit' );
  1350. printf( '<li><a href="%s"><small>' . _n('%d in stock', '%d in stock', $stock, 'woocommerce') . '</small> %s</a></li>', $product_url, $stock, $title );
  1351. }
  1352. echo '</ul>';
  1353. } else {
  1354. echo '<p>'.__( 'No products are out in stock.', 'woocommerce' ).'</p>';
  1355. }
  1356. ?>
  1357. </div>
  1358. </div>
  1359. </div>
  1360. </div>
  1361. <?php
  1362. }
  1363. /**
  1364. * Output the monthly tax stats.
  1365. *
  1366. * @access public
  1367. * @return void
  1368. */
  1369. function woocommerce_monthly_taxes() {
  1370. global $start_date, $end_date, $woocommerce, $wpdb;
  1371. $first_year = $wpdb->get_var( $wpdb->prepare( "SELECT post_date FROM $wpdb->posts WHERE post_date != 0 ORDER BY post_date ASC LIMIT 1;" ) );
  1372. if ( $first_year )
  1373. $first_year = date( 'Y', strtotime( $first_year ) );
  1374. else
  1375. $first_year = date( 'Y' );
  1376. $current_year = isset( $_POST['show_year'] ) ? $_POST['show_year'] : date( 'Y', current_time( 'timestamp' ) );
  1377. $start_date = strtotime( $current_year . '0101' );
  1378. $total_tax = $total_sales_tax = $total_shipping_tax = $count = 0;
  1379. $taxes = $tax_rows = $tax_row_labels = array();
  1380. for ( $count = 0; $count < 12; $count++ ) {
  1381. $time = strtotime( date('Ym', strtotime( '+ ' . $count . ' MONTH', $start_date ) ) . '01' );
  1382. if ( $time > current_time( 'timestamp' ) )
  1383. continue;
  1384. $month = date( 'Ym', strtotime( date( 'Ym', strtotime( '+ ' . $count . ' MONTH', $start_date ) ) . '01' ) );
  1385. $gross = $wpdb->get_var( $wpdb->prepare( "
  1386. SELECT SUM( meta.meta_value ) AS order_tax
  1387. FROM {$wpdb->posts} AS posts
  1388. LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id
  1389. LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID
  1390. LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
  1391. LEFT JOIN {$wpdb->terms} AS term USING( term_id )
  1392. WHERE meta.meta_key = '_order_total'
  1393. AND posts.post_type = 'shop_order'
  1394. AND posts.post_status = 'publish'
  1395. AND tax.taxonomy = 'shop_order_status'
  1396. AND term.slug IN ('" . implode( "','", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "')
  1397. AND '{$month}' = date_format(posts.post_date,'%%Y%%m')
  1398. " ) );
  1399. $shipping = $wpdb->get_var( $wpdb->prepare( "
  1400. SELECT SUM( meta.meta_value ) AS order_tax
  1401. FROM {$wpdb->posts} AS posts
  1402. LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id
  1403. LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID
  1404. LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
  1405. LEFT JOIN {$wpdb->terms} AS term USING( term_id )
  1406. WHERE meta.meta_key = '_order_shipping'
  1407. AND posts.post_type = 'shop_order'
  1408. AND posts.post_status = 'publish'
  1409. AND tax.taxonomy = 'shop_order_status'
  1410. AND term.slug IN ('" . implode( "','", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "')
  1411. AND '{$month}' = date_format(posts.post_date,'%%Y%%m')
  1412. " ) );
  1413. $order_tax = $wpdb->get_var( $wpdb->prepare( "
  1414. SELECT SUM( meta.meta_value ) AS order_tax
  1415. FROM {$wpdb->posts} AS posts
  1416. LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id
  1417. LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID
  1418. LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
  1419. LEFT JOIN {$wpdb->terms} AS term USING( term_id )
  1420. WHERE meta.meta_key = '_order_tax'
  1421. AND posts.post_type = 'shop_order'
  1422. AND posts.post_status = 'publish'
  1423. AND tax.taxonomy = 'shop_order_status'
  1424. AND term.slug IN ('" . implode( "','", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "')
  1425. AND '{$month}' = date_format(posts.post_date,'%%Y%%m')
  1426. " ) );
  1427. $shipping_tax = $wpdb->get_var( $wpdb->prepare( "
  1428. SELECT SUM( meta.meta_value ) AS order_tax
  1429. FROM {$wpdb->posts} AS posts
  1430. LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id
  1431. LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID
  1432. LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
  1433. LEFT JOIN {$wpdb->terms} AS term USING( term_id )
  1434. WHERE meta.meta_key = '_order_shipping_tax'
  1435. AND posts.post_type = 'shop_order'
  1436. AND posts.post_status = 'publish'
  1437. AND tax.taxonomy = 'shop_order_status'
  1438. AND term.slug IN ('" . implode( "','", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "')
  1439. AND '{$month}' = date_format(posts.post_date,'%%Y%%m')
  1440. " ) );
  1441. $tax_rows = $wpdb->get_results( $wpdb->prepare( "
  1442. SELECT
  1443. order_items.order_item_name as name,
  1444. SUM( order_item_meta.meta_value ) as tax_amount,
  1445. SUM( order_item_meta_2.meta_value ) as shipping_tax_amount,
  1446. SUM( order_item_meta.meta_value + order_item_meta_2.meta_value ) as total_tax_amount
  1447. FROM {$wpdb->prefix}woocommerce_order_items as order_items
  1448. LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id
  1449. LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta_2 ON order_items.order_item_id = order_item_meta_2.order_item_id
  1450. LEFT JOIN {$wpdb->posts} AS posts ON order_items.order_id = posts.ID
  1451. LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID = rel.object_ID
  1452. LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
  1453. LEFT JOIN {$wpdb->terms} AS term USING( term_id )
  1454. WHERE order_items.order_item_type = 'tax'
  1455. AND posts.post_type = 'shop_order'
  1456. AND posts.post_status = 'publish'
  1457. AND tax.taxonomy = 'shop_order_status'
  1458. AND term.slug IN ('" . implode( "','", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "')
  1459. AND '{$month}' = date_format( posts.post_date,'%%Y%%m' )
  1460. AND order_item_meta.meta_key = 'tax_amount'
  1461. AND order_item_meta_2.meta_key = 'shipping_tax_amount'
  1462. GROUP BY order_items.order_item_name
  1463. " ) );
  1464. if ( $tax_rows ) {
  1465. foreach ( $tax_rows as $tax_row ) {
  1466. $tax_row_labels[] = $tax_row->name;
  1467. }
  1468. }
  1469. $taxes[ date( 'M', strtotime( $month . '01' ) ) ] = array(
  1470. 'gross' => $gross,
  1471. 'shipping' => $shipping,
  1472. 'order_tax' => $order_tax,
  1473. 'shipping_tax' => $shipping_tax,
  1474. 'total_tax' => $shipping_tax + $order_tax,
  1475. 'tax_rows' => $tax_rows
  1476. );
  1477. $total_sales_tax += $order_tax;
  1478. $total_shipping_tax += $shipping_tax;
  1479. }
  1480. $total_tax = $total_sales_tax + $total_shipping_tax;
  1481. ?>
  1482. <form method="post" action="">
  1483. <p><label for="show_year"><?php _e( 'Year:', 'woocommerce' ); ?></label>
  1484. <select name="show_year" id="show_year">
  1485. <?php
  1486. for ( $i = $first_year; $i <= date('Y'); $i++ )
  1487. printf( '<option value="%s" %s>%s</option>', $i, selected( $current_year, $i, false ), $i );
  1488. ?>
  1489. </select> <input type="submit" class="button" value="<?php _e( 'Show', 'woocommerce' ); ?>" /></p>
  1490. </form>
  1491. <div id="poststuff" class="woocommerce-reports-wrap">
  1492. <div class="woocommerce-reports-sidebar">
  1493. <div class="postbox">
  1494. <h3><span><?php _e( 'Total taxes for year', 'woocommerce' ); ?></span></h3>
  1495. <div class="inside">
  1496. <p class="stat"><?php
  1497. if ( $total_tax > 0 )
  1498. echo woocommerce_price( $total_tax );
  1499. else
  1500. _e( 'n/a', 'woocommerce' );
  1501. ?></p>
  1502. </div>
  1503. </div>
  1504. <div class="postbox">
  1505. <h3><span><?php _e( 'Total product taxes for year', 'woocommerce' ); ?></span></h3>
  1506. <div class="inside">
  1507. <p class="stat"><?php
  1508. if ( $total_sales_tax > 0 )
  1509. echo woocommerce_price( $total_sales_tax );
  1510. else
  1511. _e( 'n/a', 'woocommerce' );
  1512. ?></p>
  1513. </div>
  1514. </div>
  1515. <div class="postbox">
  1516. <h3><span><?php _e( 'Total shipping tax for year', 'woocommerce' ); ?></span></h3>
  1517. <div class="inside">
  1518. <p class="stat"><?php
  1519. if ( $total_shipping_tax > 0 )
  1520. echo woocommerce_price( $total_shipping_tax );
  1521. else
  1522. _e( 'n/a', 'woocommerce' );
  1523. ?></p>
  1524. </div>
  1525. </div>
  1526. </div>
  1527. <div class="woocommerce-reports-main">
  1528. <table class="widefat">
  1529. <thead>
  1530. <tr>
  1531. <th><?php _e( 'Month', 'woocommerce' ); ?></th>
  1532. <th class="total_row"><?php _e( 'Total Sales', 'woocommerce' ); ?> <a class="tips" data-tip="<?php _e("This is the sum of the 'Order Total' field within your orders.", 'woocommerce'); ?>" href="#">[?]</a></th>
  1533. <th class="total_row"><?php _e( 'Total Shipping', 'woocommerce' ); ?> <a class="tips" data-tip="<?php _e("This is the sum of the 'Shipping Total' field within your orders.", 'woocommerce'); ?>" href="#">[?]</a></th>
  1534. <th class="total_row"><?php _e( 'Total Product Taxes', 'woocommerce' ); ?> <a class="tips" data-tip="<?php _e("This is the sum of the 'Cart Tax' field within your orders.", 'woocommerce'); ?>" href="#">[?]</a></th>
  1535. <th class="total_row"><?php _e( 'Total Shipping Taxes', 'woocommerce' ); ?> <a class="tips" data-tip="<?php _e("This is the sum of the 'Shipping Tax' field within your orders.", 'woocommerce'); ?>" href="#">[?]</a></th>
  1536. <th class="total_row"><?php _e( 'Total Taxes', 'woocommerce' ); ?> <a class="tips" data-tip="<?php _e("This is the sum of the 'Cart Tax' and 'Shipping Tax' fields within your orders.", 'woocommerce'); ?>" href="#">[?]</a></th>
  1537. <th class="total_row"><?php _e( 'Net profit', 'woocommerce' ); ?> <a class="tips" data-tip="<?php _e("Total sales minus shipping and tax.", 'woocommerce'); ?>" href="#">[?]</a></th>
  1538. <?php
  1539. $tax_row_labels = array_filter( array_unique( $tax_row_labels ) );
  1540. foreach ( $tax_row_labels as $label )
  1541. echo '<th class="tax_row">' . $label . '</th>';
  1542. ?>
  1543. </tr>
  1544. </thead>
  1545. <tfoot>
  1546. <tr>
  1547. <?php
  1548. $total = array();
  1549. foreach ( $taxes as $month => $tax ) {
  1550. $total['gross'] = isset( $total['gross'] ) ? $total['gross'] + $tax['gross'] : $tax['gross'];
  1551. $total['shipping'] = isset( $total['shipping'] ) ? $total['shipping'] + $tax['shipping'] : $tax['shipping'];
  1552. $total['order_tax'] = isset( $total['order_tax'] ) ? $total['order_tax'] + $tax['order_tax'] : $tax['order_tax'];
  1553. $total['shipping_tax'] = isset( $total['shipping_tax'] ) ? $total['shipping_tax'] + $tax['shipping_tax'] : $tax['shipping_tax'];
  1554. $total['total_tax'] = isset( $total['total_tax'] ) ? $total['total_tax'] + $tax['total_tax'] : $tax['total_tax'];
  1555. foreach ( $tax_row_labels as $label )
  1556. foreach ( $tax['tax_rows'] as $tax_row )
  1557. if ( $tax_row->name == $label ) {
  1558. $total['tax_rows'][ $label ] = isset( $total['tax_rows'][ $label ] ) ? $total['tax_rows'][ $label ] + $tax_row->total_tax_amount : $tax_row->total_tax_amount;
  1559. }
  1560. }
  1561. echo '
  1562. <td>' . __( 'Total', 'woocommerce' ) . '</td>
  1563. <td class="total_row">' . woocommerce_price( $total['gross'] ) . '</td>
  1564. <td class="total_row">' . woocommerce_price( $total['shipping'] ) . '</td>
  1565. <td class="total_row">' . woocommerce_price( $total['order_tax'] ) . '</td>
  1566. <td class="total_row">' . woocommerce_price( $total['shipping_tax'] ) . '</td>
  1567. <td class="total_row">' . woocommerce_price( $total['total_tax'] ) . '</td>
  1568. <td class="total_row">' . woocommerce_price( $total['gross'] - $total['shipping'] - $total['total_tax'] ) . '</td>';
  1569. foreach ( $tax_row_labels as $label )
  1570. if ( isset( $total['tax_rows'][ $label ] ) )
  1571. echo '<td class="tax_row">' . woocommerce_price( $total['tax_rows'][ $label ] ) . '</td>';
  1572. else
  1573. echo '<td class="tax_row">' . woocommerce_price( 0 ) . '</td>';
  1574. ?>
  1575. </tr>
  1576. <tr>
  1577. <th colspan="<?php echo 7 + sizeof( $tax_row_labels ); ?>"><button class="button toggle_tax_rows"><?php _e( 'Toggle tax rows', 'woocommerce' ); ?></button></th>
  1578. </tr>
  1579. </tfoot>
  1580. <tbody>
  1581. <?php
  1582. foreach ( $taxes as $month => $tax ) {
  1583. $alt = ( isset( $alt ) && $alt == 'alt' ) ? '' : 'alt';
  1584. echo '<tr class="' . $alt . '">
  1585. <td>' . $month . '</td>
  1586. <td class="total_row">' . woocommerce_price( $tax['gross'] ) . '</td>
  1587. <td class="total_row">' . woocommerce_price( $tax['shipping'] ) . '</td>
  1588. <td class="total_row">' . woocommerce_price( $tax['order_tax'] ) . '</td>
  1589. <td class="total_row">' . woocommerce_price( $tax['shipping_tax'] ) . '</td>
  1590. <td class="total_row">' . woocommerce_price( $tax['total_tax'] ) . '</td>
  1591. <td class="total_row">' . woocommerce_price( $tax['gross'] - $tax['shipping'] - $tax['total_tax'] ) . '</td>';
  1592. foreach ( $tax_row_labels as $label ) {
  1593. $row_total = 0;
  1594. foreach ( $tax['tax_rows'] as $tax_row ) {
  1595. if ( $tax_row->name == $label ) {
  1596. $row_total = $tax_row->total_tax_amount;
  1597. }
  1598. }
  1599. echo '<td class="tax_row">' . woocommerce_price( $row_total ) . '</td>';
  1600. }
  1601. echo '</tr>';
  1602. }
  1603. ?>
  1604. </tbody>
  1605. </table>
  1606. <script type="text/javascript">
  1607. jQuery('.toggle_tax_rows').click(function(){
  1608. jQuery('.tax_row').toggle();
  1609. jQuery('.total_row').toggle();
  1610. });
  1611. jQuery('.tax_row').hide();
  1612. </script>
  1613. </div>
  1614. </div>
  1615. <?php
  1616. }
  1617. /**
  1618. * woocommerce_category_sales function.
  1619. *
  1620. * @access public
  1621. * @return void
  1622. */
  1623. function woocommerce_category_sales() {
  1624. global $start_date, $end_date, $woocommerce, $wpdb, $wp_locale;
  1625. $first_year = $wpdb->get_var( $wpdb->prepare( "SELECT post_date FROM $wpdb->posts WHERE post_date != 0 ORDER BY post_date ASC LIMIT 1;" ) );
  1626. $first_year = ( $first_year ) ? date( 'Y', strtotime( $first_year ) ) : date( 'Y' );
  1627. $current_year = isset( $_POST['show_year'] ) ? $_POST['show_year'] : date( 'Y', current_time( 'timestamp' ) );
  1628. $start_date = strtotime( $current_year . '0101' );
  1629. $categories = get_terms( 'product_cat', array( 'parent' => 0 ) );
  1630. ?>
  1631. <form method="post" action="" class="report_filters">
  1632. <p>
  1633. <label for="show_year"><?php _e( 'Show:', 'woocommerce' ); ?></label>
  1634. <select name="show_year" id="show_year">
  1635. <?php
  1636. for ( $i = $first_year; $i <= date( 'Y' ); $i++ )
  1637. printf( '<option value="%s" %s>%s</option>', $i, selected( $current_year, $i, false ), $i );
  1638. ?>
  1639. </select>
  1640. <select multiple="multiple" class="chosen_select" id="show_categories" name="show_categories[]" style="width: 300px;">
  1641. <?php
  1642. foreach ( $categories as $category ) {
  1643. if ( $category->parent > 0 )
  1644. $prepend = '&mdash; ';
  1645. else
  1646. $prepend = '';
  1647. echo '<option value="' . $category->term_id . '" ' . selected( ! empty( $_POST['show_categories'] ) && in_array( $category->term_id, $_POST['show_categories'] ), true ) . '>' . $prepend . $category->name . '</option>';
  1648. }
  1649. ?>
  1650. </select>
  1651. <input type="submit" class="button" value="<?php _e( 'Show', 'woocommerce' ); ?>" />
  1652. </p>
  1653. </form>
  1654. <?php
  1655. $item_sales = array();
  1656. // Get order items
  1657. $start_date = date( 'Ym', strtotime( date( 'Ym', strtotime( '-1 year', $start_date ) ) . '01' ) );
  1658. $order_items = $wpdb->get_results( $wpdb->prepare( "
  1659. SELECT order_item_meta_2.meta_value as product_id, posts.post_date, SUM( order_item_meta.meta_value ) as line_total
  1660. FROM {$wpdb->prefix}woocommerce_order_items as order_items
  1661. LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id
  1662. LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta_2 ON order_items.order_item_id = order_item_meta_2.order_item_id
  1663. LEFT JOIN {$wpdb->posts} AS posts ON order_items.order_id = posts.ID
  1664. LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID = rel.object_ID
  1665. LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
  1666. LEFT JOIN {$wpdb->terms} AS term USING( term_id )
  1667. WHERE posts.post_type = 'shop_order'
  1668. AND posts.post_status = 'publish'
  1669. AND tax.taxonomy = 'shop_order_status'
  1670. AND term.slug IN ('" . implode( "','", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "')
  1671. AND date_format(posts.post_date,'%%Y%%m') >= '{$start_date}'
  1672. AND order_items.order_item_type = 'line_item'
  1673. AND order_item_meta.meta_key = '_line_total'
  1674. AND order_item_meta_2.meta_key = '_product_id'
  1675. GROUP BY order_items.order_id
  1676. ORDER BY posts.post_date ASC
  1677. " ) );
  1678. if ( $order_items ) {
  1679. foreach ( $order_items as $order_item ) {
  1680. $month = date( 'm', strtotime( $order_item->post_date ) ) - 1;
  1681. $item_sales[ $month ][ $order_item->product_id ] = isset( $item_sales[ $month ][ $order_item->product_id ] ) ? $item_sales[ $month ][ $order_item->product_id ] + $order_item->line_total : $order_item->line_total;
  1682. }
  1683. }
  1684. if ( ! empty( $_POST['show_categories'] ) && sizeof( $_POST['show_categories'] ) > 0 ) {
  1685. $show_categories = $include_categories = array_map( 'absint', $_POST['show_categories'] );
  1686. foreach( $show_categories as $cat )
  1687. $include_categories = array_merge( $include_categories, get_term_children( $cat, 'product_cat' ) );
  1688. $categories = get_terms( 'product_cat', array( 'include' => array_unique( $include_categories ) ) );
  1689. ?>
  1690. <div class="woocommerce-wide-reports-wrap">
  1691. <table class="widefat">
  1692. <thead>
  1693. <tr>
  1694. <th><?php _e( 'Category', 'woocommerce' ); ?></th>
  1695. <?php
  1696. $column_count = 0;
  1697. for ( $count = 0; $count < 12; $count++ ) :
  1698. if ( $count >= date ( 'm' ) && $current_year == date( 'Y' ) )
  1699. continue;
  1700. $column_count++;
  1701. ?>
  1702. <th><?php echo date( 'F', strtotime( '2012-' . ( $count + 1 ) . '-01' ) ); ?></th>
  1703. <?php endfor; ?>
  1704. <th><strong><?php _e( 'Total', 'woocommerce' ); ?></strong></th>
  1705. </tr>
  1706. </thead>
  1707. <tbody><?php
  1708. // While outputting, lets store them for the chart
  1709. $chart_data = $month_totals = $category_totals = array();
  1710. $top_cat = $bottom_cat = $top_cat_name = $bottom_cat_name = '';
  1711. for ( $count = 0; $count < 12; $count++ )
  1712. if ( $count >= date( 'm' ) && $current_year == date( 'Y' ) )
  1713. break;
  1714. else
  1715. $month_totals[ $count ] = 0;
  1716. foreach ( $categories as $category ) {
  1717. $cat_total = 0;
  1718. $category_chart_data = $term_ids = array();
  1719. $term_ids = get_term_children( $category->term_id, 'product_cat' );
  1720. $term_ids[] = $category->term_id;
  1721. $product_ids = get_objects_in_term( $term_ids, 'product_cat' );
  1722. if ( $category->parent > 0 )
  1723. $prepend = '&mdash; ';
  1724. else
  1725. $prepend = '';
  1726. $category_sales_html = '<tr><th>' . $prepend . $category->name . '</th>';
  1727. for ( $count = 0; $count < 12; $count++ ) {
  1728. if ( $count >= date( 'm' ) && $current_year == date( 'Y' ) )
  1729. continue;
  1730. if ( ! empty( $item_sales[ $count ] ) ) {
  1731. $matches = array_intersect_key( $item_sales[ $count ], array_flip( $product_ids ) );
  1732. $total = array_sum( $matches );
  1733. $cat_total += $total;
  1734. } else {
  1735. $total = 0;
  1736. }
  1737. $month_totals[ $count ] += $total;
  1738. $category_sales_html .= '<td>' . woocommerce_price( $total ) . '</td>';
  1739. $category_chart_data[] = array( strtotime( date( 'Ymd', strtotime( '2012-' . ( $count + 1 ) . '-01' ) ) ) . '000', $total );
  1740. }
  1741. if ( $cat_total == 0 )
  1742. continue;
  1743. $category_totals[] = $cat_total;
  1744. $category_sales_html .= '<td><strong>' . woocommerce_price( $cat_total ) . '</strong></td>';
  1745. $category_sales_html .= '</tr>';
  1746. echo $category_sales_html;
  1747. $chart_data[ $category->name ] = $category_chart_data;
  1748. if ( $cat_total > $top_cat ) {
  1749. $top_cat = $cat_total;
  1750. $top_cat_name = $category->name;
  1751. }
  1752. if ( $cat_total < $bottom_cat || $bottom_cat === '' ) {
  1753. $bottom_cat = $cat_total;
  1754. $bottom_cat_name = $category->name;
  1755. }
  1756. }
  1757. sort( $category_totals );
  1758. echo '<tr><th><strong>' . __( 'Total', 'woocommerce' ) . '</strong></th>';
  1759. for ( $count = 0; $count < 12; $count++ )
  1760. if ( $count >= date( 'm' ) && $current_year == date( 'Y' ) )
  1761. break;
  1762. else
  1763. echo '<td><strong>' . woocommerce_price( $month_totals[ $count ] ) . '</strong></td>';
  1764. echo '<td><strong>' . woocommerce_price( array_sum( $month_totals ) ) . '</strong></td></tr>';
  1765. ?></tbody>
  1766. </table>
  1767. </div>
  1768. <div id="poststuff" class="woocommerce-reports-wrap">
  1769. <div class="woocommerce-reports-sidebar">
  1770. <div class="postbox">
  1771. <h3><span><?php _e( 'Top category', 'woocommerce' ); ?></span></h3>
  1772. <div class="inside">
  1773. <p class="stat"><?php
  1774. echo $top_cat_name . ' (' . woocommerce_price( $top_cat ) . ')';
  1775. ?></p>
  1776. </div>
  1777. </div>
  1778. <?php if ( sizeof( $category_totals ) > 1 ) : ?>
  1779. <div class="postbox">
  1780. <h3><span><?php _e( 'Worst category', 'woocommerce' ); ?></span></h3>
  1781. <div class="inside">
  1782. <p class="stat"><?php
  1783. echo $bottom_cat_name . ' (' . woocommerce_price( $bottom_cat ) . ')';
  1784. ?></p>
  1785. </div>
  1786. </div>
  1787. <div class="postbox">
  1788. <h3><span><?php _e( 'Category sales average', 'woocommerce' ); ?></span></h3>
  1789. <div class="inside">
  1790. <p class="stat"><?php
  1791. if ( sizeof( $category_totals ) > 0 )
  1792. echo woocommerce_price( array_sum( $category_totals ) / sizeof( $category_totals ) );
  1793. else
  1794. echo __( 'N/A', 'woocommerce' );
  1795. ?></p>
  1796. </div>
  1797. </div>
  1798. <div class="postbox">
  1799. <h3><span><?php _e( 'Category sales median', 'woocommerce' ); ?></span></h3>
  1800. <div class="inside">
  1801. <p class="stat"><?php
  1802. if ( sizeof( $category_totals ) == 0 )
  1803. echo __( 'N/A', 'woocommerce' );
  1804. elseif ( sizeof( $category_totals ) % 2 )
  1805. echo woocommerce_price(
  1806. (
  1807. $category_totals[ floor( sizeof( $category_totals ) / 2 ) ] + $category_totals[ ceil( sizeof( $category_totals ) / 2 ) ]
  1808. ) / 2
  1809. );
  1810. else
  1811. echo woocommerce_price( $category_totals[ sizeof( $category_totals ) / 2 ] );
  1812. ?></p>
  1813. </div>
  1814. </div>
  1815. <?php endif; ?>
  1816. </div>
  1817. <div class="woocommerce-reports-main">
  1818. <div class="postbox">
  1819. <h3><span><?php _e( 'Monthly sales by category', 'woocommerce' ); ?></span></h3>
  1820. <div class="inside chart">
  1821. <div id="placeholder" style="width:100%; overflow:hidden; height:568px; position:relative;"></div>
  1822. </div>
  1823. </div>
  1824. </div>
  1825. </div>
  1826. <script type="text/javascript">
  1827. jQuery(function(){
  1828. <?php
  1829. // Variables
  1830. foreach ( $chart_data as $name => $data ) {
  1831. $varname = 'cat_' . str_replace( '-', '_', sanitize_title( $name ) ) . '_data';
  1832. echo 'var ' . $varname . ' = jQuery.parseJSON( \'' . json_encode( $data ) . '\' );';
  1833. }
  1834. ?>
  1835. var placeholder = jQuery("#placeholder");
  1836. var plot = jQuery.plot(placeholder, [
  1837. <?php
  1838. $labels = array();
  1839. foreach ( $chart_data as $name => $data ) {
  1840. $labels[] = '{ label: "' . esc_js( $name ) . '", data: ' . 'cat_' . str_replace( '-', '_', sanitize_title( $name ) ) . '_data }';
  1841. }
  1842. echo implode( ',', $labels );
  1843. ?>
  1844. ], {
  1845. series: {
  1846. lines: { show: true, fill: true },
  1847. points: { show: true, align: "left" }
  1848. },
  1849. grid: {
  1850. show: true,
  1851. aboveData: false,
  1852. color: '#aaa',
  1853. backgroundColor: '#fff',
  1854. borderWidth: 2,
  1855. borderColor: '#aaa',
  1856. clickable: false,
  1857. hoverable: true
  1858. },
  1859. xaxis: {
  1860. mode: "time",
  1861. timeformat: "%b %y",
  1862. monthNames: <?php echo json_encode( array_values( $wp_locale->month_abbrev ) ) ?>,
  1863. tickLength: 1,
  1864. minTickSize: [1, "month"]
  1865. },
  1866. yaxes: [ { min: 0, tickDecimals: 2 } ]
  1867. });
  1868. placeholder.resize();
  1869. <?php woocommerce_tooltip_js(); ?>
  1870. });
  1871. </script>
  1872. <?php
  1873. }
  1874. ?>
  1875. <script type="text/javascript">
  1876. jQuery(function(){
  1877. jQuery("select.chosen_select").chosen();
  1878. });
  1879. </script>
  1880. <?php
  1881. }
  1882. /**
  1883. * woocommerce_coupon_sales function.
  1884. *
  1885. * @access public
  1886. * @return void
  1887. */
  1888. function woocommerce_coupon_sales() {
  1889. global $start_date, $end_date, $woocommerce, $wpdb, $wp_locale;
  1890. $first_year = $wpdb->get_var( $wpdb->prepare( "SELECT post_date FROM $wpdb->posts WHERE post_date != 0 AND post_type='shop_order' ORDER BY post_date ASC LIMIT 1;" ) );
  1891. $first_year = ( $first_year ) ? date( 'Y', strtotime( $first_year ) ) : date( 'Y' );
  1892. $current_year = isset( $_POST['show_year'] ) ? $_POST['show_year'] : date( 'Y', current_time( 'timestamp' ) );
  1893. $start_date = strtotime( $current_year . '0101' );
  1894. $order_statuses = implode( "','", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) );
  1895. $coupons = $wpdb->get_col( $wpdb->prepare( "
  1896. SELECT DISTINCT meta.meta_value FROM {$wpdb->postmeta} AS meta
  1897. LEFT JOIN {$wpdb->posts} AS posts ON posts.ID = meta.post_id
  1898. LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID
  1899. LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
  1900. LEFT JOIN {$wpdb->terms} AS term USING( term_id )
  1901. WHERE meta.meta_key = 'coupons'
  1902. AND posts.post_type = 'shop_order'
  1903. AND posts.post_status = 'publish'
  1904. AND tax.taxonomy = 'shop_order_status'
  1905. AND term.slug IN ('{$order_statuses}')
  1906. " ) );
  1907. ?>
  1908. <form method="post" action="" class="report_filters">
  1909. <p>
  1910. <label for="show_year"><?php _e( 'Show:', 'woocommerce' ); ?></label>
  1911. <select name="show_year" id="show_year">
  1912. <?php
  1913. for ( $i = $first_year; $i <= date( 'Y' ); $i++ )
  1914. printf( '<option value="%s" %s>%s</option>', $i, selected( $current_year, $i, false ), $i );
  1915. ?>
  1916. </select>
  1917. <select multiple="multiple" class="chosen_select" id="show_coupons" name="show_coupons[]" style="width: 300px;">
  1918. <?php
  1919. foreach ( $coupons as $coupon ) {
  1920. echo '<option value="' . $coupon . '" ' . selected( ! empty( $_POST['show_coupons'] ) && in_array( $coupon, $_POST['show_coupons'] ), true ) . '>' . $coupon . '</option>';
  1921. }
  1922. ?>
  1923. </select>
  1924. <input type="submit" class="button" value="<?php _e( 'Show', 'woocommerce' ); ?>" />
  1925. </p>
  1926. </form>
  1927. <?php
  1928. if ( ! empty( $_POST['show_coupons'] ) && count( $_POST['show_coupons'] ) > 0 ) :
  1929. $coupons = $_POST['show_coupons'];
  1930. $coupon_sales = $monthly_totals = array();
  1931. foreach( $coupons as $coupon ) :
  1932. $monthly_sales = $wpdb->get_results( $wpdb->prepare( "
  1933. SELECT SUM(postmeta.meta_value) AS order_total, date_format(posts.post_date, '%%Y%%m') as month FROM {$wpdb->posts} AS posts
  1934. INNER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID=postmeta.post_ID
  1935. INNER JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID
  1936. INNER JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
  1937. INNER JOIN {$wpdb->terms} AS term USING( term_id )
  1938. WHERE postmeta.meta_key = '_order_total'
  1939. AND tax.taxonomy = 'shop_order_status'
  1940. AND term.slug IN ('{$order_statuses}')
  1941. AND posts.post_type = 'shop_order'
  1942. AND posts.post_status = 'publish'
  1943. AND '{$current_year}' = date_format(posts.post_date,'%%Y')
  1944. AND posts.ID IN (
  1945. SELECT post_id FROM {$wpdb->postmeta} AS meta
  1946. WHERE meta.meta_key = 'coupons'
  1947. AND meta.meta_value = '%s'
  1948. )
  1949. GROUP BY month", $coupon ), OBJECT );
  1950. foreach( $monthly_sales as $sales ) {
  1951. $month = $sales->month;
  1952. $coupon_sales[$coupon][$month] = $sales->order_total;
  1953. }
  1954. endforeach;
  1955. ?>
  1956. <div class="woocommerce-wide-reports-wrap">
  1957. <table class="widefat">
  1958. <thead>
  1959. <tr>
  1960. <th><?php _e( 'Coupon', 'woocommerce' ); ?></th>
  1961. <?php
  1962. $column_count = 0;
  1963. for ( $count = 0; $count < 12; $count++ ) :
  1964. if ( $count >= date ( 'm' ) && $current_year == date( 'Y' ) )
  1965. continue;
  1966. $month = date( 'Ym', strtotime( date( 'Ym', strtotime( '+ '. $count . ' MONTH', $start_date ) ) . '01' ) );
  1967. // set elements before += them below
  1968. $monthly_totals[$month] = 0;
  1969. $column_count++;
  1970. ?>
  1971. <th><?php echo date( 'F', strtotime( '2012-' . ( $count + 1 ) . '-01' ) ); ?></th>
  1972. <?php endfor; ?>
  1973. <th><strong><?php _e( 'Total', 'woocommerce' ); ?></strong></th>
  1974. </tr>
  1975. </thead>
  1976. <tbody><?php
  1977. // save data for chart while outputting
  1978. $chart_data = $coupon_totals = array();
  1979. foreach( $coupon_sales as $coupon_code => $sales ) {
  1980. echo '<tr><th>' . esc_html( $coupon_code ) . '</th>';
  1981. for ( $count = 0; $count < 12; $count ++ ) {
  1982. if ( $count >= date ( 'm' ) && $current_year == date( 'Y' ) )
  1983. continue;
  1984. $month = date( 'Ym', strtotime( date( 'Ym', strtotime( '+ '. $count . ' MONTH', $start_date ) ) . '01' ) );
  1985. $amount = isset( $sales[$month] ) ? $sales[$month] : 0;
  1986. echo '<td>' . woocommerce_price( $amount ) . '</td>';
  1987. $monthly_totals[$month] += $amount;
  1988. $chart_data[$coupon_code][] = array( strtotime( date( 'Ymd', strtotime( $month . '01' ) ) ) . '000', $amount );
  1989. }
  1990. echo '<td><strong>' . woocommerce_price( array_sum( $sales ) ) . '</strong></td>';
  1991. // total sales across all months
  1992. $coupon_totals[$coupon_code] = array_sum( $sales );
  1993. echo '</tr>';
  1994. }
  1995. $top_coupon_name = current( array_keys( $coupon_totals, max( $coupon_totals ) ) );
  1996. $top_coupon_sales = $coupon_totals[$top_coupon_name];
  1997. $worst_coupon_name = current( array_keys( $coupon_totals, min( $coupon_totals ) ) );
  1998. $worst_coupon_sales = $coupon_totals[$worst_coupon_name];
  1999. $median_coupon_sales = array_values( $coupon_totals );
  2000. sort($median_coupon_sales);
  2001. echo '<tr><th><strong>' . __( 'Total', 'woocommerce' ) . '</strong></th>';
  2002. foreach( $monthly_totals as $month => $totals )
  2003. echo '<td><strong>' . woocommerce_price( $totals ) . '</strong></td>';
  2004. echo '<td><strong>' . woocommerce_price( array_sum( $monthly_totals ) ) . '</strong></td></tr>';
  2005. ?></tbody>
  2006. </table>
  2007. </div>
  2008. <div id="poststuff" class="woocommerce-reports-wrap">
  2009. <div class="woocommerce-reports-sidebar">
  2010. <div class="postbox">
  2011. <h3><span><?php _e( 'Top coupon', 'woocommerce' ); ?></span></h3>
  2012. <div class="inside">
  2013. <p class="stat"><?php
  2014. echo $top_coupon_name . ' (' . woocommerce_price( $top_coupon_sales ) . ')';
  2015. ?></p>
  2016. </div>
  2017. </div>
  2018. <?php if ( sizeof( $coupon_totals ) > 1 ) : ?>
  2019. <div class="postbox">
  2020. <h3><span><?php _e( 'Worst coupon', 'woocommerce' ); ?></span></h3>
  2021. <div class="inside">
  2022. <p class="stat"><?php
  2023. echo $worst_coupon_name . ' (' . woocommerce_price( $worst_coupon_sales ) . ')';
  2024. ?></p>
  2025. </div>
  2026. </div>
  2027. <div class="postbox">
  2028. <h3><span><?php _e( 'Coupon sales average', 'woocommerce' ); ?></span></h3>
  2029. <div class="inside">
  2030. <p class="stat"><?php
  2031. echo woocommerce_price( array_sum( $coupon_totals ) / count( $coupon_totals ) );
  2032. ?></p>
  2033. </div>
  2034. </div>
  2035. <div class="postbox">
  2036. <h3><span><?php _e( 'Coupon sales median', 'woocommerce' ); ?></span></h3>
  2037. <div class="inside">
  2038. <p class="stat"><?php
  2039. if ( count( $median_coupon_sales ) == 2 )
  2040. echo __( 'N/A', 'woocommerce' );
  2041. elseif ( count( $median_coupon_sales ) % 2 )
  2042. echo woocommerce_price(
  2043. (
  2044. $median_coupon_sales[ floor( count( $median_coupon_sales ) / 2 ) ] + $median_coupon_sales[ ceil( count( $median_coupon_sales ) / 2 ) ]
  2045. ) / 2
  2046. );
  2047. else
  2048. echo woocommerce_price( $median_coupon_sales[ count( $median_coupon_sales ) / 2 ] );
  2049. ?></p>
  2050. </div>
  2051. </div>
  2052. <?php endif; ?>
  2053. </div>
  2054. <div class="woocommerce-reports-main">
  2055. <div class="postbox">
  2056. <h3><span><?php _e( 'Monthly sales by coupon', 'woocommerce' ); ?></span></h3>
  2057. <div class="inside chart">
  2058. <div id="placeholder" style="width:100%; overflow:hidden; height:568px; position:relative;"></div>
  2059. </div>
  2060. </div>
  2061. </div>
  2062. </div>
  2063. <script type="text/javascript">
  2064. jQuery(function(){
  2065. <?php
  2066. // Variables
  2067. foreach ( $chart_data as $name => $data ) {
  2068. $varname = 'coupon_' . str_replace( '-', '_', sanitize_title( $name ) ) . '_data';
  2069. echo 'var ' . $varname . ' = jQuery.parseJSON( \'' . json_encode( $data ) . '\' );';
  2070. }
  2071. ?>
  2072. var placeholder = jQuery("#placeholder");
  2073. var plot = jQuery.plot(placeholder, [
  2074. <?php
  2075. $labels = array();
  2076. foreach ( $chart_data as $name => $data ) {
  2077. $labels[] = '{ label: "' . esc_js( $name ) . '", data: ' . 'coupon_' . str_replace( '-', '_', sanitize_title( $name ) ) . '_data }';
  2078. }
  2079. echo implode( ',', $labels );
  2080. ?>
  2081. ], {
  2082. series: {
  2083. lines: { show: true, fill: true },
  2084. points: { show: true, align: "left" }
  2085. },
  2086. grid: {
  2087. show: true,
  2088. aboveData: false,
  2089. color: '#aaa',
  2090. backgroundColor: '#fff',
  2091. borderWidth: 2,
  2092. borderColor: '#aaa',
  2093. clickable: false,
  2094. hoverable: true
  2095. },
  2096. xaxis: {
  2097. mode: "time",
  2098. timeformat: "%b %y",
  2099. monthNames: <?php echo json_encode( array_values( $wp_locale->month_abbrev ) ) ?>,
  2100. tickLength: 1,
  2101. minTickSize: [1, "month"]
  2102. },
  2103. yaxes: [ { min: 0, tickDecimals: 2 } ]
  2104. });
  2105. placeholder.resize();
  2106. <?php woocommerce_tooltip_js(); ?>
  2107. });
  2108. </script>
  2109. <?php
  2110. endif; // end POST check
  2111. ?>
  2112. <script type="text/javascript">
  2113. jQuery(function(){
  2114. jQuery("select.chosen_select").chosen();
  2115. });
  2116. </script>
  2117. <?php
  2118. }