4

In My WooCommerce website I am using this plugin WooCommerce Order Cancel for Customers which allow customers to cancel their order based on payment type and on a time delay. But the cancel button only appears on the "woocommerce_order_details_after_order_table".

I would like this cancel button to appear in the "My account" > "Orders list" near the "View" button: enter image description here

I have tried to edit the plugin and adding this code: add_filter('woocommerce_my_account_my_orders_actions', array($this, 'order_cancel_button'), 10, 2);

But it doesn't work: the View button is gone and the Cancel button is not displayed.

Here is the whole code:

<?php
/**
 * Plugin Name: WooCommerce Order Cancel For Customers
 * Plugin URI: https://wpmanageninja.com/plugins/order-cancel-for-customers-woocommerce
 * Description: A tiny plugin that will enable customers to cancel woocommerce order within a certain amount of time.
 * Version: 1.0
 * Author: Techjewel
 * Author URI: https://wpmanageninja.com
 * Requires at least: 4.4
 * Tested up to: 4.8
 *
 * Text Domain: wco
 *
 */


// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
    die;
}

require_once plugin_dir_path( __FILE__ ) . 'libs/class-wco-woo-settings.php';

if ( ! class_exists( 'WCOCancelOrder' ) ) :
    class WCOCancelOrder {

        /**
         * Eligible Order statuses for cancel
         * @type array
         */
        private $eligibleCancelStatuses;

        /**
         * New Order Status for Woocommerce Order, It must have wc prefix
         * @type string
         */
        private $custom_order_status_name = 'wc-customer-cancel';

        /**
         * Declare all the action and filter hooks 
         *
         */
        public function init_plugin() {



            add_filter("plugin_action_links_".plugin_basename(__FILE__), array($this, 'wco_plugin_settings_link') );


            $cancelOrderStatus = get_option('wc_wco_settings_activate');
            if($cancelOrderStatus != 'yes') {
                return;
            }

            $eligible_order_statuses = get_option('wc_wco_settings_eligible_statuses', array());

            $this->eligibleCancelStatuses = apply_filters(
                'wco_eligible_cancel_order_statuses', $eligible_order_statuses
            );

            $this->register_custom_order_status();

            add_filter('wc_order_statuses', array($this,'custom_wc_order_statuses'));
            add_action('admin_head', array($this, 'cancel_order_font_icon'));
            add_action('woocommerce_order_details_after_order_table', array($this, 'order_cancel_button'), 10, 1);
            add_filter('woocommerce_my_account_my_orders_actions', array($this, 'order_cancel_button'), 10, 2);
            add_action('wp', array($this, 'process_cancel_order'));
            add_action('wco_after_order_cancel_action', array($this, 'send_email_notification_to_shop_admin'), 10, 2);
            add_filter('wco_notification_email_subject', array($this, 'parse_text_with_order_fields'), 10, 2);
            add_filter('wco_notification_email_body', array($this, 'parse_text_with_order_fields'), 10, 2);
        }

        public function wco_plugin_settings_link($links) { 
              $settings_link = '<a href="admin.php?page=wc-settings&tab=wco_settings">'.__('Settings', 'wco').'</a>'; 
              array_unshift($links, $settings_link); 
              return $links; 
        }


        /**
         * Register New order status for Woocommerce
         *
         * @uses register_post_status()
         */
        public function register_custom_order_status()
        {
            register_post_status( $this->custom_order_status_name, array(
                'label'                     => __('Cancelled By Customer', 'wco'),
                'public'                    => true,
                'show_in_admin_status_list' => true,
                'show_in_admin_all_list'    => true,
                'exclude_from_search'       => false,
                'label_count'               => _n_noop( 'Cancelled By Customer <span class="count">(%s)</span>', 'Cancelled By Customers <span class="count">(%s)</span>' )
            ) );
        }

        /**
         * Append newly registered order-status in woocommerce status lists
         *
         * @param array $order_statuses
         * @return array $new_order_statuses
         */
        public function custom_wc_order_statuses($order_statuses)
        {
            $new_order_statuses = array();

            foreach ( $order_statuses as $key => $status ) {

                $new_order_statuses[ $key ] = $status;

                if ( 'wc-cancelled' === $key ) {
                    $new_order_statuses[$this->custom_order_status_name] = __('Cancelled By Customer', 'wco');
                }
            }

            return $new_order_statuses;
        }

        /**
         * Add order cancel button
         * 
         * @uses $this->can_customer_cancel_order($order)
         * @param $order
         */
        public function order_cancel_button($order) {
            $can_cancel = apply_filters('wco_can_customer_cancel_order', $this->can_customer_cancel_order($order), $order);
            if($can_cancel) {
                $cancel_url = $this->get_cancel_url($order->get_id());

                do_action('wco_before_cancel_button_wrapper', $order);      
        ?>
                <div class="ico_cancel_order_wrapper">

                    <p><i><?php _e('', 'wco'); ?></i> <a class="button" href="<?php echo $cancel_url; ?>"><?php  _e('CANCEL ORDER', 'wco');?></a></p>
                </div>
    <?php
                do_action('wco_after_cancel_button_wrapper', $order);
            }
        }

        public function get_cancel_url($order_id) {
            $urlData = build_query(array(
                'wco_order_cancel_id' => $order_id,
                '_nonce' => wp_create_nonce('wco_customer_cancel_order_' . $order_id),
                'action' => 'process_cancel_order'
            ));

            return site_url().'?'.$urlData;
        }

        /**
         * Process Cancel Order once user request to cancel
         */
        public function process_cancel_order() {
            if(isset($_REQUEST['wco_order_cancel_id'])) {
                $order_id = $_REQUEST['wco_order_cancel_id'];

                if(!wp_verify_nonce($_REQUEST['_nonce'], 'wco_customer_cancel_order_'.$order_id)) {
                    wp_die('Security Error, Please try again!', 'wco');
                }

                $order = wc_get_order($order_id);

                if($this->can_customer_cancel_order($order)) {

                    $redirectUrl = esc_url( wc_get_endpoint_url( get_option( 'woocommerce_myaccount_orders_endpoint', 'orders' ), '', wc_get_page_permalink( 'myaccount' ) ));

                    $redirectUrl = apply_filters('wco_after_cancel_redirect_url', $redirectUrl , $order_id);

                    // cancel the order now
                    $order_status_change_message = __('Customer wants to cancel the order and want to get refund.', 'wco');
                    $order_status_change_message = apply_filters('wco_after_order_cancel_note', $order_status_change_message, $order_id);

                    $success_message = get_option('wc_wco_settings_cancel_success_message', __('Your order has been submitted as "Cancelled by Customer"!', 'wco'));
                    $success_message = apply_filters('wco_after_order_cancel_message', $success_message, $order_id);

                    if(!wc_has_notice($success_message)) {
                        wc_add_notice($success_message, 'success');
                    }

                    $order = new WC_Order($order_id);
                    $order->update_status($this->custom_order_status_name, $order_status_change_message);
                    do_action('wco_after_order_cancel_action', $order, get_current_user_id());
                    wp_redirect($redirectUrl);
                    die();
                } else {
                    wp_die('Sorry! You can not cancel this order now!', 'wco');
                }
            }

        }

        /**
         * Determine if customer can cancel a selected order
         * 
         * @param $order
         *
         * @return bool
         */
        private function can_customer_cancel_order($order)
        {
            $cancelOrderStatus = get_option('wc_wco_settings_activate');
            if($cancelOrderStatus != 'yes') {
                return false;
            }

            $cancelTimeValidityMinutes = apply_filters('wco_cancel_validity_minutes', get_option('wc_wco_settings_cancel_order_threshold_time', 0), $order);
            $cancelTimeValidity = $cancelTimeValidityMinutes * 60; // in seconds

            $customer_id = $order->get_customer_id();
            $user_ID = get_current_user_id();

            $order_timestamp_diff = strtotime(current_time('mysql')) - strtotime($order->get_date_created());

            if ($cancelTimeValidity > $order_timestamp_diff && $customer_id == $user_ID && in_array($order->get_status(), $this->eligibleCancelStatuses) ) {
                return true;
            }
            return false;
        }

        /**
         * CSS for Cancel Order Icon
         */
        public function cancel_order_font_icon()
        {
            echo '<style>
                    mark.customer-cancel:after{
                        font-family:WooCommerce;
                        speak:none;
                        font-weight:400;
                        font-variant:normal;
                        text-transform:none;
                        line-height:1;
                        -webkit-font-smoothing:antialiased;
                        margin:0;
                        text-indent:0;
                        position:absolute;
                        top:0;
                        left:0;
                        width:100%;
                        height:100%;
                        text-align:center;
                    }

                    mark.customer-cancel:after{
                        content:"\e012";
                        color:#ff0000;
                    }
          </style>';
        }

        /**
         * Send email notification to admin once the customer cancel an order
         * 
         * @param $order
         * @param $user_id
         */
        public function send_email_notification_to_shop_admin($order, $user_id) {
            $email_to = apply_filters('wco_notification_email', get_option('wc_wco_settings_shop_owner_email'), $order);
            echo $email_to;

            if(!$email_to)
                return;

            $email_subject = apply_filters('wco_notification_email_subject', get_option('wc_wco_settings_notification_email_subject'), $order);

            $email_body = apply_filters('wco_notification_email_body', get_option('wc_wco_settings_notification_email_body'), $order);

            $email_headers = array(
                    'Content-Type: text/html; charset=UTF-8'
            );
            $email_headers = apply_filters('wco_notification_email_headers', $email_headers, $order);

            $mail_result = wp_mail($email_to, $email_subject, $email_body, $email_headers);

            do_action('wco_after_cancel_notification_email_sent_action', $mail_result, $order);
        }

        /**
         * Parse text with order shortcodes for email
         * @param $text
         * @param $order
         *
         * @return mixed
         */
        public function parse_text_with_order_fields($text, $order) {
            $replace_fields = array(
                '%%order_id%%' => $order->get_id(),
                '%%customer_name%%' => $order->get_billing_first_name().' '.$order->get_billing_last_name(), 
                '%%order_admin_url%%' => get_edit_post_link($order->get_id())
            );

            apply_filters('wco_parse_email_replace_fields', $replace_fields, $order);

            $replaces = array_keys($replace_fields);
            $replacesWith = array_values($replace_fields);

            $parsed_text = str_replace($replaces, $replacesWith, $text);
            return $parsed_text;
        }
    }
