*/ private $admin_order_quote_cache = array(); public function __construct() { if (!is_admin()) { return false; } $this->init(); } public function init() { add_action('woocommerce_order_item_add_action_buttons', array($this, 'routeapp_custom_admin_button'), 10, 1); add_action('woocommerce_new_order_item', array($this, 'recalculate_route_fee'), 10, 1); add_action('woocommerce_add_order_tax', array($this, 'recalculate_route_fee'), 10, 1); add_action('woocommerce_saved_order_items', array($this, 'recalculate_route_fee'), 10, 1); add_action('woocommerce_delete_order_item', array($this, 'recalculate_route_fee'), 10, 1); } public static function get_route_public_instance(){ global $routeapp_public; return $routeapp_public; } public function routeapp_custom_admin_button($order) { echo $this->get_route_fee_button_html($order); } public function get_route_fee_button_html($order) { if (!$this->can_show_route_fee_buttons($order)) { return ''; } if ($this->has_route_fee_applied($order)) { return ''; } if ($this->is_order_eligible_for_route_coverage($order)) { return ''; } return ''; } public function routeapp_refresh_admin_fee_button() { check_ajax_referer( 'order-item', 'security' ); if (!isset($_POST['order_id'])) { wp_send_json_error('Missing order_id in the request.'); return; } $order = wc_get_order(absint($_POST['order_id'])); if (!$order) { wp_send_json_error('Invalid order_id.'); return; } wp_send_json_success(array( 'html' => $this->get_route_fee_button_html($order), )); } private function get_admin_order_cart_ref($order, $cart_total, $cart_items) { $items_hash = substr(md5(wp_json_encode($cart_items)), 0, 8); return 'admin-' . $order->get_id() . '-' . $cart_total . '-' . $items_hash; } private function get_admin_order_quote_context($order) { $routeapp_public = self::get_route_public_instance(); $cart_total = round($routeapp_public->get_cart_subtotal_with_only_shippable_items($order, 'order'), 2); $cart_items = $routeapp_public->get_cart_shippable_items($order, 'order'); return array( 'total' => $cart_total, 'items' => $cart_items, 'ref' => $this->get_admin_order_cart_ref($order, $cart_total, $cart_items), 'currency' => get_woocommerce_currency(), 'public' => $routeapp_public, ); } private function fetch_admin_order_quote($order) { $context = $this->get_admin_order_quote_context($order); if ($context['total'] <= 0) { return false; } if (array_key_exists($context['ref'], $this->admin_order_quote_cache)) { return $this->admin_order_quote_cache[$context['ref']]; } $quote = $context['public']->routeapp_get_quote_from_api( $context['ref'], $context['total'], $context['currency'], $context['items'] ); $this->admin_order_quote_cache[$context['ref']] = $quote; return $quote; } public function is_order_eligible_for_route_coverage($order) { if (!$order) { return false; } $routeapp_public = self::get_route_public_instance(); $route_insurance_quote = $this->fetch_admin_order_quote($order); return $this->is_quote_eligible_for_customer_coverage($route_insurance_quote, $routeapp_public); } private function is_quote_eligible_for_customer_coverage($route_insurance_quote, $routeapp_public) { if (!$route_insurance_quote) { return false; } if (!isset($route_insurance_quote->premium->amount) || $route_insurance_quote->premium->amount <= 0) { return false; } return isset($route_insurance_quote->payment_responsible->type) && $route_insurance_quote->payment_responsible->type === $routeapp_public->get_allowed_quote_type(); } private function routeapp_handle_admin_fee($order) { if (!$this->can_add_fee($order)) { return false; } $routeapp_public = self::get_route_public_instance(); $route_insurance_quote = $this->fetch_admin_order_quote($order); $route_insurance_amount = false; if ($this->is_quote_eligible_for_customer_coverage($route_insurance_quote, $routeapp_public)) { $route_insurance_amount = $route_insurance_quote->premium->amount; } if ($route_insurance_amount > 0) { $this->routeapp_create_fee($order, $route_insurance_amount); $order->calculate_totals(); if ($order->save()) { $acceptedOrderStatuses = get_option('routeapp_included_order_statuses') ? get_option('routeapp_included_order_statuses') : []; $acceptedCanceledStatuses = get_option('routeapp_cancel_order_statuses') ? get_option('routeapp_cancel_order_statuses') : []; if ( class_exists('Automattic\WooCommerce\Utilities\OrderUtil') && OrderUtil::custom_orders_table_usage_is_enabled() ) { // HPOS usage is enabled. $order->update_meta_data(self::ROUTE_CHARGE_POST_META, $route_insurance_amount ); $order->update_meta_data(self::ROUTE_PROTECTION_POST_META, true ); $order->update_meta_data('_routeapp_included_order_statuses', json_encode($acceptedOrderStatuses)); $order->update_meta_data( '_routeapp_cancel_order_statuses', json_encode($acceptedCanceledStatuses)); $order->save(); } else { // Traditional CPT-based orders are in use. update_post_meta( $order->get_id(), self::ROUTE_CHARGE_POST_META, $route_insurance_amount ); update_post_meta( $order->get_id(), self::ROUTE_PROTECTION_POST_META, true ); update_post_meta( $order->get_id(), '_routeapp_included_order_statuses', json_encode($acceptedOrderStatuses)); update_post_meta( $order->get_id(), '_routeapp_cancel_order_statuses', json_encode($acceptedCanceledStatuses)); } return true; } } else { if ($this->has_route_fee_applied($order)) { $this->routeapp_remove_route_fee($order); $order->calculate_totals(); $order->save(); } } return false; } private function routeapp_create_fee($order, $fee_amount) { $routeapp_public = self::get_route_public_instance(); if (!$this->has_route_fee_applied($order)) { $item_fee = new WC_Order_Item_Fee(); $item_fee->set_name( Routeapp_Public::ROUTE_LABEL ); $item_fee->set_tax_class( $routeapp_public->routeapp_get_taxable_class() ); $item_fee->set_tax_status( $routeapp_public->routeapp_get_fee_taxable() ? 'taxable' : 'none' ); $item_fee->set_amount( $fee_amount ); $item_fee->set_total( $fee_amount ); Routeapp_Public::routeapp_mark_protection_fee_item( $item_fee ); $order->add_item( $item_fee ); } else { $fees = $this->get_route_fees($order); foreach($fees as $fee_id => $fee) { if ($fee) { $fee->set_amount($fee_amount); $fee->set_total($fee_amount); Routeapp_Public::routeapp_mark_protection_fee_item( $fee ); $fee->save(); } } } } public function routeapp_add_admin_fee() { check_ajax_referer( 'order-item', 'security' ); if (!isset($_POST) || !$_POST['order_id']) { wp_send_json_error('Missing order_id in the request.'); return; } $order_id = isset($_POST['order_id']) ? $_POST['order_id'] : 0; $order = wc_get_order( $order_id ); if (!$order) { wp_send_json_error('Invalid order_id.'); return; } $response = $this->routeapp_handle_admin_fee($order); if ($response) { wp_send_json_success( 'order updated!' ); return; } wp_send_json_error('error while adding route fee to the order'); } function routeapp_remove_admin_fee() { check_ajax_referer( 'order-item', 'security' ); if (!isset($_POST) || !$_POST['order_id']) { wp_send_json_error('Missing order_id in the request.'); return; } $order_id = isset($_POST['order_id']) ? $_POST['order_id'] : 0; $order = wc_get_order( $order_id ); if (!$order) { wp_send_json_error('Invalid order_id.'); return; } if ($this->has_route_fee_applied($order)) { $this->routeapp_remove_route_fee($order); $order->calculate_totals(); $order->save(); } wp_send_json_success('route fee removed from the order'); } private function routeapp_remove_route_fee($order) { $fees = $this->get_route_fees($order); foreach($fees as $fee_id => $fee) { if ($fee) { wc_delete_order_item($fee_id); } } if ( class_exists('Automattic\WooCommerce\Utilities\OrderUtil') && OrderUtil::custom_orders_table_usage_is_enabled() ) { $order->delete_meta_data(self::ROUTE_CHARGE_POST_META); $order->delete_meta_data(self::ROUTE_PROTECTION_POST_META); } else { delete_post_meta( $order->get_id(), self::ROUTE_CHARGE_POST_META ); delete_post_meta( $order->get_id(), self::ROUTE_PROTECTION_POST_META ); } } public function recalculate_route_fee($order_id) { $order = wc_get_order( $order_id ); if ($this->has_route_fee_applied($order)) { $this->routeapp_handle_admin_fee($order); } } private function get_route_fees($order) { if (!$order) { return []; } $fees = $order->get_fees(); $route_fees = []; foreach($fees as $fee_id => $fee) { if (Routeapp_Public::routeapp_is_route_protection_fee($fee)) { $route_fees[$fee_id] = $fee; } } return $route_fees; } public function can_show_route_fee_buttons($order) { return $order && count($order->get_items()) > 0 && is_admin() && in_array($order->get_status(), array('pending', 'on-hold', 'auto-draft'), true); } public function can_add_fee($order) { return $this->can_show_route_fee_buttons($order); } private function has_route_fee_applied($order) { $routeFees = $this->get_route_fees($order); if (is_array($routeFees) || $routeFees instanceof Countable) { return count($routeFees) > 0; } return false; } }