ordernumbers_woocommerce.php 18.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
<?php
/**
 * This is the actual ordernumber plugin class for WooCommerce.
 * Copyright (C) 2015 Reinhold Kainhofer, Open Tools
 * Author: Open Tools, Reinhold Kainhofer
 * Author URI: http://open-tools.net
 * License: GPL2+
*/
if ( ! defined( 'ABSPATH' ) ) { 
	exit; // Exit if accessed directly
}
12
13
if (!class_exists( 'OpenToolsOrdernumbersBasic' )) 
	require_once (dirname(__FILE__) . '/ordernumbers_woocommerce_basic.php');
14
	
15
class OpenToolsOrdernumbers extends OpenToolsOrdernumbersBasic {
16
17
18
	/**
	 * Construct the plugin object
	 */
19
20
21
22
	public function __construct($basename) {
		parent::__construct($basename);
		$this->is_advanced = true;

23
24
25
26
27
28
		$this->helper->registerCallback('setupStoreReplacements',		array($this, 'setupStoreReplacements'));
		$this->helper->registerCallback('setupOrderReplacements',		array($this, 'setupOrderReplacements'));
		$this->helper->registerCallback('setupUserReplacements',		array($this, 'setupUserReplacements'));
		$this->helper->registerCallback('setupShippingReplacements',	array($this, 'setupShippingReplacements'));
		$this->helper->registerCallback('setupThirdPartyReplacements',	array($this, 'setupThirdPartyReplacements'));
	}
29
30
31
32
33
	/**
	 * Override the initializeBasicSettings method, which restricts some functionality in the basic plugin.
	 */
	protected function initializeBasicSettings() {}
	
34
35
36
37
	/**
	 * Install all neccessary filters and actions for this plugin
	 */
	protected function initializeHooks() {
38
39
40
		parent::initializeHooks();
		
		// Custom table widget for custom variable definitions: Hooks for creating and storing values
41
42
43
		add_action( 'woocommerce_admin_field_ordernumber_variables',    array( $this, 'admin_field_variables' ) );
		add_action( 'pre_update_option_ordernumber_variables',          array( $this, 'update_option_variables'));

44
		// Install hooks for third-party plugin support:
45
		$this->thirdparty_wpo_wcpdf_init();
46
		
47
48
	}
	
49
50
51
52
	protected function initializeSettingsGeneral() {
		// Remove the NAG screen of the basic version
		return array();
	}
53
	/**
54
	 * Return the tooltip for the number format settings textinput (the two plugin versions have different features!)
55
	 */
56
57
58
	protected function getNumberFormatSettingsLabel() {
		return $this->helper->__( '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. Advanced settings for the counter can be added as [#####:start/step], e.g. [#:100] to start new counters at 100, or [#/5] to increment the counter by 5. The number of # in the format determine how many digits are shown at least, e.g. [########] will always show at least 8 digits for the counter, e.g. 00000014.');
	}
59
60
61
62
63
64
65
66
67
68
69
70
	protected function addGlobalCounterSettings($settings) {
		$settings[] = array(
				'title'		=> $this->helper->__( 'Use global counter'),
				'desc' 		=> $this->helper->__( 'A global counter never resets. Non-global counters run within each number format and reset whenever any variable changes.'),
				'id' 		=> 'ordernumber_global',
				'type' 		=> 'checkbox',
				'default'	=> 'no',
			);
		return $settings;
	}
	
	
71
72
73
74
75
76
77
78
79
	protected function initializeSettingsOther() {
		return array_merge(
			$this->initializeSettingsInvoiceNumbers(),
			$this->initializeSettingsReplacements()
		);
	}
	