endif;

/**
 * Boot this plugin
 */
function wco_boot_plugin() {
    $cancelOrderClass = new WCOCancelOrder();
    $cancelOrderClass->init_plugin();

    if(is_admin()) {
        WCO_Woo_Settings::init();
    }
}
add_action('init', 'wco_boot_plugin');

How can I make the "Cancel" button appear in My account > Orders list, in the action column?

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
Rhob Gatchalian
  • 121
  • 1
  • 3
  • 7

2 Answers2

12

New Update May 2018: Conditional Cancel Button on my account orders list in Woocommerce


Old Update regarding your comment: The cancel button is available 3 days (from creation date) and solved a bug issue when the cancel button is pressed (made on January 2018)

Without editing or using this plugin, you can make enable the cancel button in My Account > Order list in the action column, using this little snippet of code where you will set:

  • The order statuses: where you want the "Cancel" button to appear
  • The duration (in days) from the order creation date.

Here is that code:

add_filter( 'woocommerce_valid_order_statuses_for_cancel', 'custom_valid_order_statuses_for_cancel', 10, 2 );
function custom_valid_order_statuses_for_cancel( $statuses, $order ){

    // Set HERE the order statuses where you want the cancel button to appear
    $custom_statuses    = array( 'completed', 'pending', 'processing', 'on-hold', 'failed' );

    // Set HERE the delay (in days)
    $duration = 3; // 3 days

    // UPDATE: Get the order ID and the WC_Order object
    if( isset($_GET['order_id']))
        $order = wc_get_order( absint( $_GET['order_id'] ) );

    $delay = $duration*24*60*60; // (duration in seconds)
    $date_created_time  = strtotime($order->get_date_created()); // Creation date time stamp
    $date_modified_time = strtotime($order->get_date_modified()); // Modified date time stamp
    $now = strtotime("now"); // Now  time stamp

    // Using Creation date time stamp
    if ( ( $date_created_time + $delay ) >= $now ) return $custom_statuses;
    else return $statuses;
}

