Initial commit.
This commit is contained in:
@@ -0,0 +1,640 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Compatibility
|
||||
*
|
||||
* @package WooCommerce\Payments\Compatibility
|
||||
*/
|
||||
|
||||
namespace WCPay\MultiCurrency;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Package;
|
||||
use Automattic\WooCommerce\Blocks\Assets\AssetDataRegistry;
|
||||
use Automattic\WooCommerce\Utilities\OrderUtil;
|
||||
use WC_Order;
|
||||
use WC_Order_Refund;
|
||||
use WCPay\MultiCurrency\Interfaces\MultiCurrencySettingsInterface;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class that contains Multi-Currency related support for WooCommerce analytics.
|
||||
*/
|
||||
class Analytics {
|
||||
const PRIORITY_EARLY = 1;
|
||||
const PRIORITY_DEFAULT = 10;
|
||||
const PRIORITY_LATE = 20;
|
||||
const PRIORITY_LATEST = 99999;
|
||||
const SCRIPT_NAME = 'WCPAY_MULTI_CURRENCY_ANALYTICS';
|
||||
|
||||
const SUPPORTED_CONTEXTS = [ 'orders', 'products', 'variations', 'categories', 'coupons', 'taxes' ];
|
||||
|
||||
/**
|
||||
* SQL string replacements made by the analytics Multi-Currency extension.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $sql_replacements = [];
|
||||
|
||||
/**
|
||||
* Instance of MultiCurrency.
|
||||
*
|
||||
* @var MultiCurrency $multi_currency
|
||||
*/
|
||||
private $multi_currency;
|
||||
|
||||
/**
|
||||
* Instance of MultiCurrencySettingsInterface.
|
||||
*
|
||||
* @var MultiCurrencySettingsInterface $settings_service
|
||||
*/
|
||||
private $settings_service;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param MultiCurrency $multi_currency Instance of MultiCurrency.
|
||||
* @param MultiCurrencySettingsInterface $settings_service Instance of MultiCurrencySettingsInterface.
|
||||
*/
|
||||
public function __construct( MultiCurrency $multi_currency, MultiCurrencySettingsInterface $settings_service ) {
|
||||
$this->multi_currency = $multi_currency;
|
||||
$this->settings_service = $settings_service;
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise all actions, filters and hooks related to analytics support.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
if ( is_admin() && current_user_can( 'manage_woocommerce' ) ) {
|
||||
add_filter( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_scripts' ] );
|
||||
$this->register_customer_currencies();
|
||||
}
|
||||
|
||||
if ( $this->settings_service->is_dev_mode() ) {
|
||||
add_filter( 'woocommerce_analytics_report_should_use_cache', [ $this, 'disable_report_caching' ] );
|
||||
}
|
||||
|
||||
// Add a filter when the order stats table is updated.
|
||||
add_filter( 'woocommerce_analytics_update_order_stats_data', [ $this, 'update_order_stats_data' ], self::PRIORITY_LATEST, 2 );
|
||||
|
||||
// Add filters when the query args are updated.
|
||||
add_filter( 'woocommerce_analytics_orders_query_args', [ $this, 'apply_customer_currency_args' ] );
|
||||
add_filter( 'woocommerce_analytics_orders_stats_query_args', [ $this, 'apply_customer_currency_args' ] );
|
||||
|
||||
// If we aren't making a REST request, or no multi currency orders exist in the merchant's store,
|
||||
// return before adding these filters.
|
||||
|
||||
if ( ! WC()->is_rest_api_request() || ! $this->has_multi_currency_orders() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->set_sql_replacements();
|
||||
|
||||
// Add the filters that are applied in each analytics query.
|
||||
add_filter( 'woocommerce_analytics_clauses_select', [ $this, 'filter_select_clauses' ], self::PRIORITY_LATE, 2 );
|
||||
add_filter( 'woocommerce_analytics_clauses_join', [ $this, 'filter_join_clauses' ], self::PRIORITY_LATE, 2 );
|
||||
|
||||
// Add the WHERE clause filter which is applied only on Order related queries.
|
||||
add_filter( 'woocommerce_analytics_clauses_where_orders_subquery', [ $this, 'filter_where_clauses' ] );
|
||||
add_filter( 'woocommerce_analytics_clauses_where_orders_stats_total', [ $this, 'filter_where_clauses' ] );
|
||||
add_filter( 'woocommerce_analytics_clauses_where_orders_stats_interval', [ $this, 'filter_where_clauses' ] );
|
||||
|
||||
// phpcs:disable WordPress.Security.NonceVerification.Recommended
|
||||
if ( ! empty( $_GET['currency'] ) && $_GET['currency'] !== $this->multi_currency->get_default_currency()->get_code() ) {
|
||||
add_filter( 'woocommerce_analytics_clauses_select_orders_subquery', [ $this, 'filter_select_orders_clauses' ] );
|
||||
add_filter( 'woocommerce_analytics_clauses_select_orders_stats_total', [ $this, 'filter_select_orders_clauses' ] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the CSS and JS scripts.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_admin_scripts() {
|
||||
$this->multi_currency->register_script_with_dependencies( self::SCRIPT_NAME, 'dist/multi-currency-analytics' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the list of currencies used on the store to the wcSettings to allow it to be accessed by the front-end JS script.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_customer_currencies() {
|
||||
$data_registry = Package::container()->get( AssetDataRegistry::class );
|
||||
if ( $data_registry->exists( 'customerCurrencies' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$currencies = $this->multi_currency->get_all_customer_currencies();
|
||||
$available_currencies = $this->multi_currency->get_available_currencies();
|
||||
$currency_options = [];
|
||||
|
||||
$default_currency = $this->multi_currency->get_default_currency();
|
||||
|
||||
// Add default currency to the list if it does not exist.
|
||||
if ( ! in_array( $default_currency->get_code(), $currencies, true ) ) {
|
||||
$currencies[] = $default_currency->get_code();
|
||||
}
|
||||
|
||||
foreach ( $currencies as $currency ) {
|
||||
if ( ! isset( $available_currencies[ $currency ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$currency_details = $available_currencies[ $currency ];
|
||||
$currency_options[] = [
|
||||
'label' => html_entity_decode( $currency_details->get_name() ),
|
||||
'value' => $currency_details->get_code(),
|
||||
];
|
||||
}
|
||||
|
||||
$data_registry->add( 'customerCurrencies', $currency_options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue scripts used on the analytics WP Admin pages.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_admin_scripts() {
|
||||
$this->register_admin_scripts();
|
||||
|
||||
wp_enqueue_script( self::SCRIPT_NAME );
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables report caching. Used for development of analytics related functionality.
|
||||
* To disable report caching
|
||||
*
|
||||
* @param array $args Filter arguments.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function disable_report_caching( $args ): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the customer currency is set, add it as a query parameter to requests to the data store.
|
||||
* This ensures that the cache is updated when this value is changed between requests.
|
||||
*
|
||||
* @param array $args The arguments passed in via the GET request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function apply_customer_currency_args( $args ): array {
|
||||
$currency_args = $this->get_customer_currency_args_from_request();
|
||||
return array_merge( $args, $currency_args );
|
||||
}
|
||||
|
||||
/**
|
||||
* When an order is updated in the stats table, perform a check to see if it is a Multi-Currency order
|
||||
* and convert the information into the store's default currency if it is.
|
||||
*
|
||||
* @param array $args - An array of the arguments to be inserted into the order stats table.
|
||||
* @param WC_Order $order - The order itself.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function update_order_stats_data( array $args, $order ): array {
|
||||
if ( ! $this->should_convert_order_stats( $order ) ) {
|
||||
return $args;
|
||||
}
|
||||
|
||||
$stripe_exchange_rate = $order->get_meta( '_wcpay_multi_currency_stripe_exchange_rate', true )
|
||||
? (float) $order->get_meta( '_wcpay_multi_currency_stripe_exchange_rate', true )
|
||||
: null;
|
||||
$order_exchange_rate = ( 1 / (float) $order->get_meta( '_wcpay_multi_currency_order_exchange_rate', true ) );
|
||||
|
||||
$exchange_rate = $stripe_exchange_rate ?? $order_exchange_rate;
|
||||
|
||||
$dp = wc_get_price_decimals();
|
||||
$args['net_total'] = round( $this->convert_amount( (float) $args['net_total'], $exchange_rate ), $dp );
|
||||
$args['shipping_total'] = round( $this->convert_amount( (float) $args['shipping_total'], $exchange_rate ), $dp );
|
||||
$args['tax_total'] = round( $this->convert_amount( (float) $args['tax_total'], $exchange_rate ), $dp );
|
||||
$args['total_sales'] = $args['net_total'] + $args['shipping_total'] + $args['tax_total'];
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add columns to get the currency and converted amount (if required).
|
||||
*
|
||||
* @param string[] $clauses - An array containing the SELECT clauses to be applied.
|
||||
* @param string $context - The context in which this SELECT clause is being called.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function filter_select_clauses( array $clauses, $context ): array {
|
||||
// If we are unable to identify a context, just return the clauses as is.
|
||||
if ( is_null( $context ) ) {
|
||||
return $clauses;
|
||||
}
|
||||
|
||||
if ( apply_filters( MultiCurrency::FILTER_PREFIX . 'disable_filter_select_clauses', false ) ) {
|
||||
return $clauses;
|
||||
}
|
||||
|
||||
$context_parts = explode( '_', $context );
|
||||
$context_page = $context_parts[0] ?? 'generic';
|
||||
$context_type = $context_parts[1] ?? null;
|
||||
|
||||
// If we can't identify the type of context we are running in (stats or subquery), then return the clauses as is.
|
||||
if ( ! in_array( $context_type, [ 'stats', 'subquery' ], true ) ) {
|
||||
return $clauses;
|
||||
}
|
||||
|
||||
$new_clauses = [];
|
||||
$sql_replacements = $this->get_sql_replacements();
|
||||
|
||||
foreach ( $clauses as $clause ) {
|
||||
if ( ! array_key_exists( $context_page, $sql_replacements ) ) {
|
||||
$replacements_array = $sql_replacements['generic'] ?? [];
|
||||
} else {
|
||||
$replacements_array = $sql_replacements[ $context_page ] ?? [];
|
||||
}
|
||||
|
||||
foreach ( $replacements_array as $find => $replace ) {
|
||||
if ( strpos( $clause, $find ) !== false ) {
|
||||
$clause = str_replace(
|
||||
$find,
|
||||
$replace,
|
||||
$clause
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$new_clauses[] = $clause;
|
||||
}
|
||||
|
||||
if ( $this->is_supported_context( $context ) && ( in_array( $context_page, self::SUPPORTED_CONTEXTS, true ) || $this->is_order_stats_table_used_in_clauses( $clauses ) ) ) {
|
||||
if ( $this->is_cot_enabled() ) {
|
||||
$new_clauses[] = ', wcpay_multicurrency_order_currency.currency AS order_currency';
|
||||
} else {
|
||||
$new_clauses[] = ', wcpay_multicurrency_currency_meta.meta_value AS order_currency';
|
||||
}
|
||||
$new_clauses[] = ', wcpay_multicurrency_default_currency_meta.meta_value AS order_default_currency';
|
||||
$new_clauses[] = ', wcpay_multicurrency_exchange_rate_meta.meta_value AS exchange_rate';
|
||||
$new_clauses[] = ', wcpay_multicurrency_stripe_exchange_rate_meta.meta_value AS stripe_exchange_rate';
|
||||
}
|
||||
|
||||
return apply_filters( MultiCurrency::FILTER_PREFIX . 'filter_select_clauses', $new_clauses );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a JOIN so that we can get the currency information.
|
||||
*
|
||||
* @param string[] $clauses - An array containing the JOIN clauses to be applied.
|
||||
* @param string $context - The context in which this SELECT clause is being called.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function filter_join_clauses( array $clauses, $context ): array {
|
||||
global $wpdb;
|
||||
|
||||
if ( apply_filters( MultiCurrency::FILTER_PREFIX . 'disable_filter_join_clauses', false ) ) {
|
||||
return $clauses;
|
||||
}
|
||||
|
||||
$context_parts = explode( '_', $context, 2 );
|
||||
$context_page = $context_parts[0] ?? 'generic';
|
||||
|
||||
$prefix = 'wcpay_multicurrency_';
|
||||
$currency_tbl = $prefix . 'currency_meta';
|
||||
$default_currency_tbl = $prefix . 'default_currency_meta';
|
||||
$exchange_rate_tbl = $prefix . 'exchange_rate_meta';
|
||||
$stripe_exchange_rate_tbl = $prefix . 'stripe_exchange_rate_meta';
|
||||
|
||||
// Allow this to work with custom order tables as well.
|
||||
if ( $this->is_cot_enabled() ) {
|
||||
$meta_table = $wpdb->prefix . 'wc_orders_meta';
|
||||
$id_field = 'order_id';
|
||||
|
||||
$currency_tbl = $prefix . 'order_currency';
|
||||
} else {
|
||||
$meta_table = $wpdb->postmeta;
|
||||
$id_field = 'post_id';
|
||||
}
|
||||
|
||||
// If this is a supported context, add the joins. If this is an unsupported context, see if we can add the joins.
|
||||
if ( $this->is_supported_context( $context ) && ( in_array( $context_page, self::SUPPORTED_CONTEXTS, true ) || $this->is_order_stats_table_used_in_clauses( $clauses ) ) ) {
|
||||
if ( $this->is_cot_enabled() ) {
|
||||
$clauses[] = "LEFT JOIN {$wpdb->prefix}wc_orders {$currency_tbl} ON {$wpdb->prefix}wc_order_stats.order_id = {$currency_tbl}.id";
|
||||
} else {
|
||||
$clauses[] = "LEFT JOIN {$meta_table} {$currency_tbl} ON {$wpdb->prefix}wc_order_stats.order_id = {$currency_tbl}.{$id_field} AND {$currency_tbl}.meta_key = '_order_currency'";
|
||||
|
||||
}
|
||||
$clauses[] = "LEFT JOIN {$meta_table} {$default_currency_tbl} ON {$wpdb->prefix}wc_order_stats.order_id = {$default_currency_tbl}.{$id_field} AND {$default_currency_tbl}.meta_key = '_wcpay_multi_currency_order_default_currency'";
|
||||
$clauses[] = "LEFT JOIN {$meta_table} {$exchange_rate_tbl} ON {$wpdb->prefix}wc_order_stats.order_id = {$exchange_rate_tbl}.{$id_field} AND {$exchange_rate_tbl}.meta_key = '_wcpay_multi_currency_order_exchange_rate'";
|
||||
$clauses[] = "LEFT JOIN {$meta_table} {$stripe_exchange_rate_tbl} ON {$wpdb->prefix}wc_order_stats.order_id = {$stripe_exchange_rate_tbl}.{$id_field} AND {$stripe_exchange_rate_tbl}.meta_key = '_wcpay_multi_currency_stripe_exchange_rate'";
|
||||
}
|
||||
|
||||
return apply_filters( MultiCurrency::FILTER_PREFIX . 'filter_join_clauses', $clauses );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the WHERE clauses (if a customer currency has been selected).
|
||||
*
|
||||
* @param string[] $clauses - An array containing the JOIN clauses to be applied.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function filter_where_clauses( array $clauses ): array {
|
||||
if ( apply_filters( MultiCurrency::FILTER_PREFIX . 'disable_filter_where_clauses', false ) ) {
|
||||
return $clauses;
|
||||
}
|
||||
|
||||
$prefix = 'wcpay_multicurrency_';
|
||||
if ( $this->is_cot_enabled() ) {
|
||||
$currency_field = $prefix . 'order_currency.currency';
|
||||
} else {
|
||||
$currency_field = $prefix . 'currency_meta.meta_value';
|
||||
}
|
||||
|
||||
$currency_args = $this->get_customer_currency_args_from_request();
|
||||
if ( ! empty( $currency_args['currency_is'] ) ) {
|
||||
/**
|
||||
* Skip implode complaining array_map as wrong argument.
|
||||
*
|
||||
* @psalm-suppress InvalidArgument
|
||||
*/
|
||||
$currency_is = sprintf( "'%s'", implode( "', '", array_map( 'esc_sql', $currency_args['currency_is'] ) ) );
|
||||
$clauses[] = "AND {$currency_field} IN ({$currency_is})";
|
||||
}
|
||||
|
||||
if ( ! empty( $currency_args['currency_is_not'] ) ) {
|
||||
/**
|
||||
* Skip implode complaining array_map as wrong argument.
|
||||
*
|
||||
* @psalm-suppress InvalidArgument
|
||||
*/
|
||||
$currency_is_not = sprintf( "'%s'", implode( "', '", array_map( 'esc_sql', $currency_args['currency_is_not'] ) ) );
|
||||
$clauses[] = "AND {$currency_field} NOT IN ({$currency_is_not})";
|
||||
}
|
||||
|
||||
if ( ! empty( $currency_args['currency'] ) ) {
|
||||
global $wpdb;
|
||||
$expression = "AND {$currency_field} = '%s'";
|
||||
$clauses[] = $wpdb->prepare( $expression, $currency_args['currency'] ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
}
|
||||
|
||||
return apply_filters( MultiCurrency::FILTER_PREFIX . 'filter_where_clauses', $clauses );
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert amounts back to order currency (if a currency has been selected).
|
||||
* Skipping it for default currency.
|
||||
*
|
||||
* @param string[] $clauses - An array containing the SELECT orders clauses to be applied.
|
||||
* @return array
|
||||
*/
|
||||
public function filter_select_orders_clauses( array $clauses ): array {
|
||||
if ( apply_filters( MultiCurrency::FILTER_PREFIX . 'disable_filter_select_orders_clauses', false ) ) {
|
||||
return $clauses;
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
$exchange_rate = 'wcpay_multicurrency_exchange_rate_meta.meta_value';
|
||||
$stripe_exchange_rate = 'wcpay_multicurrency_stripe_exchange_rate_meta.meta_value';
|
||||
$net_total = "{$wpdb->prefix}wc_order_stats.net_total";
|
||||
|
||||
foreach ( $clauses as $k => $clause ) {
|
||||
if ( strpos( $clause, $net_total ) !== false ) {
|
||||
$is_orders_subquery = strpos( $clause, $net_total . ',' ) !== false;
|
||||
$variable = $is_orders_subquery ? "$net_total," : $net_total;
|
||||
$alias = $is_orders_subquery ? ' as net_total,' : '';
|
||||
$dp = wc_get_price_decimals();
|
||||
|
||||
$clauses[ $k ] = str_replace(
|
||||
$variable,
|
||||
$this->generate_case_when(
|
||||
$stripe_exchange_rate,
|
||||
"ROUND($net_total / $stripe_exchange_rate, $dp)",
|
||||
"ROUND($net_total * $exchange_rate, $dp)"
|
||||
) . $alias,
|
||||
$clause
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return apply_filters( MultiCurrency::FILTER_PREFIX . 'filter_select_orders_clauses', $clauses );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see whether we should convert an order to store in the order stats table.
|
||||
*
|
||||
* @param WC_Order|WC_Order_Refund $order The order.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function should_convert_order_stats( $order ): bool {
|
||||
$default_currency = $this->multi_currency->get_default_currency();
|
||||
|
||||
// If this order was in the default currency, or the meta information isn't set on the order, return false.
|
||||
if ( ! $order ||
|
||||
$order->get_currency() === $default_currency->get_code() ||
|
||||
! $order->get_meta( '_wcpay_multi_currency_order_exchange_rate', true ) ||
|
||||
$order->get_meta( '_wcpay_multi_currency_order_default_currency', true ) !== $default_currency->get_code()
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an amount to the store's default currency in order to store in the stats table.
|
||||
*
|
||||
* @param float $amount The amount to convert into the store's default currency.
|
||||
* @param float $exchange_rate The exchange rate to use for the conversion.
|
||||
*
|
||||
* @return float The converted amount.
|
||||
*/
|
||||
private function convert_amount( float $amount, float $exchange_rate ): float {
|
||||
return $amount * $exchange_rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the order stats table is referenced in the clauses, to work out whether
|
||||
* to add the JOIN columns for Multi-Currency.
|
||||
*
|
||||
* @param array $clauses The array containing the clauses used.
|
||||
*
|
||||
* @return boolean Whether the order stats table is referenced.
|
||||
*/
|
||||
private function is_order_stats_table_used_in_clauses( array $clauses ): bool {
|
||||
global $wpdb;
|
||||
|
||||
foreach ( $clauses as $clause ) {
|
||||
if ( strpos( $clause, "{$wpdb->prefix}wc_order_stats" ) !== false ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* There are some queries which are made in the analytics which are actually sub-queries
|
||||
* which are used to join on an individual item/coupon/tax code. In these cases, rather than
|
||||
* the context being the expected format e.g. product_stats_total, it will simply be 'product'.
|
||||
* In these cases, we don't want to add the join columns or select them.
|
||||
*
|
||||
* @param string $context The context the query was made in.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function is_supported_context( string $context ): bool {
|
||||
$unsupported_contexts = [ 'products', 'coupons', 'taxes', 'variations', 'categories' ];
|
||||
|
||||
if ( in_array( $context, $unsupported_contexts, true ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a case when statement using the provided variables.
|
||||
*
|
||||
* @param string $variable The SQL variable we want to check for NULL.
|
||||
* @param string $then The THEN clause.
|
||||
* @param string $else The ELSE clause.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function generate_case_when( string $variable, string $then, string $else ): string {
|
||||
return "CASE WHEN {$variable} IS NOT NULL THEN {$then} ELSE {$else} END";
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an SQL query to determine whether Multi Currency has ever been used on this store,
|
||||
* by checking how many orders are in the database where an exchange currency rate has been stored.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function has_multi_currency_orders() {
|
||||
global $wpdb;
|
||||
|
||||
// Using full SQL instead of variables to keep WPCS happy.
|
||||
if ( $this->is_cot_enabled() ) {
|
||||
$result = $wpdb->get_var(
|
||||
"SELECT EXISTS(
|
||||
SELECT 1
|
||||
FROM {$wpdb->prefix}wc_orders_meta
|
||||
WHERE meta_key = '_wcpay_multi_currency_order_exchange_rate'
|
||||
LIMIT 1)
|
||||
AS count;"
|
||||
);
|
||||
} else {
|
||||
$result = $wpdb->get_var(
|
||||
"SELECT EXISTS(
|
||||
SELECT 1
|
||||
FROM {$wpdb->postmeta}
|
||||
WHERE meta_key = '_wcpay_multi_currency_order_exchange_rate'
|
||||
LIMIT 1)
|
||||
AS count;"
|
||||
);
|
||||
}
|
||||
|
||||
return intval( $result ) === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SQL replacements variable.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_sql_replacements(): array {
|
||||
return $this->sql_replacements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the passed in query params to see if currency has been passed in.
|
||||
* Will return null if no currency variable was passed in, otherwise will
|
||||
* return the currency.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_customer_currency_args_from_request(): array {
|
||||
$args = [
|
||||
'currency_is' => [],
|
||||
'currency_is_not' => [],
|
||||
'currency' => null,
|
||||
];
|
||||
|
||||
/* phpcs:disable WordPress.Security.NonceVerification */
|
||||
if ( isset( $_GET['currency_is'] ) && is_array( $_GET['currency_is'] ) ) {
|
||||
$args['currency_is'] = array_map( 'sanitize_text_field', wp_unslash( $_GET['currency_is'] ) );
|
||||
}
|
||||
|
||||
if ( isset( $_GET['currency_is_not'] ) ) {
|
||||
$args['currency_is_not'] = array_map( 'sanitize_text_field', wp_unslash( $_GET['currency_is_not'] ) );
|
||||
}
|
||||
|
||||
if ( isset( $_GET['currency'] ) ) {
|
||||
$args['currency'] = sanitize_text_field( wp_unslash( $_GET['currency'] ) );
|
||||
}
|
||||
/* phpcs:enable WordPress.Security.NonceVerification */
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the SQL replacements variable.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function set_sql_replacements() {
|
||||
$default_currency = 'wcpay_multicurrency_default_currency_meta.meta_value';
|
||||
$exchange_rate = 'wcpay_multicurrency_exchange_rate_meta.meta_value';
|
||||
$stripe_exchange_rate = 'wcpay_multicurrency_stripe_exchange_rate_meta.meta_value';
|
||||
|
||||
$discount_amount = $this->generate_case_when( $default_currency, $this->generate_case_when( $stripe_exchange_rate, "ROUND(discount_amount * {$stripe_exchange_rate}, 2)", "ROUND(discount_amount * (1 / {$exchange_rate} ), 2)" ), 'discount_amount' );
|
||||
$product_net_revenue = $this->generate_case_when( $default_currency, $this->generate_case_when( $stripe_exchange_rate, "ROUND(product_net_revenue * {$stripe_exchange_rate}, 2)", "ROUND(product_net_revenue * (1 / {$exchange_rate} ), 2)" ), 'product_net_revenue' );
|
||||
$product_gross_revenue = $this->generate_case_when( $default_currency, $this->generate_case_when( $stripe_exchange_rate, "ROUND(product_gross_revenue * {$stripe_exchange_rate}, 2)", "ROUND(product_gross_revenue * (1 / {$exchange_rate} ), 2)" ), 'product_gross_revenue' );
|
||||
|
||||
$this->sql_replacements = [
|
||||
'generic' => [
|
||||
'discount_amount' => $discount_amount,
|
||||
'product_net_revenue' => $product_net_revenue,
|
||||
'product_gross_revenue' => $product_gross_revenue,
|
||||
],
|
||||
'orders' => [
|
||||
'discount_amount' => $discount_amount,
|
||||
],
|
||||
'products' => [
|
||||
'product_net_revenue' => $product_net_revenue,
|
||||
'product_gross_revenue' => $product_gross_revenue,
|
||||
],
|
||||
'variations' => [
|
||||
'product_net_revenue' => $product_net_revenue,
|
||||
'product_gross_revenue' => $product_gross_revenue,
|
||||
],
|
||||
'categories' => [
|
||||
'product_net_revenue' => $product_net_revenue,
|
||||
'product_gross_revenue' => $product_gross_revenue,
|
||||
],
|
||||
'taxes' => [
|
||||
'SUM(total_tax)' => 'SUM(' . $this->generate_case_when( $default_currency, $this->generate_case_when( $stripe_exchange_rate, "ROUND(total_tax * {$stripe_exchange_rate}, 2)", "ROUND(total_tax * (1 / {$exchange_rate} ), 2)" ), 'total_tax' ) . ')',
|
||||
'SUM(order_tax)' => 'SUM(' . $this->generate_case_when( $default_currency, $this->generate_case_when( $stripe_exchange_rate, "ROUND(order_tax * {$stripe_exchange_rate}, 2)", "ROUND(order_tax * (1 / {$exchange_rate} ), 2)" ), 'order_tax' ) . ')',
|
||||
'SUM(shipping_tax)' => 'SUM(' . $this->generate_case_when( $default_currency, $this->generate_case_when( $stripe_exchange_rate, "ROUND(shipping_tax * {$stripe_exchange_rate}, 2)", "ROUND(shipping_tax * (1 / {$exchange_rate} ), 2)" ), 'shipping_tax' ) . ')',
|
||||
],
|
||||
'coupons' => [
|
||||
'discount_amount' => $discount_amount,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether Custom Order Tables are enabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_cot_enabled(): bool {
|
||||
return class_exists( OrderUtil::class ) && OrderUtil::custom_orders_table_usage_is_enabled();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user