	protected function initializeSettingsInvoiceNumbers() {
		$settings = array(
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
112
113
114
115
116
117
118
119
120
121
			/**
			 * Invoice number settings 
			 */
			
			array(
				'name' 		=> $this->helper->__( 'Advanced Invoice Numbers'),
				'desc'		=> $this->helper->__('This plugin currently supports modifying the invoice number formats of the following invoicing plugins: <a href="https://wordpress.org/plugins/woocommerce-pdf-invoices-packing-slips/">WooCommerce PDF Invoices & Packing Slips</a>'),
				'type' 		=> 'title',
				'id' 		=> 'invoice_options'
			),
			
			array(
				'name' 		=> $this->helper->__( 'Customize Invoice Numbers'),
				'desc' 		=> $this->helper->__( 'Check to use custom invoice numbers rather than the default format of your invoicing plugin.'),
				'id' 		=> 'customize_invoice',
				'type' 		=> 'checkbox',
				'default'	=> 'no'
			),
			array(
				'title'		=> $this->helper->__( 'Invoice number format'),
				'desc' 		=> $this->helper->__( 'The format for the invoice 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 invoice number, but reset the counter only yearly. Advanced settings for the counter can be added as [#####:start/step], e.g. [#:100] to start new counters at 100, or [#/5] to increment the counter by 5. The number of # in the format determine how many digits are shown at least, e.g. [########] will always show at least 8 digits for the counter, e.g. 00000014.'),
				'desc_tip'	=> true,
				'id' 		=> 'invoice_format',
				'default'	=> '#',
				'type' 		=> 'text',
				'css'		=> 'width: 100%',
			),
			array(
				'title'		=> $this->helper->__( 'Use global counter'),
				'desc' 		=> $this->helper->__( 'A global counter never resets. Non-global counters run within each number format and reset whenever any variable changes.'),
				'id' 		=> 'invoice_global',
				'type' 		=> 'checkbox',
				'default'	=> 'no',
			),
			array(
				'name' 		=> $this->helper->__( 'All invoice number counters'),
				'desc'		=> $this->helper->__( 'View and modify the current counter values. The counter value is the value used for the previous number. All changes are immediately applied!'),
				'desc_tip'	=> true,
				'id' 		=> 'ordernumber_counters',
				'type' 		=> 'ordernumber_counters',
				'nrtype' 	=> 'invoice',
			),
122
123
124
125
126
127
128
129
130
131
132
			array( 'type' => 'sectionend', 'id' => 'invoice_options' )
		);
		
		add_option ('customize_invoice', 'no');
		add_option ('invoice_format',    "#");
		add_option ('invoice_global',    'no');
		return $settings;
	}
	
	protected function initializeSettingsReplacements() {
		$settings = array(
133
134
135
136
137
138
139
140
141
142
			array(
				'name' 		=> $this->helper->__( 'Custom Variables'),
				'desc'		=> $this->helper->__( 'Define your own (conditional) variables for use in the number formats'),
				'type' 		=> 'title',
				'id' 		=> 'ordernumber_variables'
			),
			array(
				'id' 		=> 'ordernumber_variables',
				'type' 		=> 'ordernumber_variables',
			),
143
			array( 'type' => 'sectionend', 'id' => 'ordernumber_variables' )
144
145
		);
 		add_option ('ordernumber_variables',  array());
146
 		return $settings;
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
	}

	
	/**
	 * Render the Custom Variables configuration table
	 */
	public function admin_field_variables($settings) {
		$variables = get_option( $settings['id'], array() );
		if (!is_array($variables)) {
			$variables = array();
		} ?>
		<tr valign="top">
		    <td class="forminp forminp-<?php echo sanitize_title( $settings['type'] ) ?>" colspan="2">
				<?php
					print $this->helper->custom_variables_create_table($settings['id'], $variables);
				?>
			</td>
		</tr> 
		<?php
	}

	/** 
	 * Store the variable replacements array into the options. Need to transpose the array before we can store it into the options...
	 * This filter is called directly before the option is saved.
	 */
	public function update_option_variables ($value) {
		return OrdernumberHelper::transposeCustomVariables($value);
	}


	/** ***********************************************************
	 * 
	 *  REPLACEMENT FUNCTIONS
	 *
	 **************************************************************/
	
