Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Bower components Debian packages RPM packages NuGet packages

jsarnowski / jsarnowski/cartflows-pro   php

Repository URL to install this package:

Version: 1.6.10 

/ tracking / class-cartflows-pro-analytics-reports.php

<?php
/**
 * Flow
 *
 * @package cartflows
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * Analytics reports class.
 */
class Cartflows_Pro_Analytics_Reports {

	/**
	 * Member Variable
	 *
	 * @var object instance
	 */
	private static $instance;

	/**
	 * Flow orders
	 *
	 * @var array flow_orders
	 */
	private static $flow_orders = array();

	/**
	 * Flow gross sell
	 *
	 * @var int flow_gross
	 */
	private static $flow_gross = 0;

	/**
	 * Flow visits
	 *
	 * @var array flow_visits
	 */
	private static $flow_visits = array();

	/**
	 * Steps data
	 *
	 * @var array step_data
	 */
	private static $step_data = array();

	/**
	 * Earnings for flow
	 *
	 * @var array flow_earnings
	 */
	private static $flow_earnings = array();

	/**
	 * Report interval
	 *
	 * @var int report_interval
	 */
	private static $report_interval = 30;

	/**
	 *  Initiator
	 */
	public static function get_instance() {
		if ( ! isset( self::$instance ) ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Constructor function that initializes required actions and hooks
	 */
	public function __construct() {

		$this->load_analytics();
	}

	/**
	 *
	 * Analytics
	 *
	 * @return void
	 */
	public function load_analytics() {

		add_action( 'cartflows_add_flow_metabox', array( $this, 'add_analytics_metabox' ) );

		$flow_id = isset( $_GET['post'] ) ? intval( $_GET['post'] ) : 0;

		$analytics = wcf()->options->get_flow_meta_value( $flow_id, 'wcf-enable-analytics' );

		if ( 'no' === $analytics ) {
			return;
		}

		add_action( 'admin_enqueue_scripts', array( $this, 'load_analytics_scripts' ), 20 );
		add_action( 'wp_ajax_cartflows_set_visit_data', array( $this, 'set_visits_data' ) );
		add_action( 'admin_footer', array( $this, 'render_analytics_stat' ) );
		add_action( 'wp_ajax_wcf_reset_flow_analytics', array( $this, 'reset_flow_analytics' ) );
		add_filter( 'cartflows_admin_js_localize', array( $this, 'add_localize_vars_for_analytics' ), 10, 1 );
	}

	/**
	 *
	 * Localize variables for analytics.
	 *
	 * @param array $localize vars.
	 */
	public function add_localize_vars_for_analytics( $localize ) {
		$localize['wcf_reset_analytics_nonce']   = wp_create_nonce( 'wcf_reset_flow_analytics' );
		$localize['confirm_msg_for_analytics']   = __( 'Are you sure you want to reset the analytics for this flow?', 'cartflows-pro' );
		$localize['succesful_msg_for_analytics'] = __( 'Flow analytics data has been reset successfully.', 'cartflows-pro' );

		$flow_id = isset( $_GET['post'] ) ? intval( $_GET['post'] ) : 0;

		$localize['flow_id'] = $flow_id;

		return $localize;
	}

	/**
	 *
	 * Reset ANalytics.
	 *
	 * @return void
	 */
	public function reset_flow_analytics() {

		check_ajax_referer( 'wcf_reset_flow_analytics', 'security' );
		global $wpdb;
		$visit_db       = $wpdb->prefix . CARTFLOWS_PRO_VISITS_TABLE;
		$visits_meta_db = $wpdb->prefix . CARTFLOWS_PRO_VISITS_META_TABLE;
		$flow_id        = isset( $_POST['flow_id'] ) ? intval( $_POST['flow_id'] ) : 0;

		if ( 0 !== $flow_id ) {
			$get_steps = wcf()->flow->get_steps( $flow_id );
			$step_ids  = implode( ',', wp_list_pluck( $get_steps, 'id' ) );

			$wpdb->query( "DELETE FROM {$visit_db} WHERE step_id IN(" . $step_ids . ')' ); //phpcs:ignore

			$reset_date = current_time( 'Y-m-d H:i:s' );

			update_post_meta( $flow_id, 'wcf-analytics-reset-date', $reset_date );

			wp_send_json_success( true );
		}
		wp_send_json_error( false );
	}
	/**
	 *
	 * Add Analytics Metabox
	 *
	 * @return void
	 */
	public function add_analytics_metabox() {
		add_meta_box(
			'wcf-analytics-settings',                    // Id.
			__( 'Analytics', 'cartflows-pro' ), // Title.
			array( $this, 'analytics_metabox_markup' ),      // Callback.
			CARTFLOWS_FLOW_POST_TYPE,               // Post_type.
			'side',                               // Context.
			'high'                                  // Priority.
		);
	}

	/**
	 * Analytics Metabox Markup
	 *
	 * @param array $post post data.
	 * @return void
	 */
	public function analytics_metabox_markup( $post ) {
		?>
		<div class="wcf-flow-sandbox-table wcf-general-metabox-wrap widefat">
			<div class="wcf-flow-enable-analytics">
				<?php

					$meta_intance = Cartflows_Flow_Meta::get_instance();
					$meta_data    = $meta_intance->get_current_post_meta( $post->ID );
				foreach ( $meta_data as $key => $value ) {
					$options[ $key ] = $meta_data[ $key ]['default'];
				}

					echo wcf()->meta->get_checkbox_field(
						array(
							'name'  => 'wcf-enable-analytics',
							'value' => $options['wcf-enable-analytics'],
							'after' => esc_html__( 'Enable Flow Analytics', 'cartflows-pro' ),
						)
					);
				?>
			</div>
			<div class="wcf-flow-sandbox-table-container">
				<?php

					echo wcf()->meta->get_description_field(
						array(
							'name'    => 'wcf-analytics-note',
							'content' => esc_html__( 'Analytics offers data that helps you understand how your flows are performing.', 'cartflows-pro' ),
						)
					);
				?>
					<div class="button-wrap">
					<?php
						$this->setup_analytics_button();
						$this->reset_analytics_button();
					?>
					</div>
			</div>
		</div>
		<?php
	}

	/**
	 * Render analytics display button beside title.
	 */
	public function setup_analytics_button() {

		if ( ! Cartflows_Admin::is_flow_edit_admin() ) {
			return;
		}

		$reports_btn_markup          = '<style>.wrap{ position:relative;}</style>';
		$reports_btn_markup         .= "<div class='wcf-reports-button-wrap'>";
			$reports_btn_markup     .= "<button class='wcf-trigger-reports-popup button button-secondary'>";
				$reports_btn_markup .= esc_html__( 'View Analytics', 'cartflows-pro' );
			$reports_btn_markup     .= '</button>';
		$reports_btn_markup         .= '</div>';

		echo $reports_btn_markup;

	}

	/**
	 *
	 * Add Analytics reset button.
	 *
	 * @return void
	 */
	public function reset_analytics_button() {

		if ( ! Cartflows_Admin::is_flow_edit_admin() ) {
			return;
		}

		$reset_btn_markup          = '<style>.wrap{ position:relative;}</style>';
		$reset_btn_markup         .= "<div class='wcf-reset-button-wrap'>";
			$reset_btn_markup     .= "<button class='wcf-reset-analytics button button-secondary' id='wcf-reset-analytics-button' data-process='" . esc_html__( 'Resetting', 'cartflows-pro' ) . "'>";
				$reset_btn_markup .= esc_html__( 'Reset Analytics', 'cartflows-pro' );
			$reset_btn_markup     .= '</button>';
		$reset_btn_markup         .= '</div>';

		echo $reset_btn_markup;

	}

	/**
	 * Set visits data for later use in analytics.
	 */
	public function set_visits_data() {

		$flow_id  = isset( $_POST['flow_id'] ) ? intval( $_POST['flow_id'] ) : 0;
		$earning  = $this->get_earnings( $flow_id );
		$visits   = $this->fetch_visits( $flow_id );
		$all_data = $this->visits_map( $flow_id, $visits, $earning );

		$response = array(
			'revenue'   => $earning,
			'all_steps' => $all_data,
		);

		wp_send_json_success( $response );
	}

	/**
	 * Display analytics stat table.
	 */
	public function render_analytics_stat() {

		if ( ! Cartflows_Admin::is_flow_edit_admin() ) {
			return;
		}

		$currency_symbol = '';

		if ( wcf_pro()->is_woo_active ) {

			if ( function_exists( 'get_woocommerce_currency_symbol' ) ) {
				$currency_symbol = get_woocommerce_currency_symbol();
			}
		}

		include CARTFLOWS_TRACKING_DIR . '/view/analytics-template.php';

		include CARTFLOWS_TRACKING_DIR . '/view/analytics-popup.php';
	}

	/**
	 * Visits map.
	 *
	 * @param int   $flow_id flow id.
	 * @param array $visits visits data.
	 * @param array $earning earning data.
	 * @return array
	 */
	public function visits_map( $flow_id, $visits, $earning ) {

		$visits_map = array();

		foreach ( $visits as $v_in => $v_data ) {

			$step_id                = $v_data->step_id;
			$v_data_array           = (array) $v_data;
			$visits_map[ $step_id ] = $v_data_array;
			$step_type              = wcf()->utils->get_step_type( $step_id );

			$visits_map[ $step_id ]['revenue']         = 0;
			$visits_map[ $step_id ]['title']           = get_the_title( $step_id );
			$visits_map[ $step_id ]['note']            = get_post_meta( $step_id, 'wcf-step-note', true );
			$visits_map[ $step_id ]['conversion_rate'] = 0;

			// Set conversion rate.
			$conversions  = intval( $v_data_array['conversions'] );
			$total_visits = intval( $v_data_array['total_visits'] );

			if ( $total_visits > 0 ) {

				$conversion_rate = $conversions / intval( $v_data_array['total_visits'] ) * 100;

				$visits_map[ $step_id ]['conversion_rate'] = number_format( (float) $conversion_rate, 2, '.', '' );
			}

			switch ( $step_type ) {

				case 'checkout':
					$visits_map[ $step_id ]['revenue'] = 0;

					if ( isset( $earning['checkout'][ $step_id ] ) ) {
						$visits_map[ $step_id ]['revenue'] = $earning['checkout'][ $step_id ];
					}
					break;
				case 'upsell':
				case 'downsell':
					$visits_map[ $step_id ]['revenue'] = 0;

					if ( isset( $earning['offer'][ $step_id ] ) ) {
						$visits_map[ $step_id ]['revenue'] = $earning['offer'][ $step_id ];
					}
					break;
			}

			$visits_map[ $step_id ]['revenue'] = number_format( (float) $visits_map[ $step_id ]['revenue'], 2, '.', '' );
		}

		$all_steps = wcf()->flow->get_steps( $flow_id );

		foreach ( $all_steps as $in => $step_data ) {

			$step_id = $step_data['id'];

			if ( isset( $visits_map[ $step_id ] ) ) {

				$all_steps[ $in ]['visits'] = $visits_map[ $step_id ];

				if ( isset( $step_data['ab-test'] ) ) {

					$ab_total_visits  = 0;
					$ab_unique_visits = 0;
					$ab_conversions   = 0;
					$ab_revenue       = 0;

					// If ab test true but ab test ui is off and variations are empty.
					if ( isset( $step_data['ab-test-variations'] ) && ! empty( $step_data['ab-test-variations'] ) ) {

						$variations = $step_data['ab-test-variations'];

						foreach ( $variations as $v_in => $v_data ) {

							$v_id = $v_data['id'];

							if ( isset( $visits_map[ $v_id ] ) ) {

								$all_steps[ $in ]['visits-ab'][ $v_id ] = $visits_map[ $v_id ];

								$ab_total_visits  = $ab_total_visits + intval( $visits_map[ $v_id ]['total_visits'] );
								$ab_unique_visits = $ab_unique_visits + intval( $visits_map[ $v_id ]['unique_visits'] );
								$ab_conversions   = $ab_conversions + intval( $visits_map[ $v_id ]['conversions'] );
								$ab_revenue       = $ab_revenue + $visits_map[ $v_id ]['revenue'];

							}
						}
					} else {
						$ab_total_visits  = $all_steps[ $in ]['visits']['total_visits'];
						$ab_unique_visits = $all_steps[ $in ]['visits']['unique_visits'];
						$ab_conversions   = $all_steps[ $in ]['visits']['conversions'];
						$ab_revenue       = $all_steps[ $in ]['visits']['revenue'];

						$all_steps[ $in ]['visits-ab'][ $step_id ] = $visits_map[ $step_id ];
					}

					if ( isset( $step_data['ab-test-archived-variations'] ) && ! empty( $step_data['ab-test-archived-variations'] ) ) {

						/* Add archived variations */
						$archived_variations = $step_data['ab-test-archived-variations'];

						foreach ( $archived_variations as $v_in => $v_data ) {

							$v_id = $v_data['id'];

							if ( isset( $visits_map[ $v_id ] ) ) {

								$all_steps[ $in ]['visits-ab-archived'][ $v_id ]          = $visits_map[ $v_id ];
								$all_steps[ $in ]['visits-ab-archived'][ $v_id ]['title'] = $v_data['title'];

								if ( $v_data['deleted'] ) {
									$all_steps[ $in ]['visits-ab-archived'][ $v_id ]['archived_date'] = '(Deleted on ' . $v_data['date'] . ')';
								} else {
									$all_steps[ $in ]['visits-ab-archived'][ $v_id ]['archived_date'] = '(Archived on ' . $v_data['date'] . ')';
								}

								$all_steps[ $in ]['visits-ab-archived'][ $v_id ]['note'] = isset( $v_data['note'] ) ? $v_data['note'] : '';

								$ab_total_visits  = $ab_total_visits + intval( $visits_map[ $v_id ]['total_visits'] );
								$ab_unique_visits = $ab_unique_visits + intval( $visits_map[ $v_id ]['unique_visits'] );
								$ab_conversions   = $ab_conversions + intval( $visits_map[ $v_id ]['conversions'] );
								$ab_revenue       = $ab_revenue + $visits_map[ $v_id ]['revenue'];
							}
						}
					}

					// Add total count to main step.
					$all_steps[ $in ]['visits']['total_visits']  = $ab_total_visits;
					$all_steps[ $in ]['visits']['unique_visits'] = $ab_unique_visits;
					$all_steps[ $in ]['visits']['conversions']   = $ab_conversions;
					$all_steps[ $in ]['visits']['revenue']       = number_format( (float) $ab_revenue, 2, '.', '' );

					// Calculate total conversion count and set to main step.
					$total_conversion_rate = 0;

					if ( $ab_total_visits > 0 ) {
						$total_conversion_rate = $ab_conversions / $ab_total_visits * 100;
						$total_conversion_rate = number_format( (float) $total_conversion_rate, 2, '.', '' );
					}

					$all_steps[ $in ]['visits']['conversion_rate'] = $total_conversion_rate;
				}
			}
		}

		return $all_steps;
	}

	/**
	 * Fetch total visits.
	 *
	 * @param integer $flow_id flow_id.
	 * @return array|object|null
	 */
	public function fetch_visits( $flow_id ) {

		global $wpdb;

		$visit_db      = $wpdb->prefix . CARTFLOWS_PRO_VISITS_TABLE;
		$visit_meta_db = $wpdb->prefix . CARTFLOWS_PRO_VISITS_META_TABLE;

		$start_date = filter_input( INPUT_POST, 'date_from', FILTER_SANITIZE_STRING );
		$end_date   = filter_input( INPUT_POST, 'date_to', FILTER_SANITIZE_STRING );
		$start_date = $start_date ? $start_date : date( 'Y-m-d' ); //phpcs:ignore
		$end_date   = $end_date ? $end_date : date( 'Y-m-d' ); //phpcs:ignore
		$start_date = date( 'Y-m-d H:i:s', strtotime( $start_date . '00:00:00' ) ); //phpcs:ignore
		$end_date   = date( 'Y-m-d H:i:s', strtotime( $end_date . '23:59:59' ) ); //phpcs:ignore

		// Need to look into date format later.
		$analytics_reset_date = wcf()->options->get_flow_meta_value( $flow_id, 'wcf-analytics-reset-date' );

		if ( $analytics_reset_date > $start_date ) {
			$start_date = $analytics_reset_date;
		}

		$steps     = wcf()->flow->get_steps( $flow_id );
		$all_steps = array();

		foreach ( $steps as $s_key => $s_data ) {

			if ( isset( $s_data['ab-test'] ) ) {

				if ( isset( $s_data['ab-test-variations'] ) && ! empty( $s_data['ab-test-variations'] ) ) {

					foreach ( $s_data['ab-test-variations'] as $v_key => $v_data ) {

						$all_steps[] = $v_data['id'];
					}
				} else {
					$all_steps[] = $s_data['id'];
				}

				if ( isset( $s_data['ab-test-archived-variations'] ) && ! empty( $s_data['ab-test-archived-variations'] ) ) {

					foreach ( $s_data['ab-test-archived-variations'] as $av_key => $av_data ) {

						$all_steps[] = $av_data['id'];
					}
				}
			} else {
				$all_steps[] = $s_data['id'];
			}
		}

		$step_ids = implode( ', ', $all_steps );

		// phpcs:disable
		$query = $wpdb->prepare(
			"SELECT step_id,
			 COUNT( DISTINCT( $visit_db.id ) ) AS total_visits,
			 COUNT( DISTINCT( CASE WHEN visit_type = 'new' 
			 THEN $visit_db.id ELSE NULL END ) ) AS unique_visits,
			 COUNT( CASE WHEN $visit_meta_db.meta_key = 'conversion' 
			 AND $visit_meta_db.meta_value = 'yes' 
			 THEN step_id ELSE NULL END ) AS conversions 
			 FROM $visit_db INNER JOIN $visit_meta_db ON $visit_db.id = $visit_meta_db.visit_id
			 WHERE step_id IN ( $step_ids ) 
			 AND ( date_visited BETWEEN %s AND %s ) 
			 GROUP BY step_id
			 ORDER BY NULL",//phpcs:ignore
			$start_date,
			$end_date
		);
		// phpcs:enable
		$visits = $wpdb->get_results( $query ); //phpcs:ignore

		$visited_steps     = wp_list_pluck( (array) $visits, 'step_id' );
		$non_visited_steps = array_diff( $all_steps, $visited_steps );

		// Non visited steps.
		if ( $non_visited_steps ) {

			$non_visit = array(
				'step_id'       => 0,
				'total_visits'  => 0,
				'unique_visits' => 0,
				'conversions'   => 0,
				'revenue'       => 0,
			);

			foreach ( $non_visited_steps as $non_visited_step ) {

				$non_visit['step_id'] = $non_visited_step;
				array_push( $visits, (object) $non_visit );

			}
		}

		$step_ids_array = wp_list_pluck( (array) $steps, 'id' );
		usort(
			$visits,
			function ( $a, $b ) use ( $all_steps ) {
				return array_search( intval( $a->step_id ), $all_steps, true ) - array_search( intval( $b->step_id ), $all_steps, true );

			}
		);

		// phpcs:enable
		return $visits;
	}

	/**
	 * Calculate earning.
	 *
	 * @param integer $flow_id flow_id.
	 * @return array
	 */
	public function get_earnings( $flow_id ) {

		$orders                   = $this->get_orders_by_flow( $flow_id );
		$gross_sale               = 0;
		$checkout_total           = 0;
		$avg_order_value          = 0;
		$total_bump_offer_earning = 0;
		$checkout_earnings        = array();
		$offer_earnings           = array();
		$order_count              = 0;

		if ( ! empty( $orders ) ) {

			foreach ( $orders as $order ) {

				$order_id    = $order->ID;
				$order       = wc_get_order( $order_id );
				$order_total = $order->get_total();
				if ( ! $order->has_status( 'cancelled' ) ) {
					$gross_sale    += (float) $order_total;
					$checkout_total = (float) $order_total;
				}
				$bump_product_id      = $order->get_meta( '_wcf_bump_product' );
				$bump_offer_earnings  = 0;
				$separate_offer_order = $order->get_meta( '_cartflows_parent_flow_id' );
				$checkout_id          = $order->get_meta( '_wcf_checkout_id' );

				if ( empty( $separate_offer_order ) ) {

					$order_count++;

					foreach ( $order->get_items() as $item_id => $item_data ) {

						$item_product_id = $item_data->get_product_id();
						$item_total      = $item_data->get_total();
						$is_upsell       = wc_get_order_item_meta( $item_id, '_cartflows_upsell', true );
						$is_downsell     = wc_get_order_item_meta( $item_id, '_cartflows_downsell', true );
						$offer_step_id   = wc_get_order_item_meta( $item_id, '_cartflows_step_id', true );

						if ( 'yes' === $is_upsell ) {
							$checkout_total -= $item_total;

							if ( ! isset( $offer_earnings[ $offer_step_id ] ) ) {
								$offer_earnings[ $offer_step_id ] = 0;
							}
							$offer_earnings[ $offer_step_id ] += number_format( (float) $item_total, 2, '.', '' );
						}

						if ( 'yes' === $is_downsell ) {
							$checkout_total -= $item_total;

							if ( ! isset( $offer_earnings[ $offer_step_id ] ) ) {
								$offer_earnings[ $offer_step_id ] = 0;
							}

							$offer_earnings[ $offer_step_id ] += number_format( (float) $item_total, 2, '.', '' );
						}

						if ( $item_product_id == $bump_product_id ) {
							$bump_offer_earnings += $item_total;
							$checkout_total      -= $item_total;
						}
					}
				} else {

					$is_offer      = $order->get_meta( '_cartflows_offer' );
					$offer_step_id = $order->get_meta( '_cartflows_offer_step_id', true );

					if ( 'yes' === $is_offer ) {
						$checkout_total -= $order_total;

						if ( ! isset( $offer_earnings[ $offer_step_id ] ) ) {
							$offer_earnings[ $offer_step_id ] = 0;
						}

						$offer_earnings[ $offer_step_id ] += number_format( (float) $order_total, 2, '.', '' );
					}
				}

				$total_bump_offer_earning += $bump_offer_earnings;

				if ( ! isset( $checkout_earnings[ $checkout_id ] ) ) {
					$checkout_earnings[ $checkout_id ] = 0;
				}

				$checkout_earnings[ $checkout_id ] = $checkout_earnings[ $checkout_id ] + $checkout_total;
			}

			if ( 0 !== $order_count ) {
				$avg_order_value = $gross_sale / $order_count;
			}
		}

		$all_earning_data = array(
			'avg_order_value' => number_format( (float) $avg_order_value, 2, '.', '' ),
			'gross_sale'      => number_format( (float) $gross_sale, 2, '.', '' ),
			'checkout_sale'   => number_format( (float) $checkout_total, 2, '.', '' ),
			'offer'           => $offer_earnings,
			'checkout'        => $checkout_earnings,
			'bump_offer'      => number_format( (float) $total_bump_offer_earning, 2, '.', '' ),
		);

		return $all_earning_data;
	}

	/**
	 * Load analytics scripts.
	 */
	public function load_analytics_scripts() {

		if ( Cartflows_Admin::is_flow_edit_admin() ) {
			wp_enqueue_script( 'jquery-ui-datepicker' );
			wp_enqueue_style( 'jquery-ui-style' );
			wp_enqueue_script( 'cartflows-analytics-admin', CARTFLOWS_TRACKING_URL . 'assets/js/analytics-admin.js', array( 'jquery' ), CARTFLOWS_VER, true );
		}
	}


	/**
	 * Prepare where items for query.
	 *
	 * @param array $conditions conditions to prepare WHERE query.
	 * @return string
	 */
	protected function get_items_query_where( $conditions ) {

		global $wpdb;

		$where_conditions = array();
		$where_values     = array();

		foreach ( $conditions as $key => $condition ) {

			if ( false !== stripos( $key, 'IN' ) ) {
				$where_conditions[] = $key . '( %s )';
			} else {
				$where_conditions[] = $key . '= %s';
			}

			$where_values[] = $condition;
		}

		if ( ! empty( $where_conditions ) ) {
			// @codingStandardsIgnoreStart
			return $wpdb->prepare( 'WHERE 1 = 1 AND ' . implode( ' AND ', $where_conditions ), $where_values );
			// @codingStandardsIgnoreEnd
		} else {
			return '';
		}
	}


	/**
	 * Get orders data for flow.
	 *
	 * @param int $flow_id flow id.
	 * @return int
	 */
	public function get_orders_by_flow( $flow_id ) {

		global $wpdb;

		$start_date = filter_input( INPUT_POST, 'date_from', FILTER_SANITIZE_STRING );
		$end_date   = filter_input( INPUT_POST, 'date_to', FILTER_SANITIZE_STRING );
		$start_date = $start_date ? $start_date : date( 'Y-m-d' ); //phpcs:ignore
		$end_date   = $end_date ? $end_date : date( 'Y-m-d' ); //phpcs:ignore
		$start_date = date( 'Y-m-d H:i:s', strtotime( $start_date . '00:00:00' ) ); //phpcs:ignore
		$end_date   = date( 'Y-m-d H:i:s', strtotime( $end_date . '23:59:59' ) ); //phpcs:ignore

		$analytics_reset_date = wcf()->options->get_flow_meta_value( $flow_id, 'wcf-analytics-reset-date' );

		if ( $analytics_reset_date > $start_date ) {
			$start_date = $analytics_reset_date;
		}

		$conditions = array(
			'tb1.post_type' => 'shop_order',
		);

		$where  = $this->get_items_query_where( $conditions );
		$where .= " AND ( tb1.post_date BETWEEN '" . $start_date . "' AND '" . $end_date . "' )";
		$where .= " AND ( ( tb2.meta_key = '_wcf_flow_id' AND tb2.meta_value = $flow_id ) OR ( tb2.meta_key = '_cartflows_parent_flow_id' AND tb2.meta_value = $flow_id ) )";
		$where .= " AND tb1.post_status IN ( 'wc-completed', 'wc-processing', 'wc-cancelled' )";

		$query = 'SELECT tb1.ID, DATE( tb1.post_date ) date FROM ' . $wpdb->prefix . 'posts tb1 
		INNER JOIN ' . $wpdb->prefix . 'postmeta tb2
		ON tb1.ID = tb2.post_id 
		' . $where;

		// @codingStandardsIgnoreStart
		$orders = $wpdb->get_results( $query );
		// @codingStandardsIgnoreEnd

		self::$flow_orders = $orders;

		return $orders;
	}
}

Cartflows_Pro_Analytics_Reports::get_instance();