first commit

This commit is contained in:
2026-03-05 13:07:40 +01:00
commit 64ba0721ee
25709 changed files with 4691006 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
# Copyright (C) 2023 SkyVerge
# This file is distributed under the GNU General Public License v3.0.
msgid ""
msgstr ""
"Project-Id-Version: Sequential Order Numbers for WooCommerce 1.10.1\n"
"Report-Msgid-Bugs-To: "
"https://woocommerce.com/my-account/marketplace-ticket-form/\n"
"POT-Creation-Date: 2023-09-05 03:53:24+00:00\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"PO-Revision-Date: 2023-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
#: woocommerce-sequential-order-numbers.php:136
#. translators: Placeholders: %s - plugin name
msgid "You cannot clone instances of %s."
msgstr ""
#: woocommerce-sequential-order-numbers.php:148
#. translators: Placeholders: %s - plugin name
msgid "You cannot unserialize instances of %s."
msgstr ""
#: woocommerce-sequential-order-numbers.php:678
#. translators: Placeholders: %1$s - plugin name; %2$s - WooCommerce version;
#. %3$s, %5$s - <a> tags; %4$s - </a> tag
msgid ""
"%1$s is inactive because it requires WooCommerce %2$s or newer. Please "
"%3$supdate WooCommerce%4$s or run the %5$sWooCommerce database upgrade%4$s."
msgstr ""
#: woocommerce-sequential-order-numbers.php:728
msgid ""
"Error activating and installing <strong>Sequential Order Numbers for "
"WooCommerce</strong>: %s"
msgstr ""
#: woocommerce-sequential-order-numbers.php:730
msgid "&laquo; Go Back"
msgstr ""
#. Plugin Name of the plugin/theme
msgid "Sequential Order Numbers for WooCommerce"
msgstr ""
#. Plugin URI of the plugin/theme
msgid "http://www.skyverge.com/blog/woocommerce-sequential-order-numbers/"
msgstr ""
#. Description of the plugin/theme
msgid "Provides sequential order numbers for WooCommerce orders"
msgstr ""
#. Author of the plugin/theme
msgid "SkyVerge"
msgstr ""
#. Author URI of the plugin/theme
msgid "http://www.skyverge.com"
msgstr ""

View File