	public function setupAddressReplacements(&$reps, $prefix, $address, $nrtype) {
		$reps["[email]"]     = $address->billing_email;
		$reps["[firstname]"] = $address->billing_first_name;
		$reps["[lastname]"]  = $address->billing_last_name;

		$reps["[company]"]   = $address->billing_company;
		$reps["[zip]"]       = $address->billing_postcode;
		$reps["[postcode]"]  = $address->billing_postcode;
		$reps["[city]"]      = $address->billing_city;
    
		$country = $address->billing_country;
		$state = $address->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;
	}
	
	public function setupStoreReplacements (&$reps, $order, $nrtype) {
	}
    
	public function setupOrderReplacements (&$reps, $order, $nrtype) {
		$reps["[orderid]"] = $order->id;
		
		if ($nrtype != 'ordernumber') {
			$reps["[ordernumber]"] = $order->get_order_number();
		}
		$reps["[orderstatus]"] = $order->get_status();
		$reps["[currency]"]    = $order->get_order_currency();

		$this->setupAddressReplacements($reps, "", $order, $nrtype);
	
		$reps["[articles]"]    = $order->get_item_count();
// 		$reps["[downloadpermitted]"] = $order->is_download_permitted();
// 		$reps["[hasdownloads]"] = $order->has_downloadable_item();
// 		$reps["[coupons]"] = $order->get_used_coupons();
		$reps["[ordertotal]"]      = $order->get_total();
		$reps["[amount]"]      = $order->get_total();
		$reps["[ordersubtotal]"]      = $order->get_subtotal();
		$reps["[totaltax]"]      = $order->get_total_tax();
		$reps["[totalshipping]"]      = $order->get_total_shipping();
		
		// List-valued properties for custom variable checks:
		// TODO: Also implement variable for:
		//  - Shipping needed
		//  - Downloads available
		$lineitems = $order->get_items();
		$skus = array();
		$categories = array();
		$tags = array();
		$shippingclasses = array();
237
		$vendors = array();
238
239
240
241
242
243
244
245
246
247
		foreach ($lineitems as $l) {
			$p = $order->get_product_from_item($l);
			$skus[$p->get_sku()] = 1;
			foreach (wc_get_product_terms( $p->id, 'product_cat') as $c) {
				$categories[$c->slug] = 1;
			}
			foreach (wc_get_product_terms( $p->id, 'product_tag') as $c) {
				$tags[$c->slug] = 1;
			}
			$shippingclasses[$p->get_shipping_class()] = 1;
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
			
			// THIRD-PARTY SUPPORT
			// "WC Vendors"  support (vendors stored as post author)
			if (class_exists("WC_Vendors")) {
				$vendor = $p->post->post_author;
				$vnd = get_user_by('id', $vendor);  // Get user name by user id
				$vendors[] = $vendor;
				$vendors[] = $vnd->user_login;
			}
			
			// "WooThemes Vendor Products" support (vendors stored in its own taxonomy)
			if (class_exists("WooCommerce_Product_Vendors") && function_exists("get_product_vendors")) {
				foreach (get_product_vendors($p->id) as $vendor) {
					$vendors[] = $vendor->slug;
					$vendors[] = $vendor->ID;
				}
			}
			// END THIRD-PARTY SUPPORT
266
267
268
269
270
		}
		$reps["[skus]"] = array_keys($skus);
		$reps["[categories]"] = array_keys($categories);
		$reps["[tags]"] = array_keys($tags);
		$reps["[shippingclasses]"] = array_keys($shippingclasses);
271
		$reps["[vendors]"] = array_unique($vendors);
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
	}

	public function setupUserReplacements (&$reps, $details, $nrtype) {
		$reps["[ipaddress]"]   = $details->customer_ip_address;
		$reps["[userid]"]      = $details->get_user_id();
	}

	public function setupShippingReplacements(&$reps, $order, $nrtype) {
// 		$reps["[shippingmethod]"] = $order->getShippingMethod();
	}
	
