JezK
Edit File: Hooks.php
<?php namespace WeDevs\Dokan\Order\Admin; use Exception; use WC_Data_Store; use WC_Order; use WeDevs\Dokan\Commission\OrderCommission; use WeDevs\Dokan\Utilities\OrderUtil; use WP_Post; // don't call the file directly if ( ! defined( 'ABSPATH' ) ) { exit; } /** * Order admin related hooks * * @since 3.8.0 moved functionality from includes/Admin/Hooks.php file */ class Hooks { /** * Class constructor * * @since 3.8.0 */ public function __construct() { // Load all actions and filters if ( OrderUtil::is_hpos_enabled() ) { // hpos equivalent hooks for `manage_shop_order_posts_custom_column` add_action( 'manage_woocommerce_page_wc-orders_custom_column', [ $this, 'shop_order_custom_columns' ], 11, 2 ); // hpos equivalent hooks for admin_footer-edit.php add_action( 'admin_footer-woocommerce_page_wc-orders', [ $this, 'admin_shop_order_scripts' ] ); // hpos equivalent hooks for restrict_manage_posts add_action( 'woocommerce_order_list_table_restrict_manage_orders', [ $this, 'admin_shop_order_toggle_sub_orders' ], 10, 1 ); // hpos equivalent hooks for manage_edit-shop_order_columns add_filter( 'manage_woocommerce_page_wc-orders_columns', [ $this, 'admin_shop_order_edit_columns' ], 11 ); add_action( 'woocommerce_trash_order', [ $this, 'admin_on_trash_order' ] ); add_action( 'woocommerce_untrash_order', [ $this, 'admin_on_untrash_order' ] ); add_action( 'woocommerce_delete_order', [ $this, 'admin_on_delete_order' ] ); // HPOS equivalent hooks for filtering orders. add_filter( 'woocommerce_order_list_table_prepare_items_query_args', [ $this, 'filter_orders_by_order_type_query' ] ); add_action( 'woocommerce_order_list_table_restrict_manage_orders', [ $this, 'render_order_type_filter_dropdown' ] ); } else { add_action( 'manage_shop_order_posts_custom_column', [ $this, 'shop_order_custom_columns' ], 11, 2 ); add_action( 'admin_footer-edit.php', [ $this, 'admin_shop_order_scripts' ] ); add_action( 'restrict_manage_posts', [ $this, 'admin_shop_order_toggle_sub_orders' ], 10, 1 ); add_filter( 'manage_edit-shop_order_columns', [ $this, 'admin_shop_order_edit_columns' ], 11 ); add_action( 'wp_trash_post', [ $this, 'admin_on_trash_order' ] ); add_action( 'untrash_post', [ $this, 'admin_on_untrash_order' ] ); add_action( 'delete_post', [ $this, 'admin_on_delete_order_post' ] ); // Legacy equivalent hooks for filtering orders. add_filter( 'request', [ $this, 'filter_orders_by_order_type_query' ] ); add_action( 'restrict_manage_posts', [ $this, 'render_order_type_filter_dropdown' ] ); } // Change order meta key and value. add_filter( 'woocommerce_order_item_display_meta_key', [ $this, 'change_order_item_display_meta_key' ] ); add_filter( 'woocommerce_order_item_display_meta_value', [ $this, 'change_order_item_display_meta_value' ], 10, 2 ); add_filter( 'woocommerce_reports_get_order_report_query', [ $this, 'admin_order_reports_remove_parents' ] ); add_filter( 'post_class', [ $this, 'admin_shop_order_row_classes' ], 10, 3 ); // Add commission meta-box in order details page. add_action( 'add_meta_boxes', [ $this, 'add_commission_metabox_and_related_orders_in_order_details_page' ], 10, 2 ); } /** * Remove child orders from WC reports * * @since 3.8.0 Moved from includes/Admin/Hooks.php file * * @param array $query * * @return array */ public function admin_order_reports_remove_parents( $query ) { $query['where'] .= ' AND posts.post_parent = 0'; return $query; } /** * Change the columns shown in admin. * * @since 3.8.0 Moved from includes/Admin/Hooks.php file * @since 3.8.0 Rewritten for HPOS * * @param array $existing_columns * * @return array */ public function admin_shop_order_edit_columns( $existing_columns ) { // Remove seller, suborder column if seller is viewing his own product if ( ! current_user_can( 'manage_woocommerce' ) || ( ! empty( $_GET['author'] ) ) ) { // phpcs:ignore return $existing_columns; } $column_to_insert = [ 'admin_commission' => __( 'Commission', 'dokan-lite' ), 'seller' => __( 'Vendor', 'dokan-lite' ), 'wc_actions' => __( 'Actions', 'dokan-lite' ), 'suborder' => __( 'Sub Order', 'dokan-lite' ), ]; $columns = dokan_array_insert_after( $existing_columns, $column_to_insert ); return apply_filters( 'dokan_edit_shop_order_columns', $columns ); } /** * Adds custom column on dokan admin shop order table * * @since 3.8.0 Moved from includes/Admin/Hooks.php file * @since 3.8.0 Rewritten for HPOS * * @param string $col * @param int|WC_Order $post_id * * @return void */ public function shop_order_custom_columns( $col, $post_id ) { // return if user doesn't have access if ( ! current_user_can( 'manage_woocommerce' ) ) { return; } // check if post_id is a order if ( ! OrderUtil::is_order( $post_id ) ) { return; } if ( ! in_array( $col, [ 'order_number', 'suborder', 'seller', 'admin_commission' ], true ) ) { return; } $order = wc_get_order( $post_id ); $output = ''; switch ( $col ) { case 'order_number': if ( $order->get_parent_id() !== 0 ) { $output = '<strong>'; $output .= esc_html__( ' Sub Order of', 'dokan-lite' ); $output .= sprintf( ' <a href="%s">#%s</a>', esc_url( OrderUtil::get_admin_order_edit_url( $order->get_parent_id() ) ), esc_html( $order->get_parent_id() ) ); $output .= '</strong>'; } break; case 'suborder': if ( '1' === $order->get_meta( 'has_sub_order', true ) ) { $output = sprintf( '<button type="button" href="#" class="show-sub-orders button" data-class="parent-%1$d" data-show="%2$s" data-hide="%3$s">%2$s</button>', esc_attr( $order->get_id() ), esc_attr__( 'Show Sub-Orders', 'dokan-lite' ), esc_attr__( 'Hide Sub-Orders', 'dokan-lite' ) ); } elseif ( OrderUtil::is_hpos_enabled() && 0 !== $order->get_parent_id() ) { $output = sprintf( '<span style="display:block;" class="sub-order parent-%s"> </span>', $order->get_parent_id() ); } break; case 'seller': $has_sub = $order->get_meta( 'has_sub_order', true ); $seller = get_user_by( 'id', dokan_get_seller_id_by_order( $order->get_id() ) ); if ( $has_sub !== '1' && $seller ) { $output = sprintf( '<a href="%s">%s</a>', esc_url( OrderUtil::get_admin_order_list_url() . '&vendor_id=' . $seller->ID ), esc_html( $seller->display_name ) ); } else { $output = esc_html__( '(no name)', 'dokan-lite' ); } break; case 'admin_commission': if ( '1' === $order->get_meta( 'has_sub_order', true ) ) { $output = '--'; } else { try { $order_commission = dokan_get_container()->get( OrderCommission::class ); $order_commission->set_order( $order ); $order_commission->get(); $commission = $order_commission->get_admin_commission(); } catch ( Exception $e ) { $commission = 0; } /** * In case of refund, we are not excluding gateway fee; in case of stripe full/partial refund net amount can be negative */ if ( $commission < 0 ) { $commission = 0; } $output = wc_price( $commission ); } break; } if ( ! empty( $output ) ) { echo wp_kses_post( apply_filters( "dokan_manage_shop_order_custom_columns_{$col}", $output, $order ) ); } } /** * Adds css classes on admin shop order table * * @since 3.8.0 Moved from includes/Admin/Hooks.php file * @since 3.8.0 Rewritten for HPOS * * @param string[] $classes An array of post class names. * @param string[] $css_class An array of additional class names added to the post. * @param int $post_id The post ID. * * @global WP_Post $post * * @return array */ public function admin_shop_order_row_classes( $classes, $css_class, $post_id ) { if ( ! OrderUtil::is_order( $post_id ) ) { return $classes; } if ( ! is_admin() || ! current_user_can( 'manage_woocommerce' ) ) { return $classes; } $vendor_id = isset( $_GET['vendor_id'] ) ? absint( wp_unslash( $_GET['vendor_id'] ) ) : 0; // phpcs:ignore if ( $vendor_id ) { return $classes; } $order = wc_get_order( $post_id ); if ( ! $order ) { return $classes; } if ( $order->get_parent_id() !== 0 ) { $classes[] = 'sub-order parent-' . $order->get_parent_id(); } return $classes; } /** * Show/hide sub order css/js * * @since 3.8.0 Moved from includes/Admin/Hooks.php file * @since 3.8.0 Added HPOS support * * @return void */ public function admin_shop_order_scripts() { $current_screen = get_current_screen(); if ( ! $current_screen ) { return; } if ( ( 'edit' === $current_screen->base && 'shop_order' !== $current_screen->post_type ) && OrderUtil::get_order_admin_screen() !== $current_screen->base ) { return; } ?> <script type="text/javascript"> jQuery(function ($) { <?php if ( OrderUtil::is_hpos_enabled() ) : ?> // hpos implementation fix_row_css_styles(); toggle_suborders(); $('button.show-sub-orders').on('click', function (e) { e.preventDefault(); const self = $(this); const elms = self.parents().find(`tr>td>span.${self.data('class')}`); if (!elms.length) { return; } elms.each(function (index) { if ('none' === this.parentElement.parentElement.style.display) { self.text(self.data('hide')); this.parentElement.parentElement.style.display = ''; } else { self.text(self.data('show')); this.parentElement.parentElement.style.display = 'none'; } }); }); $('button.toggle-sub-orders').on('click', function (e) { e.preventDefault(); if (this.classList.contains('hide')) { toggle_suborders(); this.classList.remove('hide'); this.classList.add('show'); } else { toggle_suborders(false); this.classList.remove('show'); this.classList.add('hide'); } }); function toggle_suborders(hide = true) { const self = $(this); const elms = $('table.orders td.suborder>span.sub-order'); if (!elms.length) { return; } const urlParams = new URLSearchParams(window.location.search); // Hide the toggle button if the only suborders are visible. if ( 'sub_order' === urlParams.get('dokan_order_filter') ) { $('button.toggle-sub-orders').hide(); return; } if ( urlParams.get('s') || urlParams.get('vendor_id') ) { return; } elms.each(function (index) { if (hide) { this.parentElement.parentElement.style.display = 'none'; } else { this.parentElement.parentElement.style.display = ''; } }); } function fix_row_css_styles() { const self = $(this); const elms = $('table.orders td.suborder>span.sub-order'); if (!elms.length) { return; } elms.each(function (index) { this.parentElement.parentElement.bgColor = '#ECFFF2'; }); } <?php else : ?> const urlParams = new URLSearchParams(window.location.search); // Hide the toggle button if the only suborders are visible. if ( 'sub_order' !== urlParams.get('dokan_order_filter') ) { $('tr.sub-order').hide(); } else { $('button.toggle-sub-orders').hide(); } $('button.show-sub-orders').on('click', function (e) { e.preventDefault(); let $self = $(this), el = $('tr.' + $self.data('class')); if (!el.length) { return; } if (el.is(':hidden')) { el.show(); $self.text($self.data('hide')); } else { el.hide(); $self.text($self.data('show')); } }); $('button.toggle-sub-orders').on('click', function (e) { e.preventDefault(); $('tr.sub-order').toggle(); }); <?php endif; ?> }); </script> <style> tr.sub-order { background: #ECFFF2; } th#order_number { width: 21ch; } th#order_date { width: 9ch; } th#order_status { width: 12ch; } th#shipping_address { width: 18ch; } th#wc_actions { width: 9ch; } th#seller { width: 6ch; } th#suborder { width: 9ch; } </style> <?php } /** * Change order item display meta key. * * @since 3.8.0 * @since 3.8.0 Moved this method from Order/Hooks.php file * * @param string $display_key * * @return string */ public function change_order_item_display_meta_key( $display_key ) { if ( 'seller_id' === $display_key ) { return __( 'Vendor', 'dokan-lite' ); } return $display_key; } /** * Change order item display meta value. * * @since 3.8.0 * @since 3.8.0 Moved this method from Order/Hooks.php file * * @param string $display_value * @param object $meta * * @return string */ public function change_order_item_display_meta_value( $display_value, $meta ) { if ( 'seller_id' === $meta->key ) { $vendor = dokan()->vendor->get( $display_value ); $url = get_edit_user_link( $display_value ); if ( function_exists( 'dokan_pro' ) ) { $url = admin_url( 'admin.php?page=dokan#/vendors/' . $display_value ); } return '<a href=' . esc_url( $url ) . " '>" . $vendor->get_shop_name() . '</a>'; } return $display_value; } /** * Delete sub orders when parent order is trashed * * @since 3.8.0 Moved from includes/Admin/Hooks.php file * @since 3.8.0 Rewritten for HPOS * * @param int $post_id */ public function admin_on_trash_order( $post_id ) { if ( ! OrderUtil::is_order( $post_id ) ) { return; } $order = wc_get_order( $post_id ); if ( ! $order || $order->get_parent_id() !== 0 ) { return; } $child_orders = dokan()->order->get_child_orders( $order->get_id() ); foreach ( $child_orders as $child_order ) { $child_order->delete( false ); } } /** * Un-trash sub orders when parent orders are un-trashed * * @since 3.8.0 Moved from includes/Admin/Hooks.php file * @since 3.8.0 Rewritten for HPOS * * @param int $post_id * * @return void */ public function admin_on_untrash_order( $post_id ) { if ( ! OrderUtil::is_order( $post_id ) ) { return; } $order = wc_get_order( $post_id ); if ( ! $order || $order->get_parent_id() !== 0 ) { return; } $child_orders = dokan()->order->get_child_orders( $order->get_id(), [ 'status' => [ 'trash' ], ] ); if ( ! $child_orders ) { return; } try { $order_data_store = WC_Data_Store::load( 'order' ); foreach ( $child_orders as $child_order ) { if ( method_exists( $order_data_store->get_current_class_name(), 'untrash_order' ) ) { $order_data_store->untrash_order( $child_order ); } else { wp_untrash_post( $child_order->get_id() ); } } } catch ( Exception $e ) { return; } } /** * Delete sub orders and from dokan sync table when a order is deleted * * @since 3.8.0 Moved from includes/Admin/Hooks.php file * @since 3.8.0 Rewritten for HPOS * * @param int $post_id * * @return void */ public function admin_on_delete_order( $post_id ) { dokan()->order->delete_seller_order_with_suborders( $post_id ); } /** * Delete sub orders and from dokan sync table when a order is deleted * * @since 3.8.0 Moved from includes/Admin/Hooks.php file * @since 3.8.0 Rewritten for HPOS * * @param int $post_id * * @return void */ public function admin_on_delete_order_post( $post_id ) { $order = wc_get_order( $post_id ); if ( ! $order ) { return; } dokan()->order->delete_seller_order_with_suborders( $post_id ); } /** * Show a toggle button to toggle all the sub orders * * @since 3.8.0 Moved from includes/Admin/Hooks.php file * @since 3.8.0 Rewritten for HPOS * * @param string $typenow * * @return void */ public function admin_shop_order_toggle_sub_orders( $typenow ) { if ( $typenow === 'shop_order' ) { echo '<button class="toggle-sub-orders button show">' . esc_html__( 'Toggle Sub-orders', 'dokan-lite' ) . '</button>'; } } /** * Render the order type filter dropdown. * * @since 4.2.1 * * @param string $typenow * * @return void */ public function render_order_type_filter_dropdown( $typenow ) { if ( $typenow !== 'shop_order' ) { return; } // Get the current filter value $current_filter = isset( $_GET['dokan_order_filter'] ) ? sanitize_text_field( wp_unslash( $_GET['dokan_order_filter'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended // Product type filter options. $filter_options = [ '' => esc_html__( 'All Order Types', 'dokan-lite' ), 'sub_order' => esc_html__( 'Vendor Sub-orders', 'dokan-lite' ), ]; /** * Filter to add additional order type filter options. * Pro modules or others can use this hook to add their own options. * * @since 4.2.1 * * @param array $filter_options Array of filter options */ $filter_options = apply_filters( 'dokan_order_type_filter_options', $filter_options ); // Render the order filter template. dokan_get_template_part( 'orders/order-type-filter', '', [ 'filter_options' => $filter_options, 'current_filter' => $current_filter, ] ); } /** * Filter orders by order type for both HPOS and legacy. * * @since 4.2.1 * * @param array $query_args Query arguments (HPOS) or query vars (legacy) * * @return array */ public function filter_orders_by_order_type_query( $query_args ) { $screen = get_current_screen(); $post_type = $screen->post_type ?? ''; // Return early if the current screen is not order screen. $filter_type = sanitize_text_field( wp_unslash( $_GET['dokan_order_filter'] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( 'shop_order' !== $post_type || empty( $filter_type ) ) { return $query_args; } // Render only suborder products if filter types are set to 'sub_order'. $is_hpos_enabled = OrderUtil::is_hpos_enabled(); if ( 'sub_order' === $filter_type ) { $query_type = $is_hpos_enabled ? 'parent_exclude' : 'post_parent__not_in'; // Vendor Suborders (Child orders only - orders with parent_id > 0). $query_args[ $query_type ] = [ 0 ]; } /** * Allow Pro modules to handle their custom filter types. * * @since 4.2.1 * * @param array|null $query_args Modified or original query arguments. * @param string $filter_type The selected filter type * @param array $is_hpos_enabled HPOS status. */ return apply_filters( 'dokan_order_type_filter_query_args', $query_args, $filter_type, $is_hpos_enabled ); } /** * Add dokan commission meta-box in woocommerce order details page * and add suborders or related sibling orders in meta-box. * * @since 3.14.0 * * @return void */ public function add_commission_metabox_and_related_orders_in_order_details_page( $post_type, $post ) { $screen = OrderUtil::get_order_admin_screen(); if ( $screen !== $post_type ) { return; } $order = dokan()->order->get( OrderUtil::get_post_or_order_id( $post ) ); $has_sub_order = '1' === $order->get_meta( 'has_sub_order', true ); $show_commission_meta_box = apply_filters( 'dokan_show_commission_meta_box', ! $has_sub_order, $order ); // Check if the screen is order details page and if it is a child order. if ( $show_commission_meta_box ) { add_meta_box( 'dokan_commission_box', __( 'Commissions', 'dokan-lite' ), [ $this, 'commission_meta_box' ], $screen, 'normal', 'core' ); } $show_related_order_meta_box = apply_filters( 'dokan_show_related_order_meta_box', $has_sub_order || ! empty( $order->get_parent_id() ), $order ); // If the order has is a parent order or a child order, avoid those order that has no parent order or child order. if ( $show_related_order_meta_box ) { $title = $has_sub_order ? __( 'Sub orders', 'dokan-lite' ) : __( 'Related orders', 'dokan-lite' ); add_meta_box( 'dokan_sub_or_related_orders', $title, [ $this, 'sub_or_related_orders_meta_box' ], $screen, 'normal', 'core' ); } } /** * Dokan order commission meta-box body. * * @since 3.14.0 * * @param WP_Post|WC_Order $post_or_order * * @return void */ public function commission_meta_box( $post_or_order ) { $order = dokan()->order->get( OrderUtil::get_post_or_order_id( $post_or_order ) ); $all_commission_types = array_merge( dokan_commission_types(), dokan()->commission->get_legacy_commission_types() ); try { $order_commission = dokan_get_container()->get( OrderCommission::class ); $order_commission->set_order( $order ); $order_commission->get(); } catch ( \Exception $exception ) { dokan_log( 'Dokan can not calculate commission on commission meta box : ' . $exception->getMessage() ); return; } dokan_get_template_part( 'orders/commission-meta-box-html', '', [ 'order' => $order, 'all_commission_types' => $all_commission_types, 'order_commission' => $order_commission, ] ); wp_enqueue_style( 'dokan-commission-meta-box', DOKAN_PLUGIN_ASSEST . '/css/dokan-admin-commission-suborder-metabox.css', [], DOKAN_PLUGIN_VERSION ); } /** * Content of suborder or related order meta-box. * * @param $post_or_order * * @return void */ public function sub_or_related_orders_meta_box( $post_or_order ) { $order = dokan()->order->get( OrderUtil::get_post_or_order_id( $post_or_order ) ); $parent_order = new WC_Order(); $has_sub_order = '1' === $order->get_meta( 'has_sub_order', true ); if ( $has_sub_order ) { $orders_to_render = dokan()->order->get_child_orders( $order->get_id() ); } else { $orders_to_render = dokan()->order->all( [ 'parent' => $order->get_parent_id(), 'limit' => -1, 'type' => 'shop_order', ] ); $parent_order = dokan()->order->get( $order->get_parent_id() ); $orders_to_render = array_filter( $orders_to_render, function ( $item ) use ( $order ) { return $item->get_id() !== $order->get_id(); } ); array_unshift( $orders_to_render, $parent_order ); } dokan_get_template_part( 'orders/sub-order-related-order-meta-box-html', '', array( 'order' => $order, 'parent_order' => $parent_order, 'has_sub_order' => $has_sub_order, 'orders_to_render' => $orders_to_render, ) ); } }