PageRenderTime 69ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/admin/woocommerce-admin-reports.php

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