Файловый менеджер - Редактировать - /home/harasnat/www/labour/wp-content/plugins/woocommerce-payments/includes/multi-currency/Analytics.php
Назад
<?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(); } }
| ver. 1.4 |
Github
|
.
| PHP 8.1.33 | Генерация страницы: 0 |
proxy
|
phpinfo
|
Настройка