@@ -0,0 +1,232 @@
=== Sequential Order Numbers for WooCommerce ===
Contributors: SkyVerge, maxrice, tamarazuk, chasewiseman, nekojira, beka.rice
Tags: woocommerce, order number, sequential order number, woocommerce orders
Requires at least: 5.6
Tested up to: 6.3.1
Requires PHP: 7.4
Stable tag: 1.10.1
This plugin extends WooCommerce by setting sequential order numbers for new orders.
== Description ==
This plugin extends WooCommerce by automatically setting sequential order numbers for new orders. If there are existing orders at the time of installation, the sequential order numbers will start with the highest current order number.
**This plugin requires WooCommerce 3.9.4 or newer.**
> No configuration needed! The plugin is so easy to use, there aren't even any settings. Activate it, and orders will automatically become sequential.
If you have no orders in your store, your orders will begin counting from order number 1. If you have existing orders, the count will pick up from your highest order number.
If you've placed test orders, you must trash **and** permanently delete them to begin ordering at "1" (trashed orders have to be counted in case they're restored, so they need to be gone completely).
= Support Details =
We do support our free plugins and extensions, but please understand that support for premium products takes priority. We typically check the forums every few days (usually with a maximum delay of one week).
= Sequential Order Numbers Pro =
If you like this plugin, but are looking for the ability to set the starting number, or to add a custom prefix/suffix to your order numbers (ie, you'd prefer something like WT101UK, WT102UK, etc) please consider our premium Sequential Order Numbers Pro for WooCommerce plugin, which is available in the [WooCommerce Store](http://woocommerce.com/products/sequential-order-numbers-pro/).
= More Details =
- See the [product page](http://www.skyverge.com/product/woocommerce-sequential-order-numbers/) for full details.
- Check out the [Pro Version](http://woocommerce.com/products/sequential-order-numbers-pro/).
- View more of SkyVerge's [free WooCommerce extensions](http://profiles.wordpress.org/skyverge/)
- View all [SkyVerge WooCommerce extensions](http://www.skyverge.com/shop/)
Interested in contributing? You can [find the project on GitHub](https://github.com/skyverge/woocommerce-sequential-order-numbers) and contributions are welcome :)
== Installation ==
You can install the plugin in a few ways:
1. Upload the entire 'woocommerce-sequential-order-numbers' folder to the '/wp-content/plugins/' directory
2. Upload the zip file you download via Plugins &gt; Add New
3. Go to Plugins &gt; Add New and search for "Sequential Order Numbers for WooCommerce", and install the one from SkyVerge.
Once you've installed the plugin, to get started please:
1. Activate the plugin through the "Plugins" menu in WordPress.
2. No configuration needed! Order numbers will continue sequentially from the current highest order number, or from 1 if no orders have been placed yet.
== Frequently Asked Questions ==
= Where are the settings? =
The plugin doesn't require any :) When you activate it, it gets to work right away! Orders will automatically become sequential, starting from the most recent order number.
= Why doesn't my payment gateway use this number? =
For full compatibility with extensions which alter the order number, such as Sequential Order Numbers, WooCommerce extensions should use `$order->get_order_number();` rather than `$order->id` when referencing the order number.
If your extension is not displaying the correct order number, you can try contacting the developers of your payment gateway to see if it's possible to make this tiny change. Using the order number instead is both compatible with WooCommerce core and our plugin, as without the order number being changed, it will be equal to the order ID.
= Can I start the order numbers at a particular number? =
This free version does not have that functionality, but the premium [Sequential Order Numbers Pro for WooCommerce](http://www.woothemes.com/products/sequential-order-numbers-pro/) will allow you to choose any starting number that's higher than your most current order number.
= Can I start the order numbers at "1"? =
If you want to begin numbering at "1", you must trash, then permanently delete all orders in your store so that there are no order numbers already being counted.
= Can I set an order number prefix/suffix? =
This free version does not have that functionality, but it's included in the premium [Sequential Order Numbers Pro for WooCommerce](http://www.woothemes.com/products/sequential-order-numbers-pro/).
== Other Notes ==
If you'd like to make your payment gateway compatible with Sequential Order Numbers, or other plugins that filter the order number, please make one small change. Instead of referencing `$order->id` when storing order data, reference: `$order->get_order_number()`
This is compatible with WooCommerce core by default, as the order number is typically equal to the order ID. However, this will also let you be compatible with plugins such as ours, as the order number can be filtered (which is what we do to make it sequential), so using order number is preferred.
Some other notes to help developers:
= Get an order from order number =
If you want to access the order based on the sequential order number, you can do so with a helper method:
`
$order_id = wc_sequential_order_numbers()->find_order_by_order_number( $order_number );
`
This will give you the order's ID (post ID), and you can get the order object from this.
= Get the order number =
If you have access to the order ID or order object, you can easily get the sequential order number based on WooCommerce core functions.
`
$order = wc_get_order( $order_id );
$order_number = $order->get_order_number();
`
== Changelog ==
- 2023.09.05 - version 1.10.1 =
* Fix - Call save order method only in HPOS installs to avoid setting the same order number meta twice in CPT installations
- 2023.08.02 - version 1.10.0 =
* Tweak - Also set sequential order numbers for orders sent via the WooCommerce Checkout Block
* Misc - Add compatibility for WooCommerce High Performance Order Storage (HPOS)
* Misc - Require PHP 7.4 and WordPress 5.6
= 2022.07.30 - version 1.9.7 =
* Misc - Rename to Sequential Order Numbers for WooCommerce
= 2022.03.01 - version 1.9.6 =
* Misc - Require WooCommerce 3.9.4 or newer
* Misc - Replace calls to deprecated `is_ajax()` with `wp_doing_ajax()`
= 2020.05.07 - version 1.9.5 =
* Misc - Add support for WooCommerce 4.1
= 2020.03.10 - version 1.9.4 =
* Misc - Add support for WooCommerce 4.0
= 2020.02.05 - version 1.9.3 =
* Misc - Add support for WooCommerce 3.9
= 2019.11.05 - version 1.9.2 =
* Misc - Add support for WooCommerce 3.8
= 2019.10.03 - version 1.9.1 =
* Fix - Fix order number filter in WooCommerce Admin Downloads Analytics
= 2019.08.15 - version 1.9.0 =
* Misc - Add support for WooCommerce 3.7
* Misc - Remove support for WooCommerce 2.6
= 2018.07.17 - version 1.8.3 =
* Misc - Require WooCommerce 2.6.14+ and WordPress 4.4+
= 1.8.2 - 2017.08.22 =
* Fix - PHP deprecation warning when Subscriptions is used
* Misc - Removed support for WooCommerce Subscriptions older than v2.0
= 1.8.1 - 2017.03.28 =
* Fix - Removes errors on refund number display
= 1.8.0 - 2017.03.23 =
* Fix - Admin orderby was not properly scoped to orders, props [@brandondove](https://github.com/brandondove)
* Misc - Added support for WooCommerce 3.0
* Misc - Removed support for WooCommerce 2.4
= 1.7.0 - 2016.05.24 =
* Misc - Added support for WooCommerce 2.6
* Misc - Removed support for WooCommerce 2.3
= 1.6.1 - 2016.02.04 =
* Misc - WooCommerce Subscriptions: Use new hook wcs_renewal_order_meta_query instead of deprecated woocommerce_subscriptions_renewal_order_meta_query
= 1.6.0 - 2016.01.20 =
* Misc - WooCommerce Subscriptions: Use new filter hook wcs_renewal_order_created instead of deprecated woocommerce_subscriptions_renewal_order_created
* Misc - WooCommerce 2.5 compatibility
* Misc - Dropped WooCommerce 2.2 support
= 1.5.1 - 2015.11.26 =
* Fix - Compatibility fix with WooCommerce Subscriptions 2.0
= 1.5.0 - 2015.07.28 =
* Misc - WooCommerce 2.4 Compatibility
= 1.4.0 - 2015.02.10 =
* Fix - Improved install routine for shops with a large number of orders
* Misc - WooCommerce 2.3 compatibility
= 1.3.4 - 2014.09.23 =
* Fix - Compatibility fix with WooCommerce 2.1
* Fix - Fix a deprecated notice in WooCommerce 2.2
= 1.3.3 - 2014.09.05 =
* Localization - Included a .pot file for localization
= 1.3.2 - 2014.09.02 =
* Misc - WooCommerce 2.2 compatibility
= 1.3.1 - 2014.01.22 =
* Misc - WooCommerce 2.1 compatibility
= 1.3 - 2013.04.26 =
* Feature - Improved WooCommerce Subscriptions compatibility
* Feature - Improved WooCommerce Pre-Orders compatibility
* General code cleanup and refactor
= 1.2.4 - 2012.12.14 =
* Fix - WordPress 3.5 compatibility fix
* Fix - Order numbers not assigned to temporary auto-draft orders created from the admin
= 1.2.3 - 2012.06.06 =
* Fix - Removed WooCommerce functions, which caused a compatibility issue with other WooCommerce plugins
= 1.2.2 - 2012.05.25 =
* Tweak - Takes advantage of new action hooks/filters available in WooCommerce 1.5.6
* Fix - Bug fix on installation to stores with more than 10 existing orders
= 1.2.1 - 2012.05.13 =
* Tweak - Minor updates due to WooCommerce 1.5.5 release
= 1.2.0 - 2012.04.21 =
* Feature - Added support for the order tracking page
= 1.1.2 - 2012.04.18 =
* Tweak - Minor updates due to WooCommerce 1.5.4 release
= 1.1.1 - 2012.04.02 =
* Fix - Order number in the subject line of the admin new order email is fixed
= 1.1.0 - 2012.04.02 =
* Feature - Search by order number
= 1.0.1 - 2012.04.02 =
* Fix - small bug fix
= 1.0.0 - 2012.04.02 =
* Initial Release
== Upgrade Notice ==
= 1.2.2 - 2012.05.25 =
This version requires WooCommerce 1.5.6
= 1.2.1 - 2012.05.13 =
This version requires WooCommerce 1.5.5

View File

@@ -0,0 +1,790 @@
<?php
/**
* Plugin Name: Sequential Order Numbers for WooCommerce
* Plugin URI: http://www.skyverge.com/blog/woocommerce-sequential-order-numbers/
* Description: Provides sequential order numbers for WooCommerce orders
* Author: SkyVerge
* Author URI: http://www.skyverge.com
* Version: 1.10.1
* Text Domain: woocommerce-sequential-order-numbers
* Domain Path: /i18n/languages/
*
* Copyright: (c) 2012-2023, SkyVerge, Inc. (info@skyverge.com)
*
* License: GNU General Public License v3.0
* License URI: http://www.gnu.org/licenses/gpl-3.0.html
*
* @author SkyVerge
* @copyright Copyright (c) 2012-2023, SkyVerge, Inc. (info@skyverge.com)
* @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
*
* WC requires at least: 3.9.4
* WC tested up to: 8.0.3
*/
defined( 'ABSPATH' ) or exit;
// Check if WooCommerce is active
if ( ! WC_Seq_Order_Number::is_plugin_active( 'woocommerce.php' ) ) {
return;
}
class WC_Seq_Order_Number {
/** version number */
const VERSION = '1.10.1';
/** minimum required wc version */
const MINIMUM_WC_VERSION = '3.9.4';
/** @var \WC_Seq_Order_Number single instance of this plugin */
protected static $instance;
/** version option name */
const VERSION_OPTION_NAME = 'woocommerce_seq_order_number_db_version';
/**
* Construct the plugin
*
* @since 1.3.2
*/
public function __construct() {
add_action( 'plugins_loaded', [ $this, 'initialize' ] );
add_action( 'init', [ $this, 'load_translation' ] );
// handle HPOS compatibility
add_action( 'before_woocommerce_init', [ $this, 'handle_hpos_compatibility' ] );
}
/**
* Declares HPOS compatibility.
*
* @since 1.10.0
*
* @internal
*
* @return void
*/
public function handle_hpos_compatibility()
{
if ( class_exists(\Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) {
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', plugin_basename( __FILE__ ), true );
}
}
/**
* Determines whether HPOS is in use.
*
* @since 1.10.0
*
* @return bool
*/
protected function is_hpos_enabled() {
return class_exists( \Automattic\WooCommerce\Utilities\OrderUtil::class )
&& \Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled();
}
/**
* Determines if the current screen is the orders screen.
*
* @since 1.10.0
*
* @return bool
*/
protected function is_orders_screen() {
$current_screen = function_exists( 'get_current_screen') ? get_current_screen() : null;
if ( ! $current_screen ) {
return false;
}
$using_hpos = $this->is_hpos_enabled();
if ( ! $using_hpos ) {
return 'edit-shop_order' === $current_screen->id;
}
if ( is_callable( \Automattic\WooCommerce\Utilities\OrderUtil::class . '::get_order_admin_screen' ) ) {
$orders_screen_id = \Automattic\WooCommerce\Utilities\OrderUtil::get_order_admin_screen();
} else {
$orders_screen_id = function_exists( 'wc_get_page_screen_id' ) ? wc_get_page_screen_id( 'shop-order' ) : null;
}
return $orders_screen_id === $current_screen->id
&& isset( $_GET['page'] )
&& $_GET['page'] === 'wc-orders'
&& ( ! isset( $_GET['action'] ) || ! in_array( $_GET['action'], [ 'new', 'edit' ], true ) );
}
/**
* Cloning instances is forbidden due to singleton pattern.
*
* @since 1.7.0
*/
public function __clone() {
/* translators: Placeholders: %s - plugin name */
_doing_it_wrong( __FUNCTION__, sprintf( esc_html__( 'You cannot clone instances of %s.', 'woocommerce-sequential-order-numbers' ), 'Sequential Order Numbers for WooCommerce' ), '1.7.0' );
}
/**
* Unserializing instances is forbidden due to singleton pattern.
*
* @since 1.7.0
*/
public function __wakeup() {
/* translators: Placeholders: %s - plugin name */
_doing_it_wrong( __FUNCTION__, sprintf( esc_html__( 'You cannot unserialize instances of %s.', 'woocommerce-sequential-order-numbers' ), 'Sequential Order Numbers for WooCommerce' ), '1.7.0' );
}
/**
* Initialize the plugin.
*
* Prevents loading if any required conditions are not met, including minimum WooCommerce version.
*
* @internal
*
* @since 1.3.2
*/
public function initialize() {
if ( ! $this->minimum_wc_version_met() ) {
// halt functionality
return;
}
// set the custom order number on the new order
if ( ! $this->is_hpos_enabled() ) {
add_action( 'wp_insert_post', [ $this, 'set_sequential_order_number' ], 10, 2 );
} else {
add_action( 'woocommerce_checkout_update_order_meta', [ $this, 'set_sequential_order_number' ], 10, 2 );
add_action( 'woocommerce_process_shop_order_meta', [ $this, 'set_sequential_order_number' ], 35, 2 );
add_action( 'woocommerce_before_resend_order_emails', [ $this, 'set_sequential_order_number' ] );
}
// set the custom order number on WooCommerce Checkout Block submissions
add_action( 'woocommerce_store_api_checkout_update_order_meta', [ $this, 'set_sequential_order_number' ], 10, 2 );
// return our custom order number for display
add_filter( 'woocommerce_order_number', array( $this, 'get_order_number' ), 10, 2 );
// order tracking page search by order number
add_filter( 'woocommerce_shortcode_order_tracking_order_id', array( $this, 'find_order_by_order_number' ) );
// WC Subscriptions support
add_filter( 'wc_subscriptions_renewal_order_data', [ $this, 'subscriptions_remove_renewal_order_meta' ] );
add_filter( 'wcs_renewal_order_created', [ $this, 'subscriptions_set_sequential_order_number' ], 10, 2 );
// WooCommerce Admin support
if ( class_exists( 'Automattic\WooCommerce\Admin\Install', false ) || class_exists( 'WC_Admin_Install', false ) ) {
add_filter( 'woocommerce_rest_orders_prepare_object_query', array( $this, 'wc_admin_order_number_api_param' ), 10, 2 );
}
if ( is_admin() ) {
if ( $this->is_hpos_enabled() ) {
/** @see \Automattic\WooCommerce\Internal\Admin\Orders\ListTable::prepare_items() */
add_filter( 'woocommerce_shop_order_list_table_request', [ $this, 'woocommerce_custom_shop_order_orderby' ], 20 );
} else {
add_filter( 'request', [ $this, 'woocommerce_custom_shop_order_orderby' ], 20 );
}
// ensure that admin order table search by order number works
add_filter( 'woocommerce_shop_order_search_fields', [ $this, 'custom_search_fields' ] );
add_filter( 'woocommerce_order_table_search_query_meta_keys', [ $this, 'custom_search_fields'] );
// sort by underlying _order_number on the Pre-Orders table
add_filter( 'wc_pre_orders_edit_pre_orders_request', array( $this, 'custom_orderby' ) );
add_filter( 'wc_pre_orders_search_fields', array( $this, 'custom_search_fields' ) );
}
// Installation
if ( is_admin() && ! wp_doing_ajax() ) {
add_action( 'admin_init', [ $this, 'install' ] );
}
}
/**
* Loads translations.
*
* @internal
*
* @since 1.3.3
*/
public function load_translation() {
// localization
load_plugin_textdomain( 'woocommerce-sequential-order-numbers', false, dirname( plugin_basename( __FILE__ ) ) . '/i18n/languages' );
}
/**
* Search for an order having a given order number.
*
* @since 1.0.0
*
* @param string $order_number order number to search for
* @return int $order_id for the order identified by $order_number, or 0
*/
public function find_order_by_order_number( $order_number ) {
// search for the order by custom order number
if ( $this->is_hpos_enabled() ) {
$orders = wc_get_orders([
'return' => 'ids',
'limit' => 1,
'meta_query' => [
[
'key' => '_order_number',
'value' => $order_number,
'comparison' => '='
],
],
]);
} else {
$orders = get_posts( [
'numberposts' => 1,
'meta_key' => '_order_number',
'meta_value' => $order_number,
'post_type' => 'shop_order',
'post_status' => 'any',
'fields' => 'ids',
] );
}
$order_id = $orders ? current($orders) : null;
// order was found
if ( $order_id !== null ) {
return (int) $order_id;
}
// if we didn't find the order, then it may be that this plugin was disabled and an order was placed in the interim
$order = wc_get_order( $order_number );
if ( ! $order ) {
return 0;
}
// _order_number was set, so this is not an old order, it's a new one that just happened to have an order ID that matched the searched-for order_number
if ( $order->get_meta( '_order_number', true, 'edit' ) ) {
return 0;
}
return $order->get_id();
}
/**
* Set the `_order_number` field for the newly created order according to HPOS usage.
*
* @internal
*
* @since 1.0.0
*
* @param int|\WC_Order $order_id order identifier or order object
* @param \WP_Post|\WC_Order|array<string, mixed>|null $object $object order or post object or post data (depending on HPOS and hook in use)
*/
public function set_sequential_order_number( $order_id = null, $object = null ) {
global $wpdb;
$using_hpos = $this->is_hpos_enabled();
if ( $object instanceof \WP_Post ) {
$is_order = 'shop_order' === $object->post_type;
$order = $is_order ? wc_get_order( $object->ID ) : null;
$order_id = $object->ID;
$order_status = $object->post_status;
} else {
$order = $object instanceof \WC_Order ? $object : wc_get_order( (int) $order_id );
$is_order = $order instanceof \WC_Order && 'shop_order' === $order->get_type();
$order_id = ! $order_id && $order ? $order->get_id() : (int) $order_id;
$order_status = $order ? $order->get_status() : '';
if ( $is_order && $order_status !== 'auto-draft' && isset( $_GET['action'] ) && $_GET['action'] === 'new' ) {
$order_status = 'auto-draft';
}
}
// when creating an order from the admin don't create order numbers for auto-draft orders,
// because these are not linked to from the admin and so difficult to delete when CPT tables are used
if ( $is_order && ( $using_hpos || 'auto-draft' !== $order_status ) ) {
if ( $using_hpos ) {
$order_number = $order ? $order->get_meta( '_order_number' ) : '';
} else {
$order_number = get_post_meta( $order_id, '_order_number', true );
}
// if no order number has been assigned, create one
if ( empty( $order_number ) ) {
// attempt the query up to 3 times for a much higher success rate if it fails (to avoid deadlocks)
$success = false;
$order_meta_table = $using_hpos ? $wpdb->prefix . 'wc_orders_meta' : $wpdb->postmeta;
$order_id_column = $using_hpos ? 'order_id' : 'post_id';
for ( $i = 0; $i < 3 && ! $success; $i++ ) {
$success = $wpdb->query( $wpdb->prepare( "
INSERT INTO {$order_meta_table} ({$order_id_column}, meta_key, meta_value)
SELECT %d, '_order_number', IF( MAX( CAST( meta_value as UNSIGNED ) ) IS NULL, 1, MAX( CAST( meta_value as UNSIGNED ) ) + 1 )
FROM {$order_meta_table}
WHERE meta_key='_order_number'
", (int) $order_id ) );
}
// with HPOS we need to trigger a save to update the order number,
// or it won't persist by using the direct query above alone
if ( $using_hpos ) {
$order->save();
}
}
}
}
/**
* Filters to return our _order_number field rather than the order ID, for display.
*
* @since 1.0.0
*
* @param string $order_number the order id with a leading hash
* @param \WC_Order $order the order object
* @return string custom order number
*/
public function get_order_number( $order_number, $order ) {
// don't display an order number for subscription objects
if ( $order instanceof \WC_Subscription ) {
return $order_number;
}
if ( $sequential_order_number = $order->get_meta( '_order_number', true, 'edit' ) ) {
$order_number = $sequential_order_number;
}
return $order_number;
}
/** Admin filters ******************************************************/
/**
* Admin order table orderby ID operates on our meta `_order_number`.
*
* @internal
*
* @since 1.3
*
* @param array $vars associative array of orderby parameters
* @return array associative array of orderby parameters
*/
public function woocommerce_custom_shop_order_orderby( $vars ) {
global $typenow;
if ( ! is_array( $vars ) ) {
return $vars;
}
if ( ! $this->is_hpos_enabled() ) {
if ( 'shop_order' !== $typenow ) {
return $vars;
}
} elseif ( ! $this->is_orders_screen() ) {
return $vars;
}
return $this->custom_orderby( $vars );
}
/**
* Modifies the given $args argument to sort on our` _order_number` meta.
*
* @internal
*
* @since 1.3
*
* @param array $args associative array of orderby parameters
* @return array associative array of orderby parameters
*/
public function custom_orderby( $args ) {
// sorting
if ( isset( $args['orderby'] ) && 'ID' == $args['orderby'] ) {
$args = array_merge( $args, [
'meta_key' => '_order_number', // sort on numerical portion for better results
'orderby' => 'meta_value_num',
] );
}
return $args;
}
/**
* Add our custom `_order_number` to the set of search fields so that the admin search functionality is maintained.
*
* @internal
*
* @since 1.0.0
*
* @param string[] $search_fields array of order meta fields to search by
* @return string[] of order meta fields to search by
*/
public function custom_search_fields( $search_fields ) {
return array_merge( (array) $search_fields, [ '_order_number' ] );
}
/** 3rd Party Plugin Support ******************************************************/
/**
* Sets an order number on a subscriptions-created order.
*
* @since 1.3
*
* @internal
*
* @param \WC_Order $renewal_order the new renewal order object
* @param \WC_Subscription $subscription ID of a 'shop_subscription' object, or instance of a WC_Subscription object
* @return \WC_Order renewal order instance
*/
public function subscriptions_set_sequential_order_number( $renewal_order, $subscription ) {
if ( $renewal_order instanceof \WC_Order ) {
$order = wc_get_order( $renewal_order->get_id() );
if ( $order ) {
$this->set_sequential_order_number( $order->get_id(), $order );
}
}
return $renewal_order;
}
/**
* Don't copy over order number meta when creating a parent or child renewal order
*
* Prevents unnecessary order meta from polluting parent renewal orders, and set order number for subscription orders.
*
* @since 1.3
*
* @internal
*
* @param string[]|mixed $order_data
* @return string[]mixed
*/
public function subscriptions_remove_renewal_order_meta( $order_data ) {
if ( ! is_array( $order_data ) ) {
return $order_data;
}
unset( $order_data['_order_number'] );
return $order_data;
}
/**
* Hook WooCommerce Admin order number search to the meta value.
*
* @since 1.3
*
* @internal
*
* @param array $args Arguments to be passed to WC_Order_Query.
* @param WP_REST_Request $request REST API request being made.
* @return array Arguments to be passed to WC_Order_Query.
*/
public function wc_admin_order_number_api_param( $args, $request ) {
global $wpdb;
if ( '/wc/v4/orders' === $request->get_route() && isset( $request['number'] ) ) {
// Handles 'number' value here and modify $args.
$number_search = trim( $request['number'] );
$order_sql = esc_sql( $args['order'] ); // Order defaults to DESC.
$limit = intval( $args['posts_per_page'] ); // Posts per page defaults to 10.
$using_hpos = $this->is_hpos_enabled();
$order_meta_table = $using_hpos ? $wpdb->prefix . 'wc_orders_meta' : $wpdb->postmeta;
$order_id_column = $using_hpos ? 'order_id' : 'post_id';
// Search Order number meta value instead of Post ID.
$order_ids = $wpdb->get_col(
$wpdb->prepare( "
SELECT {$order_id_column}
FROM {$order_meta_table}
WHERE meta_key = '_order_number'
AND meta_value LIKE %s
ORDER BY {$order_id_column} {$order_sql}
LIMIT %d
", $wpdb->esc_like( $number_search ) . '%', $limit )
);
if ( $using_hpos ) {
$args['order__in'] = empty( $order_ids ) ? array( 0 ) : $order_ids;
} else {
$args['post__in'] = empty( $order_ids ) ? array( 0 ) : $order_ids;
}
// Remove the 'number' parameter to short circuit WooCommerce Admin's handling.
unset( $request['number'] );
}
return $args;
}
/** Helper Methods ******************************************************/
/**
* Main Sequential Order Numbers Instance, ensures only one instance is/can be loaded.
*
* @see wc_sequential_order_numbers()
*
* @since 1.7.0
*
* @return \WC_Seq_Order_Number
*/
public static function instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Helper function to determine whether a plugin is active.
*
* @since 1.8.3
*
* @param string $plugin_name plugin name, as the plugin-filename.php
* @return boolean true if the named plugin is installed and active
*/
public static function is_plugin_active( $plugin_name ) {
$active_plugins = (array) get_option( 'active_plugins', array() );
if ( is_multisite() ) {
$active_plugins = array_merge( $active_plugins, array_keys( get_site_option( 'active_sitewide_plugins', array() ) ) );
}
$plugin_filenames = array();
foreach ( $active_plugins as $plugin ) {
if ( false !== strpos( $plugin, '/' ) ) {
// normal plugin name (plugin-dir/plugin-filename.php)
list( , $filename ) = explode( '/', $plugin );
} else {
// no directory, just plugin file
$filename = $plugin;
}
$plugin_filenames[] = $filename;
}
return in_array( $plugin_name, $plugin_filenames );
}
/** Compatibility Methods ******************************************************/
/**
* Helper method to get the version of the currently installed WooCommerce.
*
* @since 1.3.2
*
* @return string woocommerce version number or null
*/
private static function get_wc_version() {
return defined( 'WC_VERSION' ) && WC_VERSION ? WC_VERSION : null;
}
/**
* Performs a minimum WooCommerce version check.
*
* @since 1.3.2
*
* @return bool
*/
private function minimum_wc_version_met() {
$version_met = true;
// if a plugin defines a minimum WC version, render a notice and skip loading the plugin
if ( defined( 'self::MINIMUM_WC_VERSION' ) && version_compare( self::get_wc_version(), self::MINIMUM_WC_VERSION, '<' ) ) {
if ( is_admin() && ! wp_doing_ajax() && ! has_action( 'admin_notices', array( $this, 'render_update_notices' ) ) ) {
add_action( 'admin_notices', array( $this, 'render_update_notices' ) );
}
$version_met = false;
}
return $version_met;
}
/**
* Renders a notice to update WooCommerce if needed
*
* @internal
*
* @since 1.3.2
*/
public function render_update_notices() {
$message = sprintf(
/* translators: Placeholders: %1$s - plugin name; %2$s - WooCommerce version; %3$s, %5$s - <a> tags; %4$s - </a> tag */
esc_html__( '%1$s is inactive because it requires WooCommerce %2$s or newer. Please %3$supdate WooCommerce%4$s or run the %5$sWooCommerce database upgrade%4$s.', 'woocommerce-sequential-order-numbers' ),
'Sequential Order Numbers',
self::MINIMUM_WC_VERSION,
'<a href="' . admin_url( 'update-core.php' ) . '">',
'</a>',
'<a href="' . admin_url( 'plugins.php?do_update_woocommerce=true' ) . '">'
);
printf( '<div class="error"><p>%s</p></div>', $message );
}
/** Lifecycle methods ******************************************************/
/**
* Run every time. Used since the activation hook is not executed when updating a plugin
*
* @internal
*
* @since 1.0.0
*/
public function install() {
$installed_version = get_option( WC_Seq_Order_Number::VERSION_OPTION_NAME );
if ( ! $installed_version ) {
// initial install, set the order number for all existing orders to the order id:
// page through the "publish" orders in blocks to avoid out of memory errors
$offset = (int) get_option( 'wc_sequential_order_numbers_install_offset', 0 );
$orders_par_page = 500;
do {
// initial install, set the order number for all existing orders to the order id
$orders = wc_get_orders( [
'type' => 'shop_order',
'offset' => $offset,
'limit' => $orders_par_page
] );
// some sort of bad database error: deactivate the plugin and display an error
if ( is_wp_error( $orders ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
deactivate_plugins( 'woocommerce-sequential-order-numbers/woocommerce-sequential-order-numbers.php' ); // hardcode the plugin path so that we can use symlinks in development
wp_die(
sprintf(
/** translators: Placeholder: %s - error message(s) */
__( 'Error activating and installing <strong>Sequential Order Numbers for WooCommerce</strong>: %s', 'woocommerce-sequential-order-numbers' ),
'<ul><li>' . implode( '</li><li>', $orders->get_error_messages() ) . '</li></ul>'
) . '<a href="' . admin_url( 'plugins.php' ) . '">' . __( '&laquo; Go Back', 'woocommerce-sequential-order-numbers' ) . '</a>'
);
} elseif ( is_array( $orders ) ) {
foreach( $orders as $order ) {
if ( '' === $order->get_meta( '_order_number', true ) ) {
$order->add_meta_data('_order_number', (string) $order->get_id() );
$order->save_meta_data();
}
}
}
// increment offset
$offset += $orders_par_page;
// and keep track of how far we made it in case we hit a script timeout
update_option( 'wc_sequential_order_numbers_install_offset', $offset );
} while ( count( $orders ) === $orders_par_page ); // while full set of results returned (meaning there may be more results still to retrieve)
}
if ( $installed_version !== WC_Seq_Order_Number::VERSION ) {
$this->upgrade( $installed_version );
// new version number
update_option( WC_Seq_Order_Number::VERSION_OPTION_NAME, WC_Seq_Order_Number::VERSION );
}
}
/**
* Runs when plugin version number changes.
*
* 1.0.0
*
* @param string $installed_version
*/
private function upgrade( $installed_version ) {
// upgrade code goes here
}
}
/**
* Returns the One True Instance of Sequential Order Numbers
*
* @since 1.7.0
*
* @return \WC_Seq_Order_Number
*/
function wc_sequential_order_numbers() {
return WC_Seq_Order_Number::instance();
}
// fire it up!
wc_sequential_order_numbers();