	/*public function setupInvoiceReplacements (&$reps, $invoice, $order, $nrtype) {
		$reps["[invoiceid]"] = $invoice->getId();
	}*/

	public function setupThirdPartyReplacements (&$reps, $details, $nrtype) {
		$reps = apply_filters( 'opentools_ordernumber_replacements', $reps, $details, $nrtype);
	}

291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368

	/** ************************************************************
	 *  Support for automatic extension updates
	 ** ************************************************************/
	public function update_access_check() {
		$ordernumber = $_POST['order_number'];
		$orderpass = $_POST['order_pass'];
		
		$json = $this->helper->ajax_counter_delete($_POST['nrtype'], $_POST['counter']);
		wp_send_json($json);
	}
    public function checkUpdateAccess($order_number, $order_pass, $json = array()) {
		// First, extract the update server URL from the manifest, then load 
		// the update XML from the update server, extract the download URL, 
		// append the order number and password and check whether access is 
		// possible.
		$json['success'] = FALSE;
		if (isset($this->_xmlFile)) {
			$xmlfile = $this->_xmlFile;
		} else {
			// VM 2 does not set the _xmlFile property, so construct it manually
			$xmlfile = JPATH_SITE . '/plugins/' . $this->_type . '/' . $this->_name . '/' . $this->_name . '.xml';
		}
		$xml = simplexml_load_file($xmlfile);
		if (!$xml || !isset($xml->updateservers)) {
			JFactory::getApplication()->enqueueMessage(JText::sprintf('OPENTOOLS_XMLMANIFEST_ERROR', $this->_xmlFile), 'error');
			return $json;
		}
		$updateservers = $xml->updateservers;
		foreach ($updateservers->children() as $server) {
			if ($server->getName()!='server') {
				JFactory::getApplication()->enqueueMessage(JText::sprintf('OPENTOOLS_XMLMANIFEST_ERROR', $this->_xmlFile), 'error');
				continue;
			}
			$updateurl = html_entity_decode((string)$server);
			$updatescript = simplexml_load_file($updateurl);
			if (!$updatescript) {
				JFactory::getApplication()->enqueueMessage(JText::sprintf('OPENTOOLS_UPDATESCRIPT_ERROR', $updateurl), 'error');
				continue;
			}
			$urls = $updatescript->xpath('/updates/update/downloads/downloadurl');
			while (list( , $node) = each($urls)) {
				$downloadurl = (string)($node);
				if ($order_number) {
					$downloadurl .= (parse_url($downloadurl, PHP_URL_QUERY) ? '&' : '?') . 'order_number=' . urlencode($order_number);
				}
				if ($order_pass) {
					$downloadurl .= (parse_url($downloadurl, PHP_URL_QUERY) ? '&' : '?') . 'order_pass=' . urlencode($order_pass);
				}
				$downloadurl .= (parse_url($downloadurl, PHP_URL_QUERY) ? '&' : '?') . 'check_access=1';

				$headers = get_headers($downloadurl);
				list($version, $status_code, $msg) = explode(' ',$headers[0], 3);
				
				// Check the HTTP Status code
				switch($status_code) {
					case 200:
						$json['success'] = TRUE;
						JFactory::getApplication()->enqueueMessage($msg, 'message');
						$this->setupUpdateCredentials($order_number, $order_pass);
						break;
					default:
						JFactory::getApplication()->enqueueMessage($msg, 'error');
						// Clear the credentials...
						$this->setupUpdateCredentials("", "");
						break;
				}
				$this->setAndSaveParams(array(
					'update_credentials_checked'=>$json['success'],
					'order_number' => $order_number,
					'order_pass' => $order_pass,
				));
			}
		}
		return $json;
    }

	
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
	/** ************************************************************
	 *  Support for WPO WooCommerce PDF Invoices and Packaging Slips
	 ** ************************************************************
	 *
	 *  - Invoice numbers are stored in the _wcpdf_invoice_number post meta
	 *  - the filter wpo_wcpdf_invoice_number($invoice_number, $order_number, $order_id, $order_data) is called 
	 *    to format the (existing) invoice number retrieved from that post meta
	 *  - The action wpo_wcpdf_process_template_order($template_type, $order_id) is called right before
	 *    the invoice is created. There we can already set the _wcpdf_invoice_number post meta with our own value
	 */
	 
	/**
	 * Initialize support for WPO WooCommerce PDF Invoices and Packaging Slips
	 */
	protected function thirdparty_wpo_wcpdf_init() {
		// TODO: Wheck whether the woocommerce-pdf-invoices-packing-slips plugin is installed at all
		add_filter ('wpo_wcpdf_invoice_number', array($this, 'thirdparty_wpo_wcpdf_invoice_number'), 30, 4);
		add_action ('wpo_wcpdf_process_template_order', array($this, 'thirdparty_wpo_wcpdf_create_number'), 10, 2);
		// Disable the invoice number-related controls in the config of the other plugin
		add_action ('woocommerce_page_wpo_wcpdf_options_page', array($this, 'thirdparty_wpo_wcpdf_remove_options'));
	}
	 
	/**
	 * Support for WPO WooCommere PDF Invoices and Packaging Slips
	 * Filter to return the invoice number => simply return the first argument unchanged (was already 
	 * created in the correct format, no need to format it now again) 
	 */
	function thirdparty_wpo_wcpdf_invoice_number($invoice_number, $order_number, $order_id, $order_data) {
		$nr = $this->get_number($order_id, $order_data, 'invoice');
		if ($nr == $order_id) {
			// No number was found, so the default is the order id => reset to invoice number
			return $invoice_number;
		} else {
			return $nr;
		}
	}
	/**
	 * The action to actually create the number and store it as post meta with the order 
	 */
	function thirdparty_wpo_wcpdf_create_number($type, $orderid) {
		if ($type=='invoice' && (get_option('customize_'.$type, 'no')!='no') ) {
			$_of = new WC_Order_Factory();  
			$order = $_of->get_order($orderid);
			$number = $this->get_or_create_number($orderid, $order, $type);
			// TODO: Store the invoice number counter in _wcpdf_invoice_number and the custom invoice 
			// number in the opentools meta, because the plugin assumes the number to be numeric...
			update_post_meta( $orderid, '_wcpdf_invoice_number', $number );
		}
	}
	
	/** 
	 * The action that is called for the WPO WCPDF invoice plugin only, when the options page is loaded.
	 * If this plugin is enabled and invoice numbers are configured, we simply remove all invoice number-specific
	 * settings, because this plugin will be responsible....
	 */
	function thirdparty_wpo_wcpdf_remove_options() {
		global $wp_settings_fields;
		if (get_option('customize_invoice', 'no')!='no') {
			$wp_settings_fields['wpo_wcpdf_template_settings']['invoice']['display_number']['title'] = $this->helper->__('Display invoice number');
			$wp_settings_fields['wpo_wcpdf_template_settings']['invoice']['display_number']['args']['description'] = $this->helper->__('The <a href="admin.php?page=wc-settings&tab=checkout&section=ordernumber">Open Tools Ordernumber plugin</a> has invoice numbers enabled and will generate invoice numbers for this plugin.' );
			unset($wp_settings_fields['wpo_wcpdf_template_settings']['invoice']['next_invoice_number']);
			unset($wp_settings_fields['wpo_wcpdf_template_settings']['invoice']['invoice_number_formatting']);
		} else {
			$wp_settings_fields['wpo_wcpdf_template_settings']['invoice']['display_number']['args']['description'] = $this->helper->__('To let the Open Tools ordernumber plugin create invoice numbers with your desired format, please enable invoices in <a href="admin.php?page=wc-settings&tab=checkout&section=ordernumber">that plugin\'s configuration page</a>.' );
		}
	}
435

436
}