woocommerce-advanced-ordernumbers.php 13.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
/**
 * Plugin Name: Advanced Ordernumbers
 * Plugin URI: http://open-tools.net/WooCommerce/Advanced-Ordernumbers.php
 * Description: Lets the user freely configure the order numbers in WooCommerce.
 * Version: 0.1.0
 * Author: Open Tools, Reinhold Kainhofer
 * Author URI: http://open-tools.net
 * Text Domain: woocommerce-advanced-ordernumbers
 * Domain Path: 
 * License: GPL2+
 WC requires at least: 2.2
WC tested up to: 2.3
*/

/**
17
 * The structure of this plugin originally followed the tutorial, although much of the plugin has been rewritten since then:
18
19
20
21
22
23
24
25
26
27
28
29
 * http://www.yaconiello.com/blog/how-to-write-wordpress-plugin/
 */
 
if ( ! defined( 'ABSPATH' ) ) { 
	exit; // Exit if accessed directly
}
/**
 * Check if WooCommerce is active
 **/
if ( in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
    // Put your plugin code here
    // Load the language files
30
	load_plugin_textdomain('opentools-ordernumbers', false, basename( dirname( __FILE__ ) ) . '/languages' );
31
32
33

	if (!class_exists("OpenToolsOrdernumbers")) {
		class OpenToolsOrdernumbers {
34
			public $ordernumber_meta = "_order_number";
35
			
36
37
38
39
40
			/**
			 * Construct the plugin object
			 */
			public function __construct()
			{
41
				$plugin = plugin_basename(__FILE__); 
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111


				// Init settings
				$this->settings = array(
					array(
						'name' 		=> __( 'Advanced Order Numbers', 'woocommerce-advanced-ordernumbers' ),
						'desc'		=> __( 'Configure the format and the counters of the order numbers in WooCommerce.', 'woocommerce-advanced-ordernumbers' ),
						'type' 		=> 'title',
						'id' 		=> 'ordernumber_options'
					),

					array(
						'name' 		=> __( 'Customize Order Numbers', 'woocommerce-advanced-ordernumbers' ),
						'desc' 		=> __( 'Check to use custom order numbers rather than the default wordpress post ID.', 'woocommerce-advanced-ordernumbers' ),
						'id' 		=> 'customize_ordernumber',
						'type' 		=> 'checkbox',
						'default'	=> 'no'
					),
					array(
						'title'		=> __( 'Order number format', 'woocommerce-advanced-ordernumbers' ),
						'desc' 		=> __( 'The format for the order numbers (variables can be entered as [...], the counter is indicated by the #). To use a different counter name than displayed, put the custom counter name after a |, e.g. "[year]-[month]/#|[year]" to use the month in the order number, but reset the counter only yearly.', 'woocommerce-advanced-ordernumbers' ),
						'desc_tip'	=> true,
						'id' 		=> 'ordernumber_format',
						'default'	=> '#',
						'type' 		=> 'text',
					),
					array(
						'title'		=> __( 'Global', 'woocommerce-advanced-ordernumbers' ),
						'desc' 		=> __( 'Counter Scope', 'woocommerce-advanced-ordernumbers' ),
						'id' 		=> 'ordernumber_global',
						'type' 		=> 'checkbox',
						'default'	=> 'no'
					),
					array(
						'title' 		=> __( 'Counter Digits', 'woocommerce-advanced-ordernumbers' ),
						'desc' 		=> __( 'Minimum number of digits for the number', 'woocommerce-advanced-ordernumbers' ),
						'desc_tip'	=> true,
						'id' 		=> 'ordernumber_padding',
						'type' 		=> 'number',
						'default'	=> '0'
					),
					array(
						'title' 	=> __( 'Counter Start', 'woocommerce-advanced-ordernumbers' ),
						'desc' 		=> __( 'Start value for each new counter', 'woocommerce-advanced-ordernumbers' ),
						'desc_tip'	=> true,
						'id' 		=> 'ordernumber_start',
						'type' 		=> 'number',
						'default'	=> '1'
					),
					array(
						'title'	=> __( 'Counter step', 'woocommerce-advanced-ordernumbers' ),
						'desc' 		=> __( 'By how much the counter will be increased after each order. Typically 1.', 'woocommerce-advanced-ordernumbers' ),
						'desc_tip'	=> true,
						'id' 		=> 'ordernumber_step',
						'type' 		=> 'number',
						'default'	=> '1'
					),
					array( 'type' => 'sectionend', 'id' => 'ordernumber_options' ),
					
					// TODO: customize order password, and other numbers!
				);
				// Default options

				add_option ('customize_ordernumber', '0');
				add_option ('ordernumber_format',    "#");
				add_option ('ordernumber_global',    '0');
				add_option ('ordernumber_padding',   '1');
				add_option ('ordernumber_start',     '1');
				add_option ('ordernumber_step',      '1');

112
				// register actions
113
114
115
				add_filter( 'woocommerce_get_sections_checkout', array($this, 'add_admin_section'));
				// Add the ordernumber post meta to the search in the backend
				add_filter( 'woocommerce_shop_order_search_fields', array($this, 'order_search_fields'));
116
117
118
				// The checkout page assumes all subpages are payment gateways, so we have to override this:
				add_action( 'woocommerce_settings_checkout', array( $this, 'output' ) );
				add_action( 'woocommerce_settings_save_checkout', array( $this, 'save' ) );
119
120
121
				// Sort the order list in the backend by order number rather than ID, make sure this is called LAST so we modify the defaults passed as arguments
				add_filter( 'manage_edit-shop_order_sortable_columns', array( $this, 'modify_order_column_sortkey' ), 9999 );

122

123
124
125
				// register filters
				add_filter("plugin_action_links_$plugin", array(&$this, 'plugin_settings_link'));
				add_filter ('woocommerce_order_number', array(&$this, 'get_ordernumber'), 10, 2/*<= Also get the order object! */);
126
127
			}
    
128
129
			// Activate the plugin
			public static function activate() {}
130
    
131
132
133
134
135
136
137
138
139
140
141
142
143
144
			// Deactivate the plugin
			public static function deactivate() {}

			function add_admin_section($sections) {
				$sections['ordernumber'] = __( 'Order Numbers', 'woocommerce-advanced-ordernumbers');
				return $sections;
			}
			
			public function output() {
				global $current_section;
				if ($current_section == 'ordernumber') {
					$settings = $this->settings;
					WC_Admin_Settings::output_fields( $settings );
				}
145
146
			}

147
148
149
150
151
152
			public function save() {
				global $current_section;
				if ($current_section == 'ordernumber') {
					$settings = $this->settings;
					WC_Admin_Settings::save_fields( $settings );
				}
153
			}
154
155
156
157
158
159
160
161
162
			
			/** 
			 * Hook to add the order numer post meta field to the searchable field 
			 * (so the admin can search for the order number in the backend)
			 */
			public function order_search_fields($fields) {
				$fields[] = $this->ordernumber_meta;
				return $fields;
			}
163
164
165
166
167
168
169
170
171
172
173
			
			/**
			 * Sort the order list's "Order" column by our post meta rather than by ID
			 */
			public function modify_order_column_sortkey($columns) {
				$custom = array(
					'order_title' => $this->ordernumber_meta,
				);
				// Use the passed columns as "default", so effectively, only the order_title will be changed:
				return wp_parse_args ($custom, $columns);
			}
174

175
			/**
176
			 * Counter handling (simple loading/storing counters), storing them as options
177
178
179
180
181
182
183
184
			 */
			function _getCounter($type, $format, $start=1) {
				$count = get_option ('ordernumber-counter-'.$type.'-'.$format, $start);
				return $count;
			}
			// Insert new counter value into the db or update existing one
			function _setCounter($type, $format, $value) {
				return update_option('ordernumber-counter-'.$type.'-'.$format, $value);
185
186
			}
			
187
			
188
			/**
189
190
191
192
193
194
195
196
197
198
199
200
201
202
			 * Variable replacements:
			 *   - Random strings/digits
			 *   - Order properties
			 *   - User properties
			 */
			
			/* Return a random "string" of the given length taken from the given alphabet */
			static function randomString($alphabet, $len) {
				$alen = strlen($alphabet);
				$r = "";
				for ($n=0; $n<$len; $n++) {
					$r .= $alphabet[mt_rand(0, $alen-1)];
				}
				return $r;
203
204
			}

205
206
207
208
209
210
211
212
213
214
215
216
217
			function replaceRandom ($match) {
				/* the regexp matches (random)(Type)(Len) as match, Type and Len is optional */
				$len = ($match[3]?$match[3]:1);
				// Fallback: If no Type is given, use Digit
				$alphabet = "0123456789";
				// Select the correct alphabet depending on Type
				switch (strtolower($match[2])) {
					case "digit": $alphabet = "0123456789"; break;
					case "hex": $alphabet = "0123456789abcdef"; break;
					case "letter": $alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; break;
					case "uletter": $alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; break;
					case "lletter": $alphabet = "abcdefghijklmnopqrstuvwxyz"; break;
					case "alphanum": $alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; break;
218
				}
219
220
221
				return self::randomString ($alphabet, $len);
			}
    
222

223
			/* replace the variables in the given format. $type indicates the type of number, currently only 'ordernumber', because WooCommerce does not support invoices or customer numbers. We might allow the shop owner to customize the order password, though. */
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
			function replace_fields ($fmt, $type, $order) {
				// First, replace all randomXXX[n] fields. This needs to be done with a regexp and a callback:
				$fmt = preg_replace_callback ('/\[(random)(.*?)([0-9]*?)\]/', array($this, 'replaceRandom'), $fmt);
        
				$reps = array (
					"[year]" => date ("Y"),
					"[year2]" => date ("y"),
					"[month]" => date("m"),
					"[day]" => date("d"),
					"[hour]" => date("H"),
					"[hour12]" => date("h"),
					"[ampm]" => date("a"),
					"[minute]" => date("i"),
					"[second]" => date("s"),
					"[orderid]" => $order->id,
					"[userid]" => $order->get_user_id(),
				);
241

242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
				$reps["[ipaddress]"]   = $order->customer_ip_address;
				$reps["[orderstatus]"] = $order->get_status();
				$reps["[email]"]       = $order->billing_email;
        
				
				$reps["[firstname]"] = $order->billing_first_name;
				$reps["[lastname]"] = $order->billing_last_name;

				$reps["[company]"] = $order->billing_company;
				$reps["[zip]"] = $order->billing_postcode;
				$reps["[postcode]"] = $order->billing_postcode;
				$reps["[city]"] = $order->billing_city;
        
				$country = $order->billing_country;
				$state = $order->billing_state;
				$allcountries = WC()->countries->get_countries();
				$states = WC()->countries->get_states($country);
				$reps["[country]"] = $country;
				$reps["[countryname]"] = ( isset( $allcountries[ $country ] ) ) ? $allcountries[ $country ] : $country;

				$reps["[state]"] = $state;
				$reps["[statename]"] = ( $country && $state && isset( $states[ $country ][ $state ] ) ) ? $states[ $country ][ $state ] : $state;
				
				$reps["[articles]"] = $order->get_item_count();
				$reps["[currency]"] = $order->get_order_currency();
				$reps["[downloadpermitted]"] = $order->is_download_permitted();
				$reps["[hasdownloads]"] = $order->has_downloadable_item();
// 				$reps["[coupons]"] = $order->get_used_coupons();
				
				if ($type != 'ordernumber') {
					$reps["[ordernumber]"] = $order->get_order_number();
				}
				$user = $order->get_user();
				if ($user) {
//					TODO: Shall we supply a variable for the user's login / display name?
// 					$reps["[user]"] = print_r($user,1);
// 					if (isset($order->username)) $reps["[username]"] = $order->username;
				}
				// Allow customization via plugins: filter function($reps, $order, $type, $fmt)
				$reps = apply_filters( 'opentools_ordernumber_replacements', $reps, $order, $type, $fmt);
				return str_ireplace (array_keys($reps), array_values($reps), $fmt);
283
			}
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306

			function create_ordernumber($orderid, $order, $type='ordernumber') {
				if (get_option('customize_'.$type, 'false')) {
					$fmt     = get_option ($type.'_format',  "#");
					$global  = get_option ($type.'_global',  1);
					$padding = get_option ($type.'_padding', 1);
					$step    = get_option ($type.'_step',    1);
					$start   = get_option ($type.'_start',   1)-$step; // The counter contains the PREVIOUS number!

					$nr = $this->replace_fields ($fmt, $type, $order);

					// Split at a | to get the number format and a possibly different counter increment format
					// If a separate counter format is given after the |, use it, otherwise reuse the number format itself as counter format
					$parts = explode ("|", $nr);
					$format = $parts[0];
        
					$counterfmt = ($global==1)?"":$parts[(count($parts)>1)?1:0];
        
					// Look up the current counter
					$count = $this->_getCounter($type, $counterfmt, $start) + $step;
					$this->_setCounter($type, $counterfmt, $count);
					// return the format with the counter inserted
					$number = str_replace ("#", sprintf('%0' . $padding . 's', $count), $format);
307
					update_post_meta( $orderid, $this->ordernumber_meta, $number );
308
309
310
311
312
313
					return $number;
				} else {
					return $orderid;
				}
			}
	
314
315
316
317
			/** 
			 * The hook to customize order numbers (requests the order number from the database; creates a new ordernumber if no entry exists in the database)
			 */
			function get_ordernumber($orderid, $order) {
318
				$stored_number = get_post_meta( $orderid, $this->ordernumber_meta, 'true');
319
320
				if (!empty($stored_number)) {
					return $stored_number;
321
				} elseif (get_option('customize_ordernumber', 'false')) {
322
					// create a new one (if this is a new order, otherwise we don't have/generate a number!)
323
324
					// TODO: Check whether this is a new number! Otherwise, do NOT create an order number
					$number = $this->create_ordernumber($orderid, $order, 'ordernumber');
325
					return $number;
326
327
328
				} else {
					print("<h1>Order number creation is DISABLED</h1>");
					return $orderid;
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
				}
			}

		}
	}

	if (class_exists("OpenToolsOrdernumbers")) {
		// Installation and uninstallation hooks
		register_activation_hook(__FILE__, array('OpenToolsOrdernumbers', 'activate'));
		register_deactivation_hook(__FILE__, array('OpenToolsOrdernumbers', 'deactivate'));

		// instantiate the plugin class
		$ordernumber_plugin = new OpenToolsOrdernumbers();
	}
 
}