Code goes in function.php file of your active child theme (or theme) or also in any plugin file.

This code is tested on Woocommerce 3+ and works. You will get something like:

enter image description here

Note: The default order statuses returned in this function are 'pending' and 'failed'

You can also use the WC_Order object argument in the function to set some custom conditions.

See this related thread: How to get WooCommerce order details

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
  • However I would like to use the plugin's function which makes the cancel button visible at set time like 3 days from the time the customer placed an order – Rhob Gatchalian Sep 17 '17 at 13:55
  • @RhobGatchalian this can be done without the plugin too… Making working with the plugin is much more complicated, long (so too broad) and not a professional way for developers (if you want to override plugin files). – LoicTheAztec Sep 17 '17 at 14:35
  • RhobGatchalian Update 2: with a time set to 3 days from creation date - corrected a typo error (a missing `;`). – LoicTheAztec Sep 17 '17 at 16:01
  • Gtting this error Uncaught ArgumentCountError: Too few arguments to function filter_woocommerce_valid_order_statuses_for_cancel(), 1 passed on pressing cancel button – Subhendu Pratap Singh Jan 18 '18 at 12:13
  • @subhendupsingh Humm yes you are right. I have updated the code… it should work for you now… – LoicTheAztec Jan 18 '18 at 12:54
  • @subhendupsingh For me it works perfectly now on WooCommerce version 3.2.x (last one). No errors on pressing cancel button, with my actual updated code… And it really cancel the order… – LoicTheAztec Jan 18 '18 at 14:10
  • 1
    Strange I am on wordpress 4.9.1 and woocommerce 3.2.6. It still shows error that wp-includes\class-wp-hook.php on line 286 expects 2 arguments but 1 is passed. – Subhendu Pratap Singh Jan 18 '18 at 14:40
  • @subhendupsingh See the new update link on the top for this answer… It should solve definitively the issue… – LoicTheAztec May 04 '18 at 00:21
  • Does this do anything with refunds? – Nathan Apr 07 '20 at 18:58
0

By default woocommerce show cancel order button for the order when its status is Pending or Failed only

But I made this plugin so you can show Order cancellation request button for order that are even under Processing state

https://wordpress.org/plugins/cancel-order-request-woocommerce/

You can set which order status to have "Cancellation request button" Order status on which the cancel request buton will be shown

Once you have set this user will have "cancel request" button on his My account order list, using those button user can send cancellation request with reason for cancellation Cancel request button shown on My account page of customer

Order cancellation reason in popup

rajesh singh
  • 11
  • 1
  • 3
  • 2
    Please consider improving this answer by [edit]ing in a *demonstration* of how your plugin can be used to solve this problem. For example, show some sample usage/code, describe any required setup, and then show a screenshot of the finished result. – Cody Gray - on strike Jul 26 '20 at 15:24