class-wc-report-coupon-usage.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. <?php
  2. /**
  3. * Coupon usage report functionality
  4. *
  5. * @package WooCommerce/Admin/Reports
  6. */
  7. if ( ! defined( 'ABSPATH' ) ) {
  8. exit; // Exit if accessed directly.
  9. }
  10. /**
  11. * WC_Report_Coupon_Usage
  12. *
  13. * @author WooThemes
  14. * @category Admin
  15. * @package WooCommerce/Admin/Reports
  16. * @version 2.1.0
  17. */
  18. class WC_Report_Coupon_Usage extends WC_Admin_Report {
  19. /**
  20. * Chart colors.
  21. *
  22. * @var array
  23. */
  24. public $chart_colours = array();
  25. /**
  26. * Coupon codes.
  27. *
  28. * @var array
  29. */
  30. public $coupon_codes = array();
  31. /**
  32. * Constructor.
  33. */
  34. public function __construct() {
  35. if ( isset( $_GET['coupon_codes'] ) && is_array( $_GET['coupon_codes'] ) ) {
  36. $this->coupon_codes = array_filter( array_map( 'sanitize_text_field', wp_unslash( $_GET['coupon_codes'] ) ) );
  37. } elseif ( isset( $_GET['coupon_codes'] ) ) {
  38. $this->coupon_codes = array_filter( array( sanitize_text_field( wp_unslash( $_GET['coupon_codes'] ) ) ) );
  39. }
  40. }
  41. /**
  42. * Get the legend for the main chart sidebar.
  43. *
  44. * @return array
  45. */
  46. public function get_chart_legend() {
  47. $legend = array();
  48. $total_discount_query = array(
  49. 'data' => array(
  50. 'discount_amount' => array(
  51. 'type' => 'order_item_meta',
  52. 'order_item_type' => 'coupon',
  53. 'function' => 'SUM',
  54. 'name' => 'discount_amount',
  55. ),
  56. ),
  57. 'where' => array(
  58. array(
  59. 'key' => 'order_item_type',
  60. 'value' => 'coupon',
  61. 'operator' => '=',
  62. ),
  63. ),
  64. 'query_type' => 'get_var',
  65. 'filter_range' => true,
  66. 'order_types' => wc_get_order_types( 'order-count' ),
  67. );
  68. $total_coupons_query = array(
  69. 'data' => array(
  70. 'order_item_id' => array(
  71. 'type' => 'order_item',
  72. 'order_item_type' => 'coupon',
  73. 'function' => 'COUNT',
  74. 'name' => 'order_coupon_count',
  75. ),
  76. ),
  77. 'where' => array(
  78. array(
  79. 'key' => 'order_item_type',
  80. 'value' => 'coupon',
  81. 'operator' => '=',
  82. ),
  83. ),
  84. 'query_type' => 'get_var',
  85. 'filter_range' => true,
  86. 'order_types' => wc_get_order_types( 'order-count' ),
  87. );
  88. if ( ! empty( $this->coupon_codes ) ) {
  89. $coupon_code_query = array(
  90. 'type' => 'order_item',
  91. 'key' => 'order_item_name',
  92. 'value' => $this->coupon_codes,
  93. 'operator' => 'IN',
  94. );
  95. $total_discount_query['where'][] = $coupon_code_query;
  96. $total_coupons_query['where'][] = $coupon_code_query;
  97. }
  98. $total_discount = $this->get_order_report_data( $total_discount_query );
  99. $total_coupons = absint( $this->get_order_report_data( $total_coupons_query ) );
  100. $legend[] = array(
  101. /* translators: %s: discount amount */
  102. 'title' => sprintf( __( '%s discounts in total', 'woocommerce' ), '<strong>' . wc_price( $total_discount ) . '</strong>' ),
  103. 'color' => $this->chart_colours['discount_amount'],
  104. 'highlight_series' => 1,
  105. );
  106. $legend[] = array(
  107. /* translators: %s: coupons amount */
  108. 'title' => sprintf( __( '%s coupons used in total', 'woocommerce' ), '<strong>' . $total_coupons . '</strong>' ),
  109. 'color' => $this->chart_colours['coupon_count'],
  110. 'highlight_series' => 0,
  111. );
  112. return $legend;
  113. }
  114. /**
  115. * Output the report.
  116. */
  117. public function output_report() {
  118. $ranges = array(
  119. 'year' => __( 'Year', 'woocommerce' ),
  120. 'last_month' => __( 'Last month', 'woocommerce' ),
  121. 'month' => __( 'This month', 'woocommerce' ),
  122. '7day' => __( 'Last 7 days', 'woocommerce' ),
  123. );
  124. $this->chart_colours = array(
  125. 'discount_amount' => '#3498db',
  126. 'coupon_count' => '#d4d9dc',
  127. );
  128. $current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day';
  129. if ( ! in_array( $current_range, array( 'custom', 'year', 'last_month', 'month', '7day' ) ) ) {
  130. $current_range = '7day';
  131. }
  132. $this->check_current_range_nonce( $current_range );
  133. $this->calculate_current_range( $current_range );
  134. include WC()->plugin_path() . '/includes/admin/views/html-report-by-date.php';
  135. }
  136. /**
  137. * Get chart widgets.
  138. *
  139. * @return array
  140. */
  141. public function get_chart_widgets() {
  142. $widgets = array();
  143. $widgets[] = array(
  144. 'title' => '',
  145. 'callback' => array( $this, 'coupons_widget' ),
  146. );
  147. return $widgets;
  148. }
  149. /**
  150. * Output coupons widget.
  151. */
  152. public function coupons_widget() {
  153. ?>
  154. <h4 class="section_title"><span><?php esc_html_e( 'Filter by coupon', 'woocommerce' ); ?></span></h4>
  155. <div class="section">
  156. <form method="GET">
  157. <div>
  158. <?php
  159. $used_coupons = $this->get_order_report_data(
  160. array(
  161. 'data' => array(
  162. 'order_item_name' => array(
  163. 'type' => 'order_item',
  164. 'order_item_type' => 'coupon',
  165. 'function' => '',
  166. 'distinct' => true,
  167. 'name' => 'order_item_name',
  168. ),
  169. ),
  170. 'where' => array(
  171. array(
  172. 'key' => 'order_item_type',
  173. 'value' => 'coupon',
  174. 'operator' => '=',
  175. ),
  176. ),
  177. 'query_type' => 'get_col',
  178. 'filter_range' => false,
  179. )
  180. );
  181. if ( ! empty( $used_coupons ) && is_array( $used_coupons ) ) :
  182. ?>
  183. <select id="coupon_codes" name="coupon_codes" class="wc-enhanced-select" data-placeholder="<?php esc_attr_e( 'Choose coupons&hellip;', 'woocommerce' ); ?>" style="width:100%;">
  184. <option value=""><?php esc_html_e( 'All coupons', 'woocommerce' ); ?></option>
  185. <?php
  186. foreach ( $used_coupons as $coupon ) {
  187. echo '<option value="' . esc_attr( $coupon ) . '"' . wc_selected( $coupon, $this->coupon_codes ) . '>' . esc_html( $coupon ) . '</option>';
  188. }
  189. ?>
  190. </select>
  191. <?php // @codingStandardsIgnoreStart ?>
  192. <button type="submit" class="submit button" value="<?php esc_attr_e( 'Show', 'woocommerce' ); ?>"><?php esc_html_e( 'Show', 'woocommerce' ); ?></button>
  193. <input type="hidden" name="range" value="<?php echo ( ! empty( $_GET['range'] ) ) ? esc_attr( wp_unslash( $_GET['range'] ) ) : ''; ?>" />
  194. <input type="hidden" name="start_date" value="<?php echo ( ! empty( $_GET['start_date'] ) ) ? esc_attr( wp_unslash( $_GET['start_date'] ) ) : ''; ?>" />
  195. <input type="hidden" name="end_date" value="<?php echo ( ! empty( $_GET['end_date'] ) ) ? esc_attr( wp_unslash( $_GET['end_date'] ) ) : ''; ?>" />
  196. <input type="hidden" name="page" value="<?php echo ( ! empty( $_GET['page'] ) ) ? esc_attr( wp_unslash( $_GET['page'] ) ) : ''; ?>" />
  197. <input type="hidden" name="tab" value="<?php echo ( ! empty( $_GET['tab'] ) ) ? esc_attr( wp_unslash( $_GET['tab'] ) ) : ''; ?>" />
  198. <input type="hidden" name="report" value="<?php echo ( ! empty( $_GET['report'] ) ) ? esc_attr( wp_unslash( $_GET['report'] ) ) : ''; ?>" />
  199. <?php // @codingStandardsIgnoreEnd ?>
  200. <?php else : ?>
  201. <span><?php esc_html_e( 'No used coupons found', 'woocommerce' ); ?></span>
  202. <?php endif; ?>
  203. </div>
  204. </form>
  205. </div>
  206. <h4 class="section_title"><span><?php esc_html_e( 'Most popular', 'woocommerce' ); ?></span></h4>
  207. <div class="section">
  208. <table cellspacing="0">
  209. <?php
  210. $most_popular = $this->get_order_report_data(
  211. array(
  212. 'data' => array(
  213. 'order_item_name' => array(
  214. 'type' => 'order_item',
  215. 'order_item_type' => 'coupon',
  216. 'function' => '',
  217. 'name' => 'coupon_code',
  218. ),
  219. 'order_item_id' => array(
  220. 'type' => 'order_item',
  221. 'order_item_type' => 'coupon',
  222. 'function' => 'COUNT',
  223. 'name' => 'coupon_count',
  224. ),
  225. ),
  226. 'where' => array(
  227. array(
  228. 'type' => 'order_item',
  229. 'key' => 'order_item_type',
  230. 'value' => 'coupon',
  231. 'operator' => '=',
  232. ),
  233. ),
  234. 'order_by' => 'coupon_count DESC',
  235. 'group_by' => 'order_item_name',
  236. 'limit' => 12,
  237. 'query_type' => 'get_results',
  238. 'filter_range' => true,
  239. )
  240. );
  241. if ( ! empty( $most_popular ) && is_array( $most_popular ) ) {
  242. foreach ( $most_popular as $coupon ) {
  243. echo '<tr class="' . ( in_array( $coupon->coupon_code, $this->coupon_codes ) ? 'active' : '' ) . '">
  244. <td class="count" width="1%">' . esc_html( $coupon->coupon_count ) . '</td>
  245. <td class="name"><a href="' . esc_url( add_query_arg( 'coupon_codes', $coupon->coupon_code ) ) . '">' . esc_html( $coupon->coupon_code ) . '</a></td>
  246. </tr>';
  247. }
  248. } else {
  249. echo '<tr><td colspan="2">' . esc_html__( 'No coupons found in range', 'woocommerce' ) . '</td></tr>';
  250. }
  251. ?>
  252. </table>
  253. </div>
  254. <h4 class="section_title"><span><?php esc_html_e( 'Most discount', 'woocommerce' ); ?></span></h4>
  255. <div class="section">
  256. <table cellspacing="0">
  257. <?php
  258. $most_discount = $this->get_order_report_data(
  259. array(
  260. 'data' => array(
  261. 'order_item_name' => array(
  262. 'type' => 'order_item',
  263. 'order_item_type' => 'coupon',
  264. 'function' => '',
  265. 'name' => 'coupon_code',
  266. ),
  267. 'discount_amount' => array(
  268. 'type' => 'order_item_meta',
  269. 'order_item_type' => 'coupon',
  270. 'function' => 'SUM',
  271. 'name' => 'discount_amount',
  272. ),
  273. ),
  274. 'where' => array(
  275. array(
  276. 'type' => 'order_item',
  277. 'key' => 'order_item_type',
  278. 'value' => 'coupon',
  279. 'operator' => '=',
  280. ),
  281. ),
  282. 'order_by' => 'discount_amount DESC',
  283. 'group_by' => 'order_item_name',
  284. 'limit' => 12,
  285. 'query_type' => 'get_results',
  286. 'filter_range' => true,
  287. )
  288. );
  289. if ( ! empty( $most_discount ) && is_array( $most_discount ) ) {
  290. foreach ( $most_discount as $coupon ) {
  291. // @codingStandardsIgnoreStart
  292. echo '<tr class="' . ( in_array( $coupon->coupon_code, $this->coupon_codes ) ? 'active' : '' ) . '">
  293. <td class="count" width="1%">' . wc_price( $coupon->discount_amount ) . '</td>
  294. <td class="name"><a href="' . esc_url( add_query_arg( 'coupon_codes', $coupon->coupon_code ) ) . '">' . esc_html( $coupon->coupon_code ) . '</a></td>
  295. </tr>';
  296. // @codingStandardsIgnoreEnd
  297. }
  298. } else {
  299. echo '<tr><td colspan="3">' . esc_html__( 'No coupons found in range', 'woocommerce' ) . '</td></tr>';
  300. }
  301. ?>
  302. </table>
  303. </div>
  304. <script type="text/javascript">
  305. jQuery('.section_title').click(function(){
  306. var next_section = jQuery(this).next('.section');
  307. if ( jQuery(next_section).is(':visible') )
  308. return false;
  309. jQuery('.section:visible').slideUp();
  310. jQuery('.section_title').removeClass('open');
  311. jQuery(this).addClass('open').next('.section').slideDown();
  312. return false;
  313. });
  314. jQuery('.section').slideUp( 100, function() {
  315. <?php if ( empty( $this->coupon_codes ) ) : ?>
  316. jQuery('.section_title:eq(1)').click();
  317. <?php else : ?>
  318. jQuery('.section_title:eq(0)').click();
  319. <?php endif; ?>
  320. });
  321. </script>
  322. <?php
  323. }
  324. /**
  325. * Output an export link.
  326. */
  327. public function get_export_button() {
  328. $current_range = ! empty( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '7day';
  329. ?>
  330. <a
  331. href="#"
  332. download="report-<?php echo esc_attr( $current_range ); ?>-<?php echo esc_attr( date_i18n( 'Y-m-d', current_time( 'timestamp' ) ) ); ?>.csv"
  333. class="export_csv"
  334. data-export="chart"
  335. data-xaxes="<?php esc_attr_e( 'Date', 'woocommerce' ); ?>"
  336. data-groupby="<?php echo esc_attr( $this->chart_groupby ); ?>"
  337. >
  338. <?php esc_html_e( 'Export CSV', 'woocommerce' ); ?>
  339. </a>
  340. <?php
  341. }
  342. /**
  343. * Get the main chart.
  344. */
  345. public function get_main_chart() {
  346. global $wp_locale;
  347. // Get orders and dates in range - we want the SUM of order totals, COUNT of order items, COUNT of orders, and the date.
  348. $order_coupon_counts_query = array(
  349. 'data' => array(
  350. 'order_item_name' => array(
  351. 'type' => 'order_item',
  352. 'order_item_type' => 'coupon',
  353. 'function' => 'COUNT',
  354. 'name' => 'order_coupon_count',
  355. ),
  356. 'post_date' => array(
  357. 'type' => 'post_data',
  358. 'function' => '',
  359. 'name' => 'post_date',
  360. ),
  361. ),
  362. 'where' => array(
  363. array(
  364. 'key' => 'order_item_type',
  365. 'value' => 'coupon',
  366. 'operator' => '=',
  367. ),
  368. ),
  369. 'group_by' => $this->group_by_query,
  370. 'order_by' => 'post_date ASC',
  371. 'query_type' => 'get_results',
  372. 'filter_range' => true,
  373. 'order_types' => wc_get_order_types( 'order-count' ),
  374. );
  375. $order_discount_amounts_query = array(
  376. 'data' => array(
  377. 'discount_amount' => array(
  378. 'type' => 'order_item_meta',
  379. 'order_item_type' => 'coupon',
  380. 'function' => 'SUM',
  381. 'name' => 'discount_amount',
  382. ),
  383. 'post_date' => array(
  384. 'type' => 'post_data',
  385. 'function' => '',
  386. 'name' => 'post_date',
  387. ),
  388. ),
  389. 'where' => array(
  390. array(
  391. 'key' => 'order_item_type',
  392. 'value' => 'coupon',
  393. 'operator' => '=',
  394. ),
  395. ),
  396. 'group_by' => $this->group_by_query . ', order_item_name',
  397. 'order_by' => 'post_date ASC',
  398. 'query_type' => 'get_results',
  399. 'filter_range' => true,
  400. 'order_types' => wc_get_order_types( 'order-count' ),
  401. );
  402. if ( ! empty( $this->coupon_codes ) ) {
  403. $coupon_code_query = array(
  404. 'type' => 'order_item',
  405. 'key' => 'order_item_name',
  406. 'value' => $this->coupon_codes,
  407. 'operator' => 'IN',
  408. );
  409. $order_coupon_counts_query['where'][] = $coupon_code_query;
  410. $order_discount_amounts_query['where'][] = $coupon_code_query;
  411. }
  412. $order_coupon_counts = $this->get_order_report_data( $order_coupon_counts_query );
  413. $order_discount_amounts = $this->get_order_report_data( $order_discount_amounts_query );
  414. // Prepare data for report.
  415. $order_coupon_counts = $this->prepare_chart_data( $order_coupon_counts, 'post_date', 'order_coupon_count', $this->chart_interval, $this->start_date, $this->chart_groupby );
  416. $order_discount_amounts = $this->prepare_chart_data( $order_discount_amounts, 'post_date', 'discount_amount', $this->chart_interval, $this->start_date, $this->chart_groupby );
  417. // Encode in json format.
  418. $chart_data = json_encode(
  419. array(
  420. 'order_coupon_counts' => array_values( $order_coupon_counts ),
  421. 'order_discount_amounts' => array_values( $order_discount_amounts ),
  422. )
  423. );
  424. ?>
  425. <div class="chart-container">
  426. <div class="chart-placeholder main"></div>
  427. </div>
  428. <script type="text/javascript">
  429. var main_chart;
  430. jQuery(function(){
  431. var order_data = jQuery.parseJSON( '<?php echo $chart_data; ?>' );<?php // @codingStandardsIgnoreLine ?>
  432. var drawGraph = function( highlight ) {
  433. var series = [
  434. {
  435. label: "<?php echo esc_js( __( 'Number of coupons used', 'woocommerce' ) ); ?>",
  436. data: order_data.order_coupon_counts,
  437. color: '<?php echo esc_js( $this->chart_colours['coupon_count'] ); ?>',
  438. bars: { fillColor: '<?php echo esc_js( $this->chart_colours['coupon_count'] ); ?>', fill: true, show: true, lineWidth: 0, barWidth: <?php echo esc_js( $this->barwidth ); ?> * 0.5, align: 'center' },
  439. shadowSize: 0,
  440. hoverable: false
  441. },
  442. {
  443. label: "<?php echo esc_js( __( 'Discount amount', 'woocommerce' ) ); ?>",
  444. data: order_data.order_discount_amounts,
  445. yaxis: 2,
  446. color: '<?php echo esc_js( $this->chart_colours['discount_amount'] ); ?>',
  447. points: { show: true, radius: 5, lineWidth: 3, fillColor: '#fff', fill: true },
  448. lines: { show: true, lineWidth: 4, fill: false },
  449. shadowSize: 0,
  450. <?php echo $this->get_currency_tooltip(); ?><?php // @codingStandardsIgnoreLine ?>
  451. }
  452. ];
  453. if ( highlight !== 'undefined' && series[ highlight ] ) {
  454. highlight_series = series[ highlight ];
  455. highlight_series.color = '#9c5d90';
  456. if ( highlight_series.bars )
  457. highlight_series.bars.fillColor = '#9c5d90';
  458. if ( highlight_series.lines ) {
  459. highlight_series.lines.lineWidth = 5;
  460. }
  461. }
  462. main_chart = jQuery.plot(
  463. jQuery('.chart-placeholder.main'),
  464. series,
  465. {
  466. legend: {
  467. show: false
  468. },
  469. grid: {
  470. color: '#aaa',
  471. borderColor: 'transparent',
  472. borderWidth: 0,
  473. hoverable: true
  474. },
  475. xaxes: [ {
  476. color: '#aaa',
  477. position: "bottom",
  478. tickColor: 'transparent',
  479. mode: "time",
  480. timeformat: "<?php echo ( 'day' === $this->chart_groupby ) ? '%d %b' : '%b'; ?>",
  481. monthNames: <?php echo json_encode( array_values( $wp_locale->month_abbrev ) ); ?>,
  482. tickLength: 1,
  483. minTickSize: [1, "<?php echo esc_js( $this->chart_groupby ); ?>"],
  484. font: {
  485. color: "#aaa"
  486. }
  487. } ],
  488. yaxes: [
  489. {
  490. min: 0,
  491. minTickSize: 1,
  492. tickDecimals: 0,
  493. color: '#ecf0f1',
  494. font: { color: "#aaa" }
  495. },
  496. {
  497. position: "right",
  498. min: 0,
  499. tickDecimals: 2,
  500. alignTicksWithAxis: 1,
  501. color: 'transparent',
  502. font: { color: "#aaa" }
  503. }
  504. ],
  505. }
  506. );
  507. jQuery('.chart-placeholder').resize();
  508. }
  509. drawGraph();
  510. jQuery('.highlight_series').hover(
  511. function() {
  512. drawGraph( jQuery(this).data('series') );
  513. },
  514. function() {
  515. drawGraph();
  516. }
  517. );
  518. });
  519. </script>
  520. <?php
  521. }
  522. }