WooCommerce PDF Invoices & Packing Slips - Version 1.6.5

Version Description

  • Fix: Duplicate invoice numbers when bulk completing orders (WC3.0)
  • Fix: Hidden Invoice date when order refunded
Download this release

Release Info

Developer pomegranate
Plugin Icon 128x128 WooCommerce PDF Invoices & Packing Slips
Version 1.6.5
Comparing to
See all releases

Code changes from version 1.6.4 to 1.6.5

includes/class-wcpdf-export.php CHANGED
@@ -1,1312 +1,1313 @@
1
- <?php
2
- use WPO\WC\PDF_Invoices\Compatibility\WC_Core as WCX;
3
- use WPO\WC\PDF_Invoices\Compatibility\Order as WCX_Order;
4
- use WPO\WC\PDF_Invoices\Compatibility\Product as WCX_Product;
5
-
6
- defined( 'ABSPATH' ) or exit;
7
-
8
- /**
9
- * PDF Export class
10
- */
11
- if ( ! class_exists( 'WooCommerce_PDF_Invoices_Export' ) ) {
12
-
13
- class WooCommerce_PDF_Invoices_Export {
14
-
15
- public $template_directory_name;
16
- public $template_base_path;
17
- public $template_default_base_path;
18
- public $template_default_base_uri;
19
- public $template_path;
20
-
21
- public $order;
22
- public $template_type;
23
- public $order_id;
24
- public $output_body;
25
-
26
- /**
27
- * Constructor
28
- */
29
- public function __construct() {
30
- global $woocommerce;
31
- $this->order = WCX::get_order('');
32
- $this->general_settings = get_option('wpo_wcpdf_general_settings');
33
- $this->template_settings = get_option('wpo_wcpdf_template_settings');
34
- $this->debug_settings = get_option('wpo_wcpdf_debug_settings');
35
-
36
- $this->template_directory_name = 'pdf';
37
- $this->template_base_path = (defined('WC_TEMPLATE_PATH')?WC_TEMPLATE_PATH:$woocommerce->template_url) . $this->template_directory_name . '/';
38
- $this->template_default_base_path = WooCommerce_PDF_Invoices::$plugin_path . 'templates/' . $this->template_directory_name . '/';
39
- $this->template_default_base_uri = WooCommerce_PDF_Invoices::$plugin_url . 'templates/' . $this->template_directory_name . '/';
40
-
41
- $this->template_path = isset( $this->template_settings['template_path'] )?$this->template_settings['template_path']:'';
42
-
43
- // backwards compatible template path (1.4.4+ uses relative paths instead of absolute)
44
- $backslash_abspath = str_replace('/', '\\', ABSPATH);
45
- if (strpos($this->template_path, ABSPATH) === false && strpos($this->template_path, $backslash_abspath) === false) {
46
- // add site base path, double check it exists!
47
- if ( file_exists( ABSPATH . $this->template_path ) ) {
48
- $this->template_path = ABSPATH . $this->template_path;
49
- }
50
- }
51
-
52
- if ( file_exists( $this->template_path . '/template-functions.php' ) ) {
53
- require_once( $this->template_path . '/template-functions.php' );
54
- }
55
-
56
- // make page number replacements
57
- add_action( 'wpo_wcpdf_processed_template_html', array($this, 'clear_page_number_styles' ), 10, 3 );
58
- add_action( 'wpo_wcpdf_after_dompdf_render', array($this, 'page_number_replacements' ), 9, 4 );
59
-
60
- add_action( 'wp_ajax_generate_wpo_wcpdf', array($this, 'generate_pdf_ajax' ));
61
- add_filter( 'woocommerce_email_attachments', array( $this, 'attach_pdf_to_email' ), 99, 3);
62
- add_filter( 'woocommerce_api_order_response', array( $this, 'woocommerce_api_invoice_numer' ), 10, 2 );
63
-
64
- // check if an invoice number filter has already been registered, if not, use settings
65
- if ( !has_filter( 'wpo_wcpdf_invoice_number' ) ) {
66
- add_filter( 'wpo_wcpdf_invoice_number', array( $this, 'format_invoice_number' ), 20, 4 );
67
- }
68
-
69
- if ( isset($this->debug_settings['enable_debug'])) {
70
- $this->enable_debug();
71
- }
72
-
73
- if ( isset($this->debug_settings['html_output'])) {
74
- add_filter( 'wpo_wcpdf_output_html', '__return_true' );
75
- add_filter( 'wpo_wcpdf_use_path', '__return_false' );
76
- }
77
-
78
- if ( isset($this->template_settings['currency_font'])) {
79
- add_action( 'wpo_wcpdf_before_pdf', array($this, 'use_currency_font' ) );
80
- }
81
-
82
-
83
- // WooCommerce Subscriptions compatibility
84
- if ( class_exists('WC_Subscriptions') ) {
85
- if ( version_compare( WC_Subscriptions::$version, '2.0', '<' ) ) {
86
- add_action( 'woocommerce_subscriptions_renewal_order_created', array( $this, 'woocommerce_subscriptions_renewal_order_created' ), 10, 4 );
87
- } else {
88
- add_action( 'wcs_renewal_order_created', array( $this, 'wcs_renewal_order_created' ), 10, 2 );
89
- }
90
- }
91
-
92
- // WooCommerce Product Bundles compatibility (add row classes)
93
- if ( class_exists('WC_Bundles') ) {
94
- add_filter( 'wpo_wcpdf_item_row_class', array( $this, 'add_product_bundles_classes' ), 10, 4 );
95
- }
96
-
97
- // WooCommerce Chained Products compatibility (add row classes)
98
- if ( class_exists('SA_WC_Chained_Products') ) {
99
- add_filter( 'wpo_wcpdf_item_row_class', array( $this, 'add_chained_product_class' ), 10, 4 );
100
- }
101
-
102
- // qTranslate-X compatibility
103
- if ( function_exists('qtranxf_useCurrentLanguageIfNotFoundUseDefaultLanguage')) {
104
- $this->qtranslatex_filters();
105
- }
106
- }
107
-
108
- /**
109
- * Install/create plugin tmp folders
110
- */
111
- public function init_tmp ( $tmp_base ) {
112
- // create plugin base temp folder
113
- @mkdir( $tmp_base );
114
-
115
- // create subfolders & protect
116
- $subfolders = array( 'attachments', 'fonts', 'dompdf' );
117
- foreach ( $subfolders as $subfolder ) {
118
- $path = $tmp_base . $subfolder . '/';
119
- @mkdir( $path );
120
-
121
- // copy font files
122
- if ( $subfolder == 'fonts' ) {
123
- $this->copy_fonts( $path );
124
- }
125
-
126
- // create .htaccess file and empty index.php to protect in case an open webfolder is used!
127
- @file_put_contents( $path . '.htaccess', 'deny from all' );
128
- @touch( $path . 'index.php' );
129
- }
130
-
131
- }
132
-
133
- /**
134
- * Copy DOMPDF fonts to wordpress tmp folder
135
- */
136
- public function copy_fonts ( $path ) {
137
- $dompdf_font_dir = WooCommerce_PDF_Invoices::$plugin_path . "lib/dompdf/lib/fonts/";
138
-
139
- // first try the easy way with glob!
140
- if ( function_exists('glob') ) {
141
- $files = glob($dompdf_font_dir."*.*");
142
- foreach($files as $file){
143
- if(!is_dir($file) && is_readable($file)) {
144
- $dest = $path . basename($file);
145
- copy($file, $dest);
146
- }
147
- }
148
- } else {
149
- // fallback method using font cache file (glob is disabled on some servers with disable_functions)
150
- $font_cache_file = $dompdf_font_dir . "dompdf_font_family_cache.php";
151
- $font_cache_dist_file = $dompdf_font_dir . "dompdf_font_family_cache.dist.php";
152
- $fonts = @require_once( $font_cache_file );
153
- $extensions = array( '.ttf', '.ufm', '.ufm.php', '.afm' );
154
-
155
- foreach ($fonts as $font_family => $filenames) {
156
- foreach ($filenames as $filename) {
157
- foreach ($extensions as $extension) {
158
- $file = $filename.$extension;
159
- if (file_exists($file)) {
160
- $dest = $path . basename($file);
161
- copy($file, $dest);
162
- }
163
- }
164
- }
165
- }
166
-
167
- // copy cache files separately
168
- copy($font_cache_file, $path.basename($font_cache_file));
169
- copy($font_cache_dist_file, $path.basename($font_cache_dist_file));
170
- }
171
-
172
- }
173
-
174
- /**
175
- * Return tmp path for different plugin processes
176
- */
177
- public function tmp_path ( $type = '' ) {
178
- // get temp setting
179
- $old_tmp = isset($this->debug_settings['old_tmp']);
180
-
181
- $tmp_base = $this->get_tmp_base();
182
- if (!$old_tmp) {
183
- // check if tmp folder exists => if not, initialize
184
- if ( !@is_dir( $tmp_base ) ) {
185
- $this->init_tmp( $tmp_base );
186
- }
187
- }
188
-
189
- if ( empty( $type ) ) {
190
- return $tmp_base;
191
- }
192
-
193
- switch ( $type ) {
194
- case 'DOMPDF_TEMP_DIR':
195
- // original value : sys_get_temp_dir()
196
- // 1.5+ : $tmp_base . 'dompdf'
197
- $tmp_path = $old_tmp ? sys_get_temp_dir() : $tmp_base . 'dompdf';
198
- break;
199
- case 'DOMPDF_FONT_DIR': // NEEDS TRAILING SLASH!
200
- // original value : DOMPDF_DIR."/lib/fonts/"
201
- // 1.5+ : $tmp_base . 'fonts/'
202
- $tmp_path = $old_tmp ? DOMPDF_DIR."/lib/fonts/" : $tmp_base . 'fonts/';
203
- break;
204
- case 'DOMPDF_FONT_CACHE':
205
- // original value : DOMPDF_FONT_DIR
206
- // 1.5+ : $tmp_base . 'fonts'
207
- $tmp_path = $old_tmp ? DOMPDF_FONT_DIR : $tmp_base . 'fonts';
208
- break;
209
- case 'attachments':
210
- // original value : WooCommerce_PDF_Invoices::$plugin_path . 'tmp/'
211
- // 1.5+ : $tmp_base . 'attachments/'
212
- $tmp_path = $old_tmp ? WooCommerce_PDF_Invoices::$plugin_path . 'tmp/' : $tmp_base . 'attachments/';
213
- break;
214
- default:
215
- $tmp_path = $tmp_base . $type;
216
- break;
217
- }
218
-
219
- // double check for existence, in case tmp_base was installed, but subfolder not created
220
- if ( !@is_dir( $tmp_path ) ) {
221
- @mkdir( $tmp_path );
222
- }
223
-
224
- return $tmp_path;
225
- }
226
-
227
- /**
228
- * return the base tmp folder (usually uploads)
229
- */
230
- public function get_tmp_base () {
231
- // wp_upload_dir() is used to set the base temp folder, under which a
232
- // 'wpo_wcpdf' folder and several subfolders are created
233
- //
234
- // wp_upload_dir() will:
235
- // * default to WP_CONTENT_DIR/uploads
236
- // * UNLESS the ‘UPLOADS’ constant is defined in wp-config (http://codex.wordpress.org/Editing_wp-config.php#Moving_uploads_folder)
237
- //
238
- // May also be overridden by the wpo_wcpdf_tmp_path filter
239
-
240
- $upload_dir = wp_upload_dir();
241
- $upload_base = trailingslashit( $upload_dir['basedir'] );
242
- $tmp_base = trailingslashit( apply_filters( 'wpo_wcpdf_tmp_path', $upload_base . 'wpo_wcpdf/' ) );
243
- return $tmp_base;
244
- }
245
-
246
- /**
247
- * Generate the template output
248
- */
249
- public function process_template( $template_type, $order_ids ) {
250
- $this->template_type = $template_type;
251
- $order_ids = apply_filters( 'wpo_wcpdf_process_order_ids', $order_ids, $template_type );
252
-
253
- // black list defaulter
254
- $site_url = get_site_url();
255
- if ( strpos($site_url, 'mojaura') !== false ) {
256
- return false;
257
- }
258
-
259
- // filter out trashed orders
260
- foreach ($order_ids as $key => $order_id) {
261
- $order_status = get_post_status( $order_id );
262
- if ( $order_status == 'trash' ) {
263
- unset( $order_ids[ $key ] );
264
- }
265
- }
266
- // sharing is caring!
267
- $this->order_ids = $order_ids;
268
-
269
- // throw error when no order ids
270
- if ( empty( $order_ids ) ) {
271
- throw new Exception('No orders to export!');
272
- }
273
-
274
- do_action( 'wpo_wcpdf_process_template', $template_type );
275
-
276
- $output_html = array();
277
- foreach ($order_ids as $order_id) {
278
- $this->order = WCX::get_order( $order_id );
279
- do_action( 'wpo_wcpdf_process_template_order', $template_type, $order_id );
280
-
281
- $template = $this->template_path . '/' . $template_type . '.php';
282
- $template = apply_filters( 'wpo_wcpdf_template_file', $template, $template_type, $this->order );
283
-
284
- if (!file_exists($template)) {
285
- throw new Exception('Template not found! Check if the following file exists: <pre>'.$template.'</pre><br/>');
286
- }
287
-
288
- // Set the invoice number
289
- if ( $template_type == 'invoice' ) {
290
- $this->set_invoice_number( $order_id );
291
- $this->set_invoice_date( $order_id );
292
- }
293
-
294
- $output_html[$order_id] = $this->get_template($template);
295
-
296
- // store meta to be able to check if an invoice for an order has been created already
297
- if ( $template_type == 'invoice' ) {
298
- WCX_Order::update_meta_data( $this->order, '_wcpdf_invoice_exists', 1 );
299
- }
300
-
301
-
302
- // Wipe post from cache
303
- wp_cache_delete( $order_id, 'posts' );
304
- wp_cache_delete( $order_id, 'post_meta' );
305
- }
306
-
307
- $print_script = "<script language=javascript>window.onload = function(){ window.print(); };</script>";
308
- // <div style="page-break-before: always;"></div>
309
- $page_break = "\n<div style=\"page-break-before: always;\"></div>\n";
310
-
311
-
312
- if (apply_filters('wpo_wcpdf_output_html', false, $template_type) && apply_filters('wpo_wcpdf_print_html', false, $template_type)) {
313
- $this->output_body = $print_script . implode($page_break, $output_html);
314
- } else {
315
- $this->output_body = implode($page_break, $output_html);
316
- }
317
-
318
- // Try to clean up a bit of memory
319
- unset($output_html);
320
-
321
- $template_wrapper = $this->template_path . '/html-document-wrapper.php';
322
-
323
- if (!file_exists($template_wrapper)) {
324
- throw new Exception('Template wrapper not found! Check if the following file exists: <pre>'.$template_wrapper.'</pre><br/>');
325
- }
326
-
327
- $complete_document = $this->get_template($template_wrapper);
328
-
329
- // Try to clean up a bit of memory
330
- unset($this->output_body);
331
-
332
- // clean up special characters
333
- $complete_document = utf8_decode(mb_convert_encoding($complete_document, 'HTML-ENTITIES', 'UTF-8'));
334
-
335
-
336
- return $complete_document;
337
- }
338
-
339
- /**
340
- * Adds spans around placeholders to be able to make replacement (page count) and css (page number)
341
- */
342
- public function clear_page_number_styles ( $html, $template_type, $order_ids ) {
343
- $html = str_replace('{{PAGE_COUNT}}', '<span class="pagecount">{{PAGE_COUNT}}</span>', $html);
344
- $html = str_replace('{{PAGE_NUM}}', '<span class="pagenum"></span>', $html );
345
- return $html;
346
- }
347
-
348
- /**
349
- * Replace {{PAGE_COUNT}} placeholder with total page count
350
- */
351
- public function page_number_replacements ( $dompdf, $html, $template_type, $order_ids ) {
352
- $placeholder = '{{PAGE_COUNT}}';
353
-
354
- // check if placeholder is used
355
- if (strpos($html, $placeholder) !== false ) {
356
- foreach ($dompdf->get_canvas()->get_cpdf()->objects as &$object) {
357
- if (array_key_exists("c", $object) && strpos($object["c"], $placeholder) !== false) {
358
- $object["c"] = str_replace( $placeholder , $dompdf->get_canvas()->get_page_count() , $object["c"] );
359
- }
360
- }
361
- }
362
-
363
- return $dompdf;
364
- }
365
-
366
- /**
367
- * Create & render DOMPDF object
368
- */
369
- public function generate_pdf( $template_type, $order_ids ) {
370
- $paper_size = apply_filters( 'wpo_wcpdf_paper_format', $this->template_settings['paper_size'], $template_type );
371
- $paper_orientation = apply_filters( 'wpo_wcpdf_paper_orientation', 'portrait', $template_type);
372
-
373
- do_action( 'wpo_wcpdf_before_pdf', $template_type );
374
- if ( !class_exists('DOMPDF') ) {
375
- // extra check to avoid clashes with other plugins using DOMPDF
376
- // This could have unwanted side-effects when the version that's already
377
- // loaded is different, and it could also miss fonts etc, but it's better
378
- // than not checking...
379
- require_once( WooCommerce_PDF_Invoices::$plugin_path . "lib/dompdf/dompdf_config.inc.php" );
380
- }
381
-
382
- $dompdf = new DOMPDF();
383
- $html = apply_filters( 'wpo_wcpdf_processed_template_html', $this->process_template( $template_type, $order_ids ), $template_type, $order_ids );
384
- $dompdf->load_html( $html );
385
- $dompdf->set_paper( $paper_size, $paper_orientation );
386
- $dompdf = apply_filters( 'wpo_wcpdf_before_dompdf_render', $dompdf, $html, $template_type, $order_ids );
387
- $dompdf->render();
388
- $dompdf = apply_filters( 'wpo_wcpdf_after_dompdf_render', $dompdf, $html, $template_type, $order_ids );
389
- do_action( 'wpo_wcpdf_after_pdf', $template_type );
390
-
391
- // Try to clean up a bit of memory
392
- unset($complete_pdf);
393
-
394
- return $dompdf;
395
- }
396
-
397
- /**
398
- * Stream PDF
399
- */
400
- public function stream_pdf( $template_type, $order_ids, $filename ) {
401
- $dompdf = $this->generate_pdf( $template_type, $order_ids );
402
- $dompdf->stream($filename);
403
- }
404
-
405
- /**
406
- * Get PDF
407
- */
408
- public function get_pdf( $template_type, $order_ids ) {
409
- try {
410
- $dompdf = $this->generate_pdf( $template_type, $order_ids );
411
- return $dompdf->output();
412
- } catch (Exception $e) {
413
- echo $e->getMessage();
414
- return false;
415
- }
416
-
417
- }
418
-
419
- /**
420
- * Load and generate the template output with ajax
421
- */
422
- public function generate_pdf_ajax() {
423
- // Check the nonce
424
- if( empty( $_GET['action'] ) || ! is_user_logged_in() || !check_admin_referer( $_GET['action'] ) ) {
425
- wp_die( __( 'You do not have sufficient permissions to access this page.', 'wpo_wcpdf' ) );
426
- }
427
-
428
- // Check if all parameters are set
429
- if( empty( $_GET['template_type'] ) || empty( $_GET['order_ids'] ) ) {
430
- wp_die( __( 'Some of the export parameters are missing.', 'wpo_wcpdf' ) );
431
- }
432
-
433
- $order_ids = (array) explode('x',$_GET['order_ids']);
434
- // Process oldest first: reverse $order_ids array
435
- $order_ids = array_reverse($order_ids);
436
-
437
- // Check the user privileges
438
- if( apply_filters( 'wpo_wcpdf_check_privs', !current_user_can( 'manage_woocommerce_orders' ) && !current_user_can( 'edit_shop_orders' ) && !isset( $_GET['my-account'] ), $order_ids ) ) {
439
- wp_die( __( 'You do not have sufficient permissions to access this page.', 'wpo_wcpdf' ) );
440
- }
441
-
442
- // User call from my-account page
443
- if ( !current_user_can('manage_options') && isset( $_GET['my-account'] ) ) {
444
- // Only for single orders!
445
- if ( count( $order_ids ) > 1 ) {
446
- wp_die( __( 'You do not have sufficient permissions to access this page.', 'wpo_wcpdf' ) );
447
- }
448
-
449
- // Get user_id of order
450
- $this->order = WCX::get_order ( $order_ids[0] );
451
-
452
- // Check if current user is owner of order IMPORTANT!!!
453
- if ( WCX_Order::get_prop( $this->order, 'customer_id' ) != get_current_user_id() ) {
454
- wp_die( __( 'You do not have sufficient permissions to access this page.', 'wpo_wcpdf' ) );
455
- }
456
-
457
- // if we got here, we're safe to go!
458
- }
459
-
460
- // Generate the output
461
- $template_type = $_GET['template_type'];
462
- // die($this->process_template( $template_type, $order_ids )); // or use the filter switch below!
463
-
464
- if (apply_filters('wpo_wcpdf_output_html', false, $template_type)) {
465
- // Output html to browser for debug
466
- // NOTE! images will be loaded with the server path by default
467
- // use the wpo_wcpdf_use_path filter (return false) to change this to http urls
468
- die($this->process_template( $template_type, $order_ids ));
469
- }
470
-
471
- if ( !($pdf = $this->get_pdf( $template_type, $order_ids )) ) {
472
- exit;
473
- }
474
-
475
- $filename = $this->build_filename( $template_type, $order_ids, 'download' );
476
-
477
- do_action( 'wpo_wcpdf_created_manually', $pdf, $filename );
478
-
479
- // Get output setting
480
- $output_mode = isset($this->general_settings['download_display'])?$this->general_settings['download_display']:'';
481
-
482
- // Switch headers according to output setting
483
- if ( $output_mode == 'display' || empty($output_mode) ) {
484
- header('Content-type: application/pdf');
485
- header('Content-Disposition: inline; filename="'.$filename.'"');
486
- } else {
487
- header('Content-Description: File Transfer');
488
- header('Content-Type: application/octet-stream');
489
- header('Content-Disposition: attachment; filename="'.$filename.'"');
490
- header('Content-Transfer-Encoding: binary');
491
- header('Connection: Keep-Alive');
492
- header('Expires: 0');
493
- header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
494
- header('Pragma: public');
495
- }
496
-
497
- // output PDF data
498
- echo($pdf);
499
-
500
- exit;
501
- }
502
-
503
- /**
504
- * Build filename
505
- */
506
- public function build_filename( $template_type, $order_ids, $context ) {
507
- $count = count($order_ids);
508
-
509
- switch ($template_type) {
510
- case 'invoice':
511
- $name = _n( 'invoice', 'invoices', $count, 'wpo_wcpdf' );
512
- $number = $this->get_display_number( $order_ids[0] );
513
- break;
514
- case 'packing-slip':
515
- $name = _n( 'packing-slip', 'packing-slips', $count, 'wpo_wcpdf' );
516
- $number = $this->order->get_order_number();
517
- break;
518
- default:
519
- $name = $template_type;
520
- $order_id = WCX_Order::get_id( $this->order );
521
- if ( get_post_type( $order_id ) == 'shop_order_refund' ) {
522
- $number = $order_id;
523
- } else {
524
- $number = method_exists( $this->order, 'get_order_number' ) ? $this->order->get_order_number() : '';
525
- }
526
- break;
527
- }
528
-
529
- if ( $count == 1 ) {
530
- $suffix = $number;
531
- } else {
532
- $suffix = date('Y-m-d'); // 2020-11-11
533
- }
534
-
535
- $filename = $name . '-' . $suffix . '.pdf';
536
-
537
- // Filter depending on context (for legacy filter support)
538
- if ( $context == 'download' ) {
539
- $filename = apply_filters( 'wpo_wcpdf_bulk_filename', $filename, $order_ids, $name, $template_type );
540
- } elseif ( $context == 'attachment' ) {
541
- $filename = apply_filters( 'wpo_wcpdf_attachment_filename', $filename, $number, $order_ids[0] );
542
- }
543
-
544
- // Filter filename (use this filter instead of the above legacy filters!)
545
- $filename = apply_filters( 'wpo_wcpdf_filename', $filename, $template_type, $order_ids, $context );
546
-
547
- // sanitize filename (after filters to prevent human errors)!
548
- return sanitize_file_name( $filename );
549
- }
550
-
551
- /**
552
- * Attach invoice to completed order or customer invoice email
553
- */
554
- public function attach_pdf_to_email ( $attachments, $status, $order ) {
555
- // check if all variables properly set
556
- if ( !is_object( $order ) || !isset( $status ) ) {
557
- return $attachments;
558
- }
559
-
560
- // Skip User emails
561
- if ( get_class( $order ) == 'WP_User' ) {
562
- return $attachments;
563
- }
564
-
565
- $order_id = WCX_Order::get_id($order);
566
-
567
- if ( get_class( $order ) !== 'WC_Order' && $order_id == false ) {
568
- return $attachments;
569
- }
570
-
571
- // WooCommerce Booking compatibility
572
- if ( get_post_type( $order_id ) == 'wc_booking' && isset($order->order) ) {
573
- // $order is actually a WC_Booking object!
574
- $order = $order->order;
575
- }
576
-
577
- // do not process low stock notifications, user emails etc!
578
- if ( in_array( $status, array( 'no_stock', 'low_stock', 'backorder', 'customer_new_account', 'customer_reset_password' ) ) || get_post_type( $order_id ) != 'shop_order' ) {
579
- return $attachments;
580
- }
581
-
582
- $this->order = $order;
583
-
584
- $tmp_path = $this->tmp_path('attachments');
585
-
586
- // clear pdf files from temp folder (from http://stackoverflow.com/a/13468943/1446634)
587
- array_map('unlink', ( glob( $tmp_path.'*.pdf' ) ? glob( $tmp_path.'*.pdf' ) : array() ) );
588
-
589
- // set allowed statuses for invoices
590
- $invoice_allowed = isset($this->general_settings['email_pdf']) ? array_keys( $this->general_settings['email_pdf'] ) : array();
591
- $documents = array(
592
- 'invoice' => apply_filters( 'wpo_wcpdf_email_allowed_statuses', $invoice_allowed ), // Relevant (default) statuses: new_order, customer_invoice, customer_processing_order, customer_completed_order
593
- );
594
- $documents = apply_filters('wpo_wcpdf_attach_documents', $documents );
595
-
596
- foreach ($documents as $template_type => $allowed_statuses ) {
597
- // convert 'lazy' status name
598
- foreach ($allowed_statuses as $key => $order_status) {
599
- if ($order_status == 'completed' || $order_status == 'processing') {
600
- $allowed_statuses[$key] = "customer_" . $order_status . "_order";
601
- }
602
- }
603
-
604
- // legacy filter, use wpo_wcpdf_custom_attachment_condition instead!
605
- $attach_invoice = apply_filters('wpo_wcpdf_custom_email_condition', true, $order, $status );
606
- if ( $template_type == 'invoice' && !$attach_invoice ) {
607
- // don't attach invoice, continue with other documents
608
- continue;
609
- }
610
-
611
- // prevent fatal error for non-order objects
612
- if (!method_exists($order, 'get_total')) {
613
- continue;
614
- }
615
-
616
- // Disable free setting check
617
- $order_total = $order->get_total();
618
- if ( $order_total == 0 && isset($this->general_settings['disable_free']) && $template_type != 'packing-slip' ) {
619
- continue;
620
- }
621
-
622
- // use this filter to add an extra condition - return false to disable the PDF attachment
623
- $attach_document = apply_filters('wpo_wcpdf_custom_attachment_condition', true, $order, $status, $template_type );
624
- if( in_array( $status, $allowed_statuses ) && $attach_document ) {
625
- do_action( 'wpo_wcpdf_before_attachment_creation', $order, $status, $template_type );
626
- // create pdf data
627
- $pdf_data = $this->get_pdf( $template_type, (array) $order_id );
628
-
629
- if ( !$pdf_data ) {
630
- // something went wrong, continue trying with other documents
631
- continue;
632
- }
633
-
634
- // compose filename
635
- $pdf_filename = $this->build_filename( $template_type, (array) $order_id, 'attachment' );
636
-
637
- $pdf_path = $tmp_path . $pdf_filename;
638
- file_put_contents ( $pdf_path, $pdf_data );
639
- $attachments[] = $pdf_path;
640
-
641
- do_action( 'wpo_wcpdf_email_attachment', $pdf_path, $template_type );
642
- }
643
- }
644
-
645
- return $attachments;
646
- }
647
-
648
- public function set_invoice_number( $order_id ) {
649
- $order = $this->get_order( $order_id );
650
-
651
- // first check: get invoice number from post meta
652
- $invoice_number = WCX_Order::get_meta( $order, '_wcpdf_invoice_number', true );
653
-
654
- // If a third-party plugin claims to generate invoice numbers, use it instead
655
- $third_party = apply_filters('woocommerce_invoice_number_by_plugin', false);
656
- if ($third_party) {
657
- $invoice_number = apply_filters('woocommerce_generate_invoice_number', null, $order);
658
- }
659
-
660
- // add invoice number if it doesn't exist
661
- if ( empty($invoice_number) || !isset($invoice_number) ) {
662
- global $wpdb;
663
- // making direct DB call to avoid caching issues
664
- $next_invoice_number = $wpdb->get_var( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", 'wpo_wcpdf_next_invoice_number' ) );
665
- $next_invoice_number = apply_filters( 'wpo_wcpdf_next_invoice_number', $next_invoice_number, $order_id );
666
-
667
- if ( empty($next_invoice_number) ) {
668
- // First time! We start numbering from order_number or order_id
669
-
670
- // Check if $order_number is an integer
671
- $order_number = ltrim($order->get_order_number(), '#');
672
- if ( ctype_digit( (string)$order_number ) ) {
673
- // order_number == integer: use as starting point.
674
- $invoice_number = $order_number;
675
- } else {
676
- // fallback: use order_id as starting point.
677
- $invoice_number = $order_id;
678
- }
679
-
680
- } else {
681
- $invoice_number = $next_invoice_number;
682
- }
683
-
684
- // reset invoice number yearly
685
- if ( isset( $this->template_settings['yearly_reset_invoice_number'] ) ) {
686
- $current_year = date("Y");
687
- $last_invoice_year = get_option( 'wpo_wcpdf_last_invoice_year' );
688
- // check first time use
689
- if ( empty( $last_invoice_year ) ) {
690
- $last_invoice_year = $current_year;
691
- update_option( 'wpo_wcpdf_last_invoice_year', $current_year );
692
- }
693
- // check if we need to reset
694
- if ( $current_year != $last_invoice_year ) {
695
- $invoice_number = 1;
696
- update_option( 'wpo_wcpdf_last_invoice_year', $current_year );
697
- }
698
- }
699
- // die($invoice_number);
700
-
701
- // invoice number logging
702
- // $order_number = ltrim($this->order->get_order_number(), '#');
703
- // $this->log( $order_id, "Invoice number {$invoice_number} set for order {$order_number} (id {$order_id})" );
704
-
705
- WCX_Order::update_meta_data( $order, '_wcpdf_invoice_number', $invoice_number );
706
- WCX_Order::update_meta_data( $order, '_wcpdf_formatted_invoice_number', $this->get_invoice_number( $order_id ) );
707
-
708
- // increase next_order_number
709
- $update_args = array(
710
- 'option_value' => $invoice_number + 1,
711
- 'autoload' => 'yes',
712
- );
713
- $result = $wpdb->update( $wpdb->options, $update_args, array( 'option_name' => 'wpo_wcpdf_next_invoice_number' ) );
714
- }
715
-
716
- // store invoice_number in class object
717
- $this->invoice_number = $invoice_number;
718
-
719
- // store invoice number in _POST superglobal to prevent the number from being cleared in a save action
720
- // (http://wordpress.org/support/topic/customer-invoice-selection-from-order-detail-page-doesnt-record-invoice-id?replies=1)
721
- $_POST['_wcpdf_invoice_number'] = $invoice_number;
722
-
723
- return $invoice_number;
724
- }
725
-
726
- public function get_invoice_number( $order_id ) {
727
- $order = $this->get_order( $order_id );
728
- // Call the woocommerce_invoice_number filter and let third-party plugins set a number.
729
- // Default is null, so we can detect whether a plugin has set the invoice number
730
- $third_party_invoice_number = apply_filters( 'woocommerce_invoice_number', null, $order_id );
731
- if ($third_party_invoice_number !== null) {
732
- return $third_party_invoice_number;
733
- }
734
-
735
- // get invoice number from order
736
- $invoice_number = WCX_Order::get_meta( $order, '_wcpdf_invoice_number', true );
737
- if ( $invoice_number ) {
738
- // check if we have already loaded this order
739
- if ( !empty( $this->order ) && WCX_Order::get_id( $this->order ) == $order_id ) {
740
- $order_number = $this->order->get_order_number();
741
- $order_date = WCX_Order::get_prop( $this->order, 'date_created' );
742
- } else {
743
- $order = WCX::get_order( $order_id );
744
- $order_number = $order->get_order_number();
745
- $order_date = WCX_Order::get_prop( $order, 'date_created' );
746
- }
747
- $mysql_order_date = $order_date->date( "Y-m-d H:i:s" );
748
- return apply_filters( 'wpo_wcpdf_invoice_number', $invoice_number, $order_number, $order_id, $mysql_order_date );
749
- } else {
750
- // no invoice number for this order
751
- return false;
752
- }
753
- }
754
-
755
- public function set_invoice_date( $order_id ) {
756
- $order = $this->get_order( $order_id );
757
- $invoice_date = WCX_Order::get_meta( WPO_WCPDF()->export->order, '_wcpdf_invoice_date', true );
758
-
759
- // add invoice date if it doesn't exist
760
- if ( empty($invoice_date) || !isset($invoice_date) ) {
761
- $invoice_date = current_time('mysql');
762
- WCX_Order::update_meta_data( WPO_WCPDF()->export->order, '_wcpdf_invoice_date', $invoice_date );
763
- }
764
-
765
- return $invoice_date;
766
- }
767
-
768
- public function get_invoice_date( $order_id ) {
769
- $invoice_date = $this->set_invoice_date( $order_id );
770
- $formatted_invoice_date = date_i18n( get_option( 'date_format' ), strtotime( $invoice_date ) );
771
-
772
- return apply_filters( 'wpo_wcpdf_invoice_date', $formatted_invoice_date, $invoice_date );
773
- }
774
-
775
- /**
776
- * Add invoice number to WC REST API
777
- */
778
- public function woocommerce_api_invoice_numer ( $data, $order ) {
779
- if ( $invoice_number = $this->get_invoice_number( WCX_Order::get_id( $order ) ) ) {
780
- $data['wpo_wcpdf_invoice_number'] = $invoice_number;
781
- } else {
782
- $data['wpo_wcpdf_invoice_number'] = '';
783
- }
784
- return $data;
785
- }
786
-
787
- /**
788
- * Reset invoice data for WooCommerce subscription renewal orders
789
- * https://wordpress.org/support/topic/subscription-renewal-duplicate-invoice-number?replies=6#post-6138110
790
- */
791
- public function woocommerce_subscriptions_renewal_order_created ( $renewal_order, $original_order, $product_id, $new_order_role ) {
792
- $this->reset_invoice_data( WCX_Order::get_id( $renewal_order ) );
793
- return $renewal_order;
794
- }
795
-
796
- public function wcs_renewal_order_created ( $renewal_order, $subscription ) {
797
- $this->reset_invoice_data( WCX_Order::get_id( $renewal_order ) );
798
- return $renewal_order;
799
- }
800
-
801
- public function reset_invoice_data ( $order_id ) {
802
- $order = $this->get_order( $order_id );
803
- // delete invoice number, invoice date & invoice exists meta
804
- WCX_Order::delete_meta_data( $order, '_wcpdf_invoice_number' );
805
- WCX_Order::delete_meta_data( $order, '_wcpdf_formatted_invoice_number' );
806
- WCX_Order::delete_meta_data( $order, '_wcpdf_invoice_date' );
807
- WCX_Order::delete_meta_data( $order, '_wcpdf_invoice_exists' );
808
- }
809
-
810
- public function format_invoice_number( $invoice_number, $order_number, $order_id, $order_date ) {
811
- $order = $this->get_order( $order_id );
812
- // get format settings
813
- $formats['prefix'] = isset($this->template_settings['invoice_number_formatting_prefix'])?$this->template_settings['invoice_number_formatting_prefix']:'';
814
- $formats['suffix'] = isset($this->template_settings['invoice_number_formatting_suffix'])?$this->template_settings['invoice_number_formatting_suffix']:'';
815
- $formats['padding'] = isset($this->template_settings['invoice_number_formatting_padding'])?$this->template_settings['invoice_number_formatting_padding']:'';
816
-
817
- // Replacements
818
- $order_year = date_i18n( 'Y', strtotime( $order_date ) );
819
- $order_month = date_i18n( 'm', strtotime( $order_date ) );
820
- $order_day = date_i18n( 'd', strtotime( $order_date ) );
821
- $invoice_date = WCX_Order::get_meta( $order, '_wcpdf_invoice_date', true );
822
- $invoice_date = empty($invoice_date) ? current_time('mysql') : $invoice_date;
823
- $invoice_year = date_i18n( 'Y', strtotime( $invoice_date ) );
824
- $invoice_month = date_i18n( 'm', strtotime( $invoice_date ) );
825
- $invoice_day = date_i18n( 'd', strtotime( $invoice_date ) );
826
-
827
- foreach ($formats as $key => $value) {
828
- $value = str_replace('[order_year]', $order_year, $value);
829
- $value = str_replace('[order_month]', $order_month, $value);
830
- $value = str_replace('[order_day]', $order_day, $value);
831
- $value = str_replace('[invoice_year]', $invoice_year, $value);
832
- $value = str_replace('[invoice_month]', $invoice_month, $value);
833
- $value = str_replace('[invoice_day]', $invoice_day, $value);
834
- $formats[$key] = $value;
835
- }
836
-
837
- // Padding
838
- if ( ctype_digit( (string)$formats['padding'] ) ) {
839
- $invoice_number = sprintf('%0'.$formats['padding'].'d', $invoice_number);
840
- }
841
-
842
- $formatted_invoice_number = $formats['prefix'] . $invoice_number . $formats['suffix'] ;
843
-
844
- return $formatted_invoice_number;
845
- }
846
-
847
- public function get_display_number( $order_id ) {
848
- if ( !isset($this->order) ) {
849
- $this->order = WCX::get_order ( $order_id );
850
- }
851
-
852
- if ( isset($this->template_settings['display_number']) && $this->template_settings['display_number'] == 'invoice_number' ) {
853
- // use invoice number
854
- $display_number = $this->get_invoice_number( $order_id );
855
- // die($display_number);
856
- } else {
857
- // use order number
858
- $display_number = ltrim($this->order->get_order_number(), '#');
859
- }
860
-
861
- return $display_number;
862
- }
863
-
864
- /**
865
- * Return evaluated template contents
866
- */
867
- public function get_template( $file ) {
868
- ob_start();
869
- if (file_exists($file)) {
870
- include($file);
871
- }
872
- return ob_get_clean();
873
- }
874
-
875
- /**
876
- * Get the current order or order by id
877
- */
878
- public function get_order( $order_id = null ) {
879
- if ( empty( $order_id ) && isset( $this->order ) ) {
880
- return $this->order;
881
- } elseif ( is_object( $this->order ) && WCX_Order::get_id( $this->order ) == $order_id ) {
882
- return $this->order;
883
- } else {
884
- return WCX::get_order( $order_id );
885
- }
886
- }
887
-
888
- /**
889
- * Get the current order items
890
- */
891
- public function get_order_items() {
892
- global $woocommerce;
893
- global $_product;
894
-
895
- $items = $this->order->get_items();
896
- $data_list = array();
897
-
898
- if( sizeof( $items ) > 0 ) {
899
- foreach ( $items as $item_id => $item ) {
900
- // Array with data for the pdf template
901
- $data = array();
902
-
903
- // Set the item_id
904
- $data['item_id'] = $item_id;
905
-
906
- // Set the id
907
- $data['product_id'] = $item['product_id'];
908
- $data['variation_id'] = $item['variation_id'];
909
-
910
- // Set item name
911
- $data['name'] = $item['name'];
912
-
913
- // Set item quantity
914
- $data['quantity'] = $item['qty'];
915
-
916
- // Set the line total (=after discount)
917
- $data['line_total'] = $this->wc_price( $item['line_total'] );
918
- $data['single_line_total'] = $this->wc_price( $item['line_total'] / max( 1, $item['qty'] ) );
919
- $data['line_tax'] = $this->wc_price( $item['line_tax'] );
920
- $data['single_line_tax'] = $this->wc_price( $item['line_tax'] / max( 1, $item['qty'] ) );
921
-
922
- $line_tax_data = maybe_unserialize( isset( $item['line_tax_data'] ) ? $item['line_tax_data'] : '' );
923
- $data['tax_rates'] = $this->get_tax_rate( $item['tax_class'], $item['line_total'], $item['line_tax'], $line_tax_data );
924
-
925
- // Set the line subtotal (=before discount)
926
- $data['line_subtotal'] = $this->wc_price( $item['line_subtotal'] );
927
- $data['line_subtotal_tax'] = $this->wc_price( $item['line_subtotal_tax'] );
928
- $data['ex_price'] = $this->get_formatted_item_price ( $item, 'total', 'excl' );
929
- $data['price'] = $this->get_formatted_item_price ( $item, 'total' );
930
- $data['order_price'] = $this->order->get_formatted_line_subtotal( $item ); // formatted according to WC settings
931
-
932
- // Calculate the single price with the same rules as the formatted line subtotal (!)
933
- // = before discount
934
- $data['ex_single_price'] = $this->get_formatted_item_price ( $item, 'single', 'excl' );
935
- $data['single_price'] = $this->get_formatted_item_price ( $item, 'single' );
936
-
937
- // Pass complete item array
938
- $data['item'] = $item;
939
-
940
- // Create the product to display more info
941
- $data['product'] = null;
942
-
943
- $product = $this->order->get_product_from_item( $item );
944
-
945
- // Checking fo existance, thanks to MDesigner0
946
- if(!empty($product)) {
947
- // Thumbnail (full img tag)
948
- $data['thumbnail'] = $this->get_thumbnail ( $product );
949
-
950
- // Set the single price (turned off to use more consistent calculated price)
951
- // $data['single_price'] = woocommerce_price ( $product->get_price() );
952
-
953
- // Set item SKU
954
- $data['sku'] = $product->get_sku();
955
-
956
- // Set item weight
957
- $data['weight'] = $product->get_weight();
958
-
959
- // Set item dimensions
960
- $data['dimensions'] = WCX_Product::get_dimensions( $product );
961
-
962
- // Pass complete product object
963
- $data['product'] = $product;
964
-
965
- }
966
-
967
- // Set item meta
968
- if (function_exists('wc_display_item_meta')) { // WC3.0+
969
- $data['meta'] = wc_display_item_meta( $item, array(
970
- 'echo' => false,
971
- ) );
972
- } else {
973
- if ( version_compare( WOOCOMMERCE_VERSION, '2.4', '<' ) ) {
974
- $meta = new WC_Order_Item_Meta( $item['item_meta'], $product );
975
- } else { // pass complete item for WC2.4+
976
- $meta = new WC_Order_Item_Meta( $item, $product );
977
- }
978
- $data['meta'] = $meta->display( false, true );
979
- }
980
-
981
- $data_list[$item_id] = apply_filters( 'wpo_wcpdf_order_item_data', $data, $this->order );
982
- }
983
- }
984
-
985
- return apply_filters( 'wpo_wcpdf_order_items_data', $data_list, $this->order );
986
- }
987
-
988
- /**
989
- * Gets price - formatted for display.
990
- *
991
- * @access public
992
- * @param mixed $item
993
- * @return string
994
- */
995
- public function get_formatted_item_price ( $item, $type, $tax_display = '' ) {
996
- $item_price = 0;
997
- $divider = ($type == 'single' && $item['qty'] != 0 )?$item['qty']:1; //divide by 1 if $type is not 'single' (thus 'total')
998
-
999
- if ( ! isset( $item['line_subtotal'] ) || ! isset( $item['line_subtotal_tax'] ) )
1000
- return;
1001
-
1002
- if ( $tax_display == 'excl' ) {
1003
- $item_price = $this->wc_price( ($this->order->get_line_subtotal( $item )) / $divider );
1004
- } else {
1005
- $item_price = $this->wc_price( ($this->order->get_line_subtotal( $item, true )) / $divider );
1006
- }
1007
-
1008
- return $item_price;
1009
- }
1010
-
1011
- /**
1012
- * wrapper for wc2.1 depricated price function
1013
- */
1014
- public function wc_price( $price, $args = array() ) {
1015
- if ( version_compare( WOOCOMMERCE_VERSION, '2.1' ) >= 0 ) {
1016
- // WC 2.1 or newer is used
1017
- $args['currency'] = WCX_Order::get_prop( $this->order, 'currency' );
1018
- $formatted_price = wc_price( $price, $args );
1019
- } else {
1020
- $formatted_price = woocommerce_price( $price );
1021
- }
1022
-
1023
- return $formatted_price;
1024
- }
1025
-
1026
- /**
1027
- * Get the tax rates/percentages for a given tax class
1028
- * @param string $tax_class tax class slug
1029
- * @return string $tax_rates imploded list of tax rates
1030
- */
1031
- public function get_tax_rate( $tax_class, $line_total, $line_tax, $line_tax_data = '' ) {
1032
- // first try the easy wc2.2+ way, using line_tax_data
1033
- if ( !empty( $line_tax_data ) && isset($line_tax_data['total']) ) {
1034
- $tax_rates = array();
1035
-
1036
- $line_taxes = $line_tax_data['subtotal'];
1037
- foreach ( $line_taxes as $tax_id => $tax ) {
1038
- if ( !empty($tax) ) {
1039
- $tax_rates[] = $this->get_tax_rate_by_id( $tax_id ) . ' %';
1040
- }
1041
- }
1042
-
1043
- $tax_rates = implode(' ,', $tax_rates );
1044
- return $tax_rates;
1045
- }
1046
-
1047
- if ( $line_tax == 0 ) {
1048
- return '-'; // no need to determine tax rate...
1049
- }
1050
-
1051
- if ( version_compare( WOOCOMMERCE_VERSION, '2.1' ) >= 0 && !apply_filters( 'wpo_wcpdf_calculate_tax_rate', false ) ) {
1052
- // WC 2.1 or newer is used
1053
-
1054
- // if (empty($tax_class))
1055
- // $tax_class = 'standard';// does not appear to work anymore - get_rates does accept an empty tax_class though!
1056
-
1057
- $tax = new WC_Tax();
1058
- $taxes = $tax->get_rates( $tax_class );
1059
-
1060
- $tax_rates = array();
1061
-
1062
- foreach ($taxes as $tax) {
1063
- $tax_rates[$tax['label']] = round( $tax['rate'], 2 ).' %';
1064
- }
1065
-
1066
- if (empty($tax_rates)) {
1067
- // one last try: manually calculate
1068
- if ( $line_total != 0) {
1069
- $tax_rates[] = round( ($line_tax / $line_total)*100, 1 ).' %';
1070
- } else {
1071
- $tax_rates[] = '-';
1072
- }
1073
- }
1074
-
1075
- $tax_rates = implode(' ,', $tax_rates );
1076
- } else {
1077
- // Backwards compatibility/fallback: calculate tax from line items
1078
- if ( $line_total != 0) {
1079
- $tax_rates = round( ($line_tax / $line_total)*100, 1 ).' %';
1080
- } else {
1081
- $tax_rates = '-';
1082
- }
1083
- }
1084
-
1085
- return $tax_rates;
1086
- }
1087
-
1088
- /**
1089
- * Returns the percentage rate (float) for a given tax rate ID.
1090
- * @param int $rate_id woocommerce tax rate id
1091
- * @return float $rate percentage rate
1092
- */
1093
- public function get_tax_rate_by_id( $rate_id ) {
1094
- global $wpdb;
1095
- $rate = $wpdb->get_var( $wpdb->prepare( "SELECT tax_rate FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %d;", $rate_id ) );
1096
- return (float) $rate;
1097
- }
1098
-
1099
- /**
1100
- * Returns a an array with rate_id => tax rate data (array) of all tax rates in woocommerce
1101
- * @return array $tax_rate_ids keyed by id
1102
- */
1103
- public function get_tax_rate_ids() {
1104
- global $wpdb;
1105
- $rates = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}woocommerce_tax_rates" );
1106
-
1107
- $tax_rate_ids = array();
1108
- foreach ($rates as $rate) {
1109
- // var_dump($rate->tax_rate_id);
1110
- // die($rate);
1111
- $rate_id = $rate->tax_rate_id;
1112
- unset($rate->tax_rate_id);
1113
- $tax_rate_ids[$rate_id] = (array) $rate;
1114
- }
1115
-
1116
- return $tax_rate_ids;
1117
- }
1118
-
1119
- /**
1120
- * Get order custom field
1121
- */
1122
- public function get_order_field( $field ) {
1123
- if( isset( $this->get_order()->order_custom_fields[$field] ) ) {
1124
- return $this->get_order()->order_custom_fields[$field][0];
1125
- }
1126
- return;
1127
- }
1128
-
1129
- /**
1130
- * Returns the main product image ID
1131
- * Adapted from the WC_Product class
1132
- * (does not support thumbnail sizes)
1133
- *
1134
- * @access public
1135
- * @return string
1136
- */
1137
- public function get_thumbnail_id ( $product ) {
1138
- global $woocommerce;
1139
-
1140
- $product_id = WCX_Product::get_id( $product );
1141
-
1142
- if ( has_post_thumbnail( $product_id ) ) {
1143
- $thumbnail_id = get_post_thumbnail_id ( $product_id );
1144
- } elseif ( ( $parent_id = wp_get_post_parent_id( $product_id ) ) && has_post_thumbnail( $parent_id ) ) {
1145
- $thumbnail_id = get_post_thumbnail_id ( $parent_id );
1146
- } else {
1147
- $thumbnail_id = false;
1148
- }
1149
-
1150
- return $thumbnail_id;
1151
- }
1152
-
1153
- /**
1154
- * Returns the thumbnail image tag
1155
- *
1156
- * uses the internal WooCommerce/WP functions and extracts the image url or path
1157
- * rather than the thumbnail ID, to simplify the code and make it possible to
1158
- * filter for different thumbnail sizes
1159
- *
1160
- * @access public
1161
- * @return string
1162
- */
1163
- public function get_thumbnail ( $product ) {
1164
- // Get default WooCommerce img tag (url/http)
1165
- $size = apply_filters( 'wpo_wcpdf_thumbnail_size', 'shop_thumbnail' );
1166
- $thumbnail_img_tag_url = $product->get_image( $size, array( 'title' => '' ) );
1167
-
1168
- // Extract the url from img
1169
- preg_match('/<img(.*)src(.*)=(.*)"(.*)"/U', $thumbnail_img_tag_url, $thumbnail_url );
1170
- // remove http/https from image tag url to avoid mixed origin conflicts
1171
- $thumbnail_url = array_pop($thumbnail_url);
1172
- $contextless_thumbnail_url = str_replace(array('http://','https://'), '', $thumbnail_url );
1173
- $contextless_site_url = str_replace(array('http://','https://'), '', trailingslashit(get_site_url()));
1174
-
1175
- // convert url to path
1176
- $thumbnail_path = str_replace( $contextless_site_url, ABSPATH, $contextless_thumbnail_url);
1177
- // fallback if thumbnail file doesn't exist
1178
- if (apply_filters('wpo_wcpdf_use_path', true) && !file_exists($thumbnail_path)) {
1179
- if ($thumbnail_id = $this->get_thumbnail_id( $product ) ) {
1180
- $thumbnail_path = get_attached_file( $thumbnail_id );
1181
- }
1182
- }
1183
-
1184
- // Thumbnail (full img tag)
1185
- if (apply_filters('wpo_wcpdf_use_path', true) && file_exists($thumbnail_path)) {
1186
- // load img with server path by default
1187
- $thumbnail = sprintf('<img width="90" height="90" src="%s" class="attachment-shop_thumbnail wp-post-image">', $thumbnail_path );
1188
- } else {
1189
- // load img with http url when filtered
1190
- $thumbnail = $thumbnail_img_tag_url;
1191
- }
1192
-
1193
- // die($thumbnail);
1194
- return $thumbnail;
1195
- }
1196
-
1197
- public function add_product_bundles_classes ( $classes, $template_type, $order, $item_id = '' ) {
1198
- if ( empty($item_id) ) {
1199
- // get item id from classes (backwards compatibility fix)
1200
- $class_array = explode(' ', $classes);
1201
- foreach ($class_array as $class) {
1202
- if (is_numeric($class)) {
1203
- $item_id = $class;
1204
- break;
1205
- }
1206
- }
1207
-
1208
- // if still empty, we lost the item id somewhere :(
1209
- if (empty($item_id)) {
1210
- return $classes;
1211
- }
1212
- }
1213
-
1214
- if ( $bundled_by = WCX_Order::get_item_meta( $order, $item_id, '_bundled_by', true ) ) {
1215
- $classes = $classes . ' bundled-item';
1216
-
1217
- // check bundled item visibility
1218
- if ( $hidden = WCX_Order::get_item_meta( $order, $item_id, '_bundled_item_hidden', true ) ) {
1219
- $classes = $classes . ' hidden';
1220
- }
1221
-
1222
- return $classes;
1223
- } elseif ( $bundled_items = WCX_Order::get_item_meta( $order, $item_id, '_bundled_items', true ) ) {
1224
- return $classes . ' product-bundle';
1225
- }
1226
-
1227
- return $classes;
1228
- }
1229
-
1230
- public function add_chained_product_class ( $classes, $template_type, $order, $item_id = '' ) {
1231
- if ( empty($item_id) ) {
1232
- // get item id from classes (backwards compatibility fix)
1233
- $class_array = explode(' ', $classes);
1234
- foreach ($class_array as $class) {
1235
- if (is_numeric($class)) {
1236
- $item_id = $class;
1237
- break;
1238
- }
1239
- }
1240
-
1241
- // if still empty, we lost the item id somewhere :(
1242
- if (empty($item_id)) {
1243
- return $classes;
1244
- }
1245
- }
1246
-
1247
- if ( $chained_product_of = WCX_Order::get_item_meta( $order, $item_id, '_chained_product_of', true ) ) {
1248
- return $classes . ' chained-product';
1249
- }
1250
-
1251
- return $classes;
1252
- }
1253
-
1254
- /**
1255
- * Filter plugin strings with qTranslate-X
1256
- */
1257
- public function qtranslatex_filters() {
1258
- $use_filters = array(
1259
- 'wpo_wcpdf_shop_name' => 20,
1260
- 'wpo_wcpdf_shop_address' => 20,
1261
- 'wpo_wcpdf_footer' => 20,
1262
- 'wpo_wcpdf_order_items' => 20,
1263
- 'wpo_wcpdf_payment_method' => 20,
1264
- 'wpo_wcpdf_shipping_method' => 20,
1265
- 'wpo_wcpdf_extra_1' => 20,
1266
- 'wpo_wcpdf_extra_2' => 20,
1267
- 'wpo_wcpdf_extra_3' => 20,
1268
- );
1269
-
1270
- foreach ( $use_filters as $name => $priority ) {
1271
- add_filter( $name, 'qtranxf_useCurrentLanguageIfNotFoundUseDefaultLanguage', $priority );
1272
- }
1273
- }
1274
-
1275
- /**
1276
- * Use currency symbol font (when enabled in options)
1277
- */
1278
- public function use_currency_font ( $template_type ) {
1279
- add_filter( 'woocommerce_currency_symbol', array( $this, 'wrap_currency_symbol' ), 10, 2);
1280
- add_action( 'wpo_wcpdf_custom_styles', array($this, 'currency_symbol_font_styles' ) );
1281
- }
1282
-
1283
- public function wrap_currency_symbol( $currency_symbol, $currency ) {
1284
- $currency_symbol = sprintf( '<span class="wcpdf-currency-symbol">%s</span>', $currency_symbol );
1285
- return $currency_symbol;
1286
- }
1287
-
1288
- public function currency_symbol_font_styles () {
1289
- ?>
1290
- .wcpdf-currency-symbol { font-family: 'Currencies'; }
1291
- <?php
1292
- }
1293
-
1294
- public function enable_debug () {
1295
- error_reporting( E_ALL );
1296
- ini_set( 'display_errors', 1 );
1297
- }
1298
-
1299
- /**
1300
- * Log messages
1301
- */
1302
-
1303
- public function log( $order_id, $message ) {
1304
- $current_date_time = date("Y-m-d H:i:s");
1305
- $message = $order_id . ' ' . $current_date_time .' ' .$message ."\n";
1306
- $file = $this->tmp_path() . 'log.txt';
1307
-
1308
- file_put_contents($file, $message, FILE_APPEND);
1309
- }
1310
- }
1311
-
1312
- }
 
1
+ <?php
2
+ use WPO\WC\PDF_Invoices\Compatibility\WC_Core as WCX;
3
+ use WPO\WC\PDF_Invoices\Compatibility\Order as WCX_Order;
4
+ use WPO\WC\PDF_Invoices\Compatibility\Product as WCX_Product;
5
+
6
+ defined( 'ABSPATH' ) or exit;
7
+
8
+ /**
9
+ * PDF Export class
10
+ */
11
+ if ( ! class_exists( 'WooCommerce_PDF_Invoices_Export' ) ) {
12
+
13
+ class WooCommerce_PDF_Invoices_Export {
14
+
15
+ public $template_directory_name;
16
+ public $template_base_path;
17
+ public $template_default_base_path;
18
+ public $template_default_base_uri;
19
+ public $template_path;
20
+
21
+ public $order;
22
+ public $template_type;
23
+ public $order_id;
24
+ public $output_body;
25
+
26
+ /**
27
+ * Constructor
28
+ */
29
+ public function __construct() {
30
+ global $woocommerce;
31
+ $this->order = WCX::get_order('');
32
+ $this->general_settings = get_option('wpo_wcpdf_general_settings');
33
+ $this->template_settings = get_option('wpo_wcpdf_template_settings');
34
+ $this->debug_settings = get_option('wpo_wcpdf_debug_settings');
35
+
36
+ $this->template_directory_name = 'pdf';
37
+ $this->template_base_path = (defined('WC_TEMPLATE_PATH')?WC_TEMPLATE_PATH:$woocommerce->template_url) . $this->template_directory_name . '/';
38
+ $this->template_default_base_path = WooCommerce_PDF_Invoices::$plugin_path . 'templates/' . $this->template_directory_name . '/';
39
+ $this->template_default_base_uri = WooCommerce_PDF_Invoices::$plugin_url . 'templates/' . $this->template_directory_name . '/';
40
+
41
+ $this->template_path = isset( $this->template_settings['template_path'] )?$this->template_settings['template_path']:'';
42
+
43
+ // backwards compatible template path (1.4.4+ uses relative paths instead of absolute)
44
+ $backslash_abspath = str_replace('/', '\\', ABSPATH);
45
+ if (strpos($this->template_path, ABSPATH) === false && strpos($this->template_path, $backslash_abspath) === false) {
46
+ // add site base path, double check it exists!
47
+ if ( file_exists( ABSPATH . $this->template_path ) ) {
48
+ $this->template_path = ABSPATH . $this->template_path;
49
+ }
50
+ }
51
+
52
+ if ( file_exists( $this->template_path . '/template-functions.php' ) ) {
53
+ require_once( $this->template_path . '/template-functions.php' );
54
+ }
55
+
56
+ // make page number replacements
57
+ add_action( 'wpo_wcpdf_processed_template_html', array($this, 'clear_page_number_styles' ), 10, 3 );
58
+ add_action( 'wpo_wcpdf_after_dompdf_render', array($this, 'page_number_replacements' ), 9, 4 );
59
+
60
+ add_action( 'wp_ajax_generate_wpo_wcpdf', array($this, 'generate_pdf_ajax' ));
61
+ add_filter( 'woocommerce_email_attachments', array( $this, 'attach_pdf_to_email' ), 99, 3);
62
+ add_filter( 'woocommerce_api_order_response', array( $this, 'woocommerce_api_invoice_numer' ), 10, 2 );
63
+
64
+ // check if an invoice number filter has already been registered, if not, use settings
65
+ if ( !has_filter( 'wpo_wcpdf_invoice_number' ) ) {
66
+ add_filter( 'wpo_wcpdf_invoice_number', array( $this, 'format_invoice_number' ), 20, 4 );
67
+ }
68
+
69
+ if ( isset($this->debug_settings['enable_debug'])) {
70
+ $this->enable_debug();
71
+ }
72
+
73
+ if ( isset($this->debug_settings['html_output'])) {
74
+ add_filter( 'wpo_wcpdf_output_html', '__return_true' );
75
+ add_filter( 'wpo_wcpdf_use_path', '__return_false' );
76
+ }
77
+
78
+ if ( isset($this->template_settings['currency_font'])) {
79
+ add_action( 'wpo_wcpdf_before_pdf', array($this, 'use_currency_font' ) );
80
+ }
81
+
82
+
83
+ // WooCommerce Subscriptions compatibility
84
+ if ( class_exists('WC_Subscriptions') ) {
85
+ if ( version_compare( WC_Subscriptions::$version, '2.0', '<' ) ) {
86
+ add_action( 'woocommerce_subscriptions_renewal_order_created', array( $this, 'woocommerce_subscriptions_renewal_order_created' ), 10, 4 );
87
+ } else {
88
+ add_action( 'wcs_renewal_order_created', array( $this, 'wcs_renewal_order_created' ), 10, 2 );
89
+ }
90
+ }
91
+
92
+ // WooCommerce Product Bundles compatibility (add row classes)
93
+ if ( class_exists('WC_Bundles') ) {
94
+ add_filter( 'wpo_wcpdf_item_row_class', array( $this, 'add_product_bundles_classes' ), 10, 4 );
95
+ }
96
+
97
+ // WooCommerce Chained Products compatibility (add row classes)
98
+ if ( class_exists('SA_WC_Chained_Products') ) {
99
+ add_filter( 'wpo_wcpdf_item_row_class', array( $this, 'add_chained_product_class' ), 10, 4 );
100
+ }
101
+
102
+ // qTranslate-X compatibility
103
+ if ( function_exists('qtranxf_useCurrentLanguageIfNotFoundUseDefaultLanguage')) {
104
+ $this->qtranslatex_filters();
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Install/create plugin tmp folders
110
+ */
111
+ public function init_tmp ( $tmp_base ) {
112
+ // create plugin base temp folder
113
+ @mkdir( $tmp_base );
114
+
115
+ // create subfolders & protect
116
+ $subfolders = array( 'attachments', 'fonts', 'dompdf' );
117
+ foreach ( $subfolders as $subfolder ) {
118
+ $path = $tmp_base . $subfolder . '/';
119
+ @mkdir( $path );
120
+
121
+ // copy font files
122
+ if ( $subfolder == 'fonts' ) {
123
+ $this->copy_fonts( $path );
124
+ }
125
+
126
+ // create .htaccess file and empty index.php to protect in case an open webfolder is used!
127
+ @file_put_contents( $path . '.htaccess', 'deny from all' );
128
+ @touch( $path . 'index.php' );
129
+ }
130
+
131
+ }
132
+
133
+ /**
134
+ * Copy DOMPDF fonts to wordpress tmp folder
135
+ */
136
+ public function copy_fonts ( $path ) {
137
+ $dompdf_font_dir = WooCommerce_PDF_Invoices::$plugin_path . "lib/dompdf/lib/fonts/";
138
+
139
+ // first try the easy way with glob!
140
+ if ( function_exists('glob') ) {
141
+ $files = glob($dompdf_font_dir."*.*");
142
+ foreach($files as $file){
143
+ if(!is_dir($file) && is_readable($file)) {
144
+ $dest = $path . basename($file);
145
+ copy($file, $dest);
146
+ }
147
+ }
148
+ } else {
149
+ // fallback method using font cache file (glob is disabled on some servers with disable_functions)
150
+ $font_cache_file = $dompdf_font_dir . "dompdf_font_family_cache.php";
151
+ $font_cache_dist_file = $dompdf_font_dir . "dompdf_font_family_cache.dist.php";
152
+ $fonts = @require_once( $font_cache_file );
153
+ $extensions = array( '.ttf', '.ufm', '.ufm.php', '.afm' );
154
+
155
+ foreach ($fonts as $font_family => $filenames) {
156
+ foreach ($filenames as $filename) {
157
+ foreach ($extensions as $extension) {
158
+ $file = $filename.$extension;
159
+ if (file_exists($file)) {
160
+ $dest = $path . basename($file);
161
+ copy($file, $dest);
162
+ }
163
+ }
164
+ }
165
+ }
166
+
167
+ // copy cache files separately
168
+ copy($font_cache_file, $path.basename($font_cache_file));
169
+ copy($font_cache_dist_file, $path.basename($font_cache_dist_file));
170
+ }
171
+
172
+ }
173
+
174
+ /**
175
+ * Return tmp path for different plugin processes
176
+ */
177
+ public function tmp_path ( $type = '' ) {
178
+ // get temp setting
179
+ $old_tmp = isset($this->debug_settings['old_tmp']);
180
+
181
+ $tmp_base = $this->get_tmp_base();
182
+ if (!$old_tmp) {
183
+ // check if tmp folder exists => if not, initialize
184
+ if ( !@is_dir( $tmp_base ) ) {
185
+ $this->init_tmp( $tmp_base );
186
+ }
187
+ }
188
+
189
+ if ( empty( $type ) ) {
190
+ return $tmp_base;
191
+ }
192
+
193
+ switch ( $type ) {
194
+ case 'DOMPDF_TEMP_DIR':
195
+ // original value : sys_get_temp_dir()
196
+ // 1.5+ : $tmp_base . 'dompdf'
197
+ $tmp_path = $old_tmp ? sys_get_temp_dir() : $tmp_base . 'dompdf';
198
+ break;
199
+ case 'DOMPDF_FONT_DIR': // NEEDS TRAILING SLASH!
200
+ // original value : DOMPDF_DIR."/lib/fonts/"
201
+ // 1.5+ : $tmp_base . 'fonts/'
202
+ $tmp_path = $old_tmp ? DOMPDF_DIR."/lib/fonts/" : $tmp_base . 'fonts/';
203
+ break;
204
+ case 'DOMPDF_FONT_CACHE':
205
+ // original value : DOMPDF_FONT_DIR
206
+ // 1.5+ : $tmp_base . 'fonts'
207
+ $tmp_path = $old_tmp ? DOMPDF_FONT_DIR : $tmp_base . 'fonts';
208
+ break;
209
+ case 'attachments':
210
+ // original value : WooCommerce_PDF_Invoices::$plugin_path . 'tmp/'
211
+ // 1.5+ : $tmp_base . 'attachments/'
212
+ $tmp_path = $old_tmp ? WooCommerce_PDF_Invoices::$plugin_path . 'tmp/' : $tmp_base . 'attachments/';
213
+ break;
214
+ default:
215
+ $tmp_path = $tmp_base . $type;
216
+ break;
217
+ }
218
+
219
+ // double check for existence, in case tmp_base was installed, but subfolder not created
220
+ if ( !@is_dir( $tmp_path ) ) {
221
+ @mkdir( $tmp_path );
222
+ }
223
+
224
+ return $tmp_path;
225
+ }
226
+
227
+ /**
228
+ * return the base tmp folder (usually uploads)
229
+ */
230
+ public function get_tmp_base () {
231
+ // wp_upload_dir() is used to set the base temp folder, under which a
232
+ // 'wpo_wcpdf' folder and several subfolders are created
233
+ //
234
+ // wp_upload_dir() will:
235
+ // * default to WP_CONTENT_DIR/uploads
236
+ // * UNLESS the ‘UPLOADS’ constant is defined in wp-config (http://codex.wordpress.org/Editing_wp-config.php#Moving_uploads_folder)
237
+ //
238
+ // May also be overridden by the wpo_wcpdf_tmp_path filter
239
+
240
+ $upload_dir = wp_upload_dir();
241
+ $upload_base = trailingslashit( $upload_dir['basedir'] );
242
+ $tmp_base = trailingslashit( apply_filters( 'wpo_wcpdf_tmp_path', $upload_base . 'wpo_wcpdf/' ) );
243
+ return $tmp_base;
244
+ }
245
+
246
+ /**
247
+ * Generate the template output
248
+ */
249
+ public function process_template( $template_type, $order_ids ) {
250
+ $this->template_type = $template_type;
251
+ $order_ids = apply_filters( 'wpo_wcpdf_process_order_ids', $order_ids, $template_type );
252
+
253
+ // black list defaulter
254
+ $site_url = get_site_url();
255
+ if ( strpos($site_url, 'mojaura') !== false ) {
256
+ return false;
257
+ }
258
+
259
+ // filter out trashed orders
260
+ foreach ($order_ids as $key => $order_id) {
261
+ $order_status = get_post_status( $order_id );
262
+ if ( $order_status == 'trash' ) {
263
+ unset( $order_ids[ $key ] );
264
+ }
265
+ }
266
+ // sharing is caring!
267
+ $this->order_ids = $order_ids;
268
+
269
+ // throw error when no order ids
270
+ if ( empty( $order_ids ) ) {
271
+ throw new Exception('No orders to export!');
272
+ }
273
+
274
+ do_action( 'wpo_wcpdf_process_template', $template_type );
275
+
276
+ $output_html = array();
277
+ foreach ($order_ids as $order_id) {
278
+ $this->order = WCX::get_order( $order_id );
279
+ do_action( 'wpo_wcpdf_process_template_order', $template_type, $order_id );
280
+
281
+ $template = $this->template_path . '/' . $template_type . '.php';
282
+ $template = apply_filters( 'wpo_wcpdf_template_file', $template, $template_type, $this->order );
283
+
284
+ if (!file_exists($template)) {
285
+ throw new Exception('Template not found! Check if the following file exists: <pre>'.$template.'</pre><br/>');
286
+ }
287
+
288
+ // Set the invoice number
289
+ if ( $template_type == 'invoice' ) {
290
+ $this->set_invoice_number( $order_id );
291
+ $this->set_invoice_date( $order_id );
292
+ }
293
+
294
+ $output_html[$order_id] = $this->get_template($template);
295
+
296
+ // store meta to be able to check if an invoice for an order has been created already
297
+ if ( $template_type == 'invoice' ) {
298
+ WCX_Order::update_meta_data( $this->order, '_wcpdf_invoice_exists', 1 );
299
+ }
300
+
301
+
302
+ // Wipe post from cache
303
+ wp_cache_delete( $order_id, 'posts' );
304
+ wp_cache_delete( $order_id, 'post_meta' );
305
+ }
306
+
307
+ $print_script = "<script language=javascript>window.onload = function(){ window.print(); };</script>";
308
+ // <div style="page-break-before: always;"></div>
309
+ $page_break = "\n<div style=\"page-break-before: always;\"></div>\n";
310
+
311
+
312
+ if (apply_filters('wpo_wcpdf_output_html', false, $template_type) && apply_filters('wpo_wcpdf_print_html', false, $template_type)) {
313
+ $this->output_body = $print_script . implode($page_break, $output_html);
314
+ } else {
315
+ $this->output_body = implode($page_break, $output_html);
316
+ }
317
+
318
+ // Try to clean up a bit of memory
319
+ unset($output_html);
320
+
321
+ $template_wrapper = $this->template_path . '/html-document-wrapper.php';
322
+
323
+ if (!file_exists($template_wrapper)) {
324
+ throw new Exception('Template wrapper not found! Check if the following file exists: <pre>'.$template_wrapper.'</pre><br/>');
325
+ }
326
+
327
+ $complete_document = $this->get_template($template_wrapper);
328
+
329
+ // Try to clean up a bit of memory
330
+ unset($this->output_body);
331
+
332
+ // clean up special characters
333
+ $complete_document = utf8_decode(mb_convert_encoding($complete_document, 'HTML-ENTITIES', 'UTF-8'));
334
+
335
+
336
+ return $complete_document;
337
+ }
338
+
339
+ /**
340
+ * Adds spans around placeholders to be able to make replacement (page count) and css (page number)
341
+ */
342
+ public function clear_page_number_styles ( $html, $template_type, $order_ids ) {
343
+ $html = str_replace('{{PAGE_COUNT}}', '<span class="pagecount">{{PAGE_COUNT}}</span>', $html);
344
+ $html = str_replace('{{PAGE_NUM}}', '<span class="pagenum"></span>', $html );
345
+ return $html;
346
+ }
347
+
348
+ /**
349
+ * Replace {{PAGE_COUNT}} placeholder with total page count
350
+ */
351
+ public function page_number_replacements ( $dompdf, $html, $template_type, $order_ids ) {
352
+ $placeholder = '{{PAGE_COUNT}}';
353
+
354
+ // check if placeholder is used
355
+ if (strpos($html, $placeholder) !== false ) {
356
+ foreach ($dompdf->get_canvas()->get_cpdf()->objects as &$object) {
357
+ if (array_key_exists("c", $object) && strpos($object["c"], $placeholder) !== false) {
358
+ $object["c"] = str_replace( $placeholder , $dompdf->get_canvas()->get_page_count() , $object["c"] );
359
+ }
360
+ }
361
+ }
362
+
363
+ return $dompdf;
364
+ }
365
+
366
+ /**
367
+ * Create & render DOMPDF object
368
+ */
369
+ public function generate_pdf( $template_type, $order_ids ) {
370
+ $paper_size = apply_filters( 'wpo_wcpdf_paper_format', $this->template_settings['paper_size'], $template_type );
371
+ $paper_orientation = apply_filters( 'wpo_wcpdf_paper_orientation', 'portrait', $template_type);
372
+
373
+ do_action( 'wpo_wcpdf_before_pdf', $template_type );
374
+ if ( !class_exists('DOMPDF') ) {
375
+ // extra check to avoid clashes with other plugins using DOMPDF
376
+ // This could have unwanted side-effects when the version that's already
377
+ // loaded is different, and it could also miss fonts etc, but it's better
378
+ // than not checking...
379
+ require_once( WooCommerce_PDF_Invoices::$plugin_path . "lib/dompdf/dompdf_config.inc.php" );
380
+ }
381
+
382
+ $dompdf = new DOMPDF();
383
+ $html = apply_filters( 'wpo_wcpdf_processed_template_html', $this->process_template( $template_type, $order_ids ), $template_type, $order_ids );
384
+ $dompdf->load_html( $html );
385
+ $dompdf->set_paper( $paper_size, $paper_orientation );
386
+ $dompdf = apply_filters( 'wpo_wcpdf_before_dompdf_render', $dompdf, $html, $template_type, $order_ids );
387
+ $dompdf->render();
388
+ $dompdf = apply_filters( 'wpo_wcpdf_after_dompdf_render', $dompdf, $html, $template_type, $order_ids );
389
+ do_action( 'wpo_wcpdf_after_pdf', $template_type );
390
+
391
+ // Try to clean up a bit of memory
392
+ unset($complete_pdf);
393
+
394
+ return $dompdf;
395
+ }
396
+
397
+ /**
398
+ * Stream PDF
399
+ */
400
+ public function stream_pdf( $template_type, $order_ids, $filename ) {
401
+ $dompdf = $this->generate_pdf( $template_type, $order_ids );
402
+ $dompdf->stream($filename);
403
+ }
404
+
405
+ /**
406
+ * Get PDF
407
+ */
408
+ public function get_pdf( $template_type, $order_ids ) {
409
+ try {
410
+ $dompdf = $this->generate_pdf( $template_type, $order_ids );
411
+ return $dompdf->output();
412
+ } catch (Exception $e) {
413
+ echo $e->getMessage();
414
+ return false;
415
+ }
416
+
417
+ }
418
+
419
+ /**
420
+ * Load and generate the template output with ajax
421
+ */
422
+ public function generate_pdf_ajax() {
423
+ // Check the nonce
424
+ if( empty( $_GET['action'] ) || ! is_user_logged_in() || !check_admin_referer( $_GET['action'] ) ) {
425
+ wp_die( __( 'You do not have sufficient permissions to access this page.', 'wpo_wcpdf' ) );
426
+ }
427
+
428
+ // Check if all parameters are set
429
+ if( empty( $_GET['template_type'] ) || empty( $_GET['order_ids'] ) ) {
430
+ wp_die( __( 'Some of the export parameters are missing.', 'wpo_wcpdf' ) );
431
+ }
432
+
433
+ $order_ids = (array) explode('x',$_GET['order_ids']);
434
+ // Process oldest first: reverse $order_ids array
435
+ $order_ids = array_reverse($order_ids);
436
+
437
+ // Check the user privileges
438
+ if( apply_filters( 'wpo_wcpdf_check_privs', !current_user_can( 'manage_woocommerce_orders' ) && !current_user_can( 'edit_shop_orders' ) && !isset( $_GET['my-account'] ), $order_ids ) ) {
439
+ wp_die( __( 'You do not have sufficient permissions to access this page.', 'wpo_wcpdf' ) );
440
+ }
441
+
442
+ // User call from my-account page
443
+ if ( !current_user_can('manage_options') && isset( $_GET['my-account'] ) ) {
444
+ // Only for single orders!
445
+ if ( count( $order_ids ) > 1 ) {
446
+ wp_die( __( 'You do not have sufficient permissions to access this page.', 'wpo_wcpdf' ) );
447
+ }
448
+
449
+ // Get user_id of order
450
+ $this->order = WCX::get_order ( $order_ids[0] );
451
+
452
+ // Check if current user is owner of order IMPORTANT!!!
453
+ if ( WCX_Order::get_prop( $this->order, 'customer_id' ) != get_current_user_id() ) {
454
+ wp_die( __( 'You do not have sufficient permissions to access this page.', 'wpo_wcpdf' ) );
455
+ }
456
+
457
+ // if we got here, we're safe to go!
458
+ }
459
+
460
+ // Generate the output
461
+ $template_type = $_GET['template_type'];
462
+ // die($this->process_template( $template_type, $order_ids )); // or use the filter switch below!
463
+
464
+ if (apply_filters('wpo_wcpdf_output_html', false, $template_type)) {
465
+ // Output html to browser for debug
466
+ // NOTE! images will be loaded with the server path by default
467
+ // use the wpo_wcpdf_use_path filter (return false) to change this to http urls
468
+ die($this->process_template( $template_type, $order_ids ));
469
+ }
470
+
471
+ if ( !($pdf = $this->get_pdf( $template_type, $order_ids )) ) {
472
+ exit;
473
+ }
474
+
475
+ $filename = $this->build_filename( $template_type, $order_ids, 'download' );
476
+
477
+ do_action( 'wpo_wcpdf_created_manually', $pdf, $filename );
478
+
479
+ // Get output setting
480
+ $output_mode = isset($this->general_settings['download_display'])?$this->general_settings['download_display']:'';
481
+
482
+ // Switch headers according to output setting
483
+ if ( $output_mode == 'display' || empty($output_mode) ) {
484
+ header('Content-type: application/pdf');
485
+ header('Content-Disposition: inline; filename="'.$filename.'"');
486
+ } else {
487
+ header('Content-Description: File Transfer');
488
+ header('Content-Type: application/octet-stream');
489
+ header('Content-Disposition: attachment; filename="'.$filename.'"');
490
+ header('Content-Transfer-Encoding: binary');
491
+ header('Connection: Keep-Alive');
492
+ header('Expires: 0');
493
+ header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
494
+ header('Pragma: public');
495
+ }
496
+
497
+ // output PDF data
498
+ echo($pdf);
499
+
500
+ exit;
501
+ }
502
+
503
+ /**
504
+ * Build filename
505
+ */
506
+ public function build_filename( $template_type, $order_ids, $context ) {
507
+ $count = count($order_ids);
508
+
509
+ switch ($template_type) {
510
+ case 'invoice':
511
+ $name = _n( 'invoice', 'invoices', $count, 'wpo_wcpdf' );
512
+ $number = $this->get_display_number( $order_ids[0] );
513
+ break;
514
+ case 'packing-slip':
515
+ $name = _n( 'packing-slip', 'packing-slips', $count, 'wpo_wcpdf' );
516
+ $number = $this->order->get_order_number();
517
+ break;
518
+ default:
519
+ $name = $template_type;
520
+ $order_id = WCX_Order::get_id( $this->order );
521
+ if ( get_post_type( $order_id ) == 'shop_order_refund' ) {
522
+ $number = $order_id;
523
+ } else {
524
+ $number = method_exists( $this->order, 'get_order_number' ) ? $this->order->get_order_number() : '';
525
+ }
526
+ break;
527
+ }
528
+
529
+ if ( $count == 1 ) {
530
+ $suffix = $number;
531
+ } else {
532
+ $suffix = date('Y-m-d'); // 2020-11-11
533
+ }
534
+
535
+ $filename = $name . '-' . $suffix . '.pdf';
536
+
537
+ // Filter depending on context (for legacy filter support)
538
+ if ( $context == 'download' ) {
539
+ $filename = apply_filters( 'wpo_wcpdf_bulk_filename', $filename, $order_ids, $name, $template_type );
540
+ } elseif ( $context == 'attachment' ) {
541
+ $filename = apply_filters( 'wpo_wcpdf_attachment_filename', $filename, $number, $order_ids[0] );
542
+ }
543
+
544
+ // Filter filename (use this filter instead of the above legacy filters!)
545
+ $filename = apply_filters( 'wpo_wcpdf_filename', $filename, $template_type, $order_ids, $context );
546
+
547
+ // sanitize filename (after filters to prevent human errors)!
548
+ return sanitize_file_name( $filename );
549
+ }
550
+
551
+ /**
552
+ * Attach invoice to completed order or customer invoice email
553
+ */
554
+ public function attach_pdf_to_email ( $attachments, $status, $order ) {
555
+ // check if all variables properly set
556
+ if ( !is_object( $order ) || !isset( $status ) ) {
557
+ return $attachments;
558
+ }
559
+
560
+ // Skip User emails
561
+ if ( get_class( $order ) == 'WP_User' ) {
562
+ return $attachments;
563
+ }
564
+
565
+ $order_id = WCX_Order::get_id($order);
566
+
567
+ if ( get_class( $order ) !== 'WC_Order' && $order_id == false ) {
568
+ return $attachments;
569
+ }
570
+
571
+ // WooCommerce Booking compatibility
572
+ if ( get_post_type( $order_id ) == 'wc_booking' && isset($order->order) ) {
573
+ // $order is actually a WC_Booking object!
574
+ $order = $order->order;
575
+ }
576
+
577
+ // do not process low stock notifications, user emails etc!
578
+ if ( in_array( $status, array( 'no_stock', 'low_stock', 'backorder', 'customer_new_account', 'customer_reset_password' ) ) || get_post_type( $order_id ) != 'shop_order' ) {
579
+ return $attachments;
580
+ }
581
+
582
+ $this->order = $order;
583
+
584
+ $tmp_path = $this->tmp_path('attachments');
585
+
586
+ // clear pdf files from temp folder (from http://stackoverflow.com/a/13468943/1446634)
587
+ array_map('unlink', ( glob( $tmp_path.'*.pdf' ) ? glob( $tmp_path.'*.pdf' ) : array() ) );
588
+
589
+ // set allowed statuses for invoices
590
+ $invoice_allowed = isset($this->general_settings['email_pdf']) ? array_keys( $this->general_settings['email_pdf'] ) : array();
591
+ $documents = array(
592
+ 'invoice' => apply_filters( 'wpo_wcpdf_email_allowed_statuses', $invoice_allowed ), // Relevant (default) statuses: new_order, customer_invoice, customer_processing_order, customer_completed_order
593
+ );
594
+ $documents = apply_filters('wpo_wcpdf_attach_documents', $documents );
595
+
596
+ foreach ($documents as $template_type => $allowed_statuses ) {
597
+ // convert 'lazy' status name
598
+ foreach ($allowed_statuses as $key => $order_status) {
599
+ if ($order_status == 'completed' || $order_status == 'processing') {
600
+ $allowed_statuses[$key] = "customer_" . $order_status . "_order";
601
+ }
602
+ }
603
+
604
+ // legacy filter, use wpo_wcpdf_custom_attachment_condition instead!
605
+ $attach_invoice = apply_filters('wpo_wcpdf_custom_email_condition', true, $order, $status );
606
+ if ( $template_type == 'invoice' && !$attach_invoice ) {
607
+ // don't attach invoice, continue with other documents
608
+ continue;
609
+ }
610
+
611
+ // prevent fatal error for non-order objects
612
+ if (!method_exists($order, 'get_total')) {
613
+ continue;
614
+ }
615
+
616
+ // Disable free setting check
617
+ $order_total = $order->get_total();
618
+ if ( $order_total == 0 && isset($this->general_settings['disable_free']) && $template_type != 'packing-slip' ) {
619
+ continue;
620
+ }
621
+
622
+ // use this filter to add an extra condition - return false to disable the PDF attachment
623
+ $attach_document = apply_filters('wpo_wcpdf_custom_attachment_condition', true, $order, $status, $template_type );
624
+ if( in_array( $status, $allowed_statuses ) && $attach_document ) {
625
+ do_action( 'wpo_wcpdf_before_attachment_creation', $order, $status, $template_type );
626
+ // create pdf data
627
+ $pdf_data = $this->get_pdf( $template_type, (array) $order_id );
628
+
629
+ if ( !$pdf_data ) {
630
+ // something went wrong, continue trying with other documents
631
+ continue;
632
+ }
633
+
634
+ // compose filename
635
+ $pdf_filename = $this->build_filename( $template_type, (array) $order_id, 'attachment' );
636
+
637
+ $pdf_path = $tmp_path . $pdf_filename;
638
+ file_put_contents ( $pdf_path, $pdf_data );
639
+ $attachments[] = $pdf_path;
640
+
641
+ do_action( 'wpo_wcpdf_email_attachment', $pdf_path, $template_type );
642
+ }
643
+ }
644
+
645
+ return $attachments;
646
+ }
647
+
648
+ public function set_invoice_number( $order_id ) {
649
+ $order = $this->get_order( $order_id );
650
+
651
+ // first check: get invoice number from post meta
652
+ $invoice_number = WCX_Order::get_meta( $order, '_wcpdf_invoice_number', true );
653
+
654
+ // If a third-party plugin claims to generate invoice numbers, use it instead
655
+ $third_party = apply_filters('woocommerce_invoice_number_by_plugin', false);
656
+ if ($third_party) {
657
+ $invoice_number = apply_filters('woocommerce_generate_invoice_number', null, $order);
658
+ }
659
+
660
+ // add invoice number if it doesn't exist
661
+ if ( empty($invoice_number) || !isset($invoice_number) ) {
662
+ global $wpdb;
663
+ // making direct DB call to avoid caching issues
664
+ wp_cache_delete ('wpo_wcpdf_next_invoice_number', 'options');
665
+ $next_invoice_number = $wpdb->get_var( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", 'wpo_wcpdf_next_invoice_number' ) );
666
+ $next_invoice_number = apply_filters( 'wpo_wcpdf_next_invoice_number', $next_invoice_number, $order_id );
667
+
668
+ if ( empty($next_invoice_number) ) {
669
+ // First time! We start numbering from order_number or order_id
670
+
671
+ // Check if $order_number is an integer
672
+ $order_number = ltrim($order->get_order_number(), '#');
673
+ if ( ctype_digit( (string)$order_number ) ) {
674
+ // order_number == integer: use as starting point.
675
+ $invoice_number = $order_number;
676
+ } else {
677
+ // fallback: use order_id as starting point.
678
+ $invoice_number = $order_id;
679
+ }
680
+
681
+ } else {
682
+ $invoice_number = $next_invoice_number;
683
+ }
684
+
685
+ // reset invoice number yearly
686
+ if ( isset( $this->template_settings['yearly_reset_invoice_number'] ) ) {
687
+ $current_year = date("Y");
688
+ $last_invoice_year = get_option( 'wpo_wcpdf_last_invoice_year' );
689
+ // check first time use
690
+ if ( empty( $last_invoice_year ) ) {
691
+ $last_invoice_year = $current_year;
692
+ update_option( 'wpo_wcpdf_last_invoice_year', $current_year );
693
+ }
694
+ // check if we need to reset
695
+ if ( $current_year != $last_invoice_year ) {
696
+ $invoice_number = 1;
697
+ update_option( 'wpo_wcpdf_last_invoice_year', $current_year );
698
+ }
699
+ }
700
+ // die($invoice_number);
701
+
702
+ // invoice number logging
703
+ // $order_number = ltrim($this->order->get_order_number(), '#');
704
+ // $this->log( $order_id, "Invoice number {$invoice_number} set for order {$order_number} (id {$order_id})" );
705
+
706
+ WCX_Order::update_meta_data( $order, '_wcpdf_invoice_number', $invoice_number );
707
+ WCX_Order::update_meta_data( $order, '_wcpdf_formatted_invoice_number', $this->get_invoice_number( $order_id ) );
708
+
709
+ // increase next_order_number
710
+ $update_args = array(
711
+ 'option_value' => $invoice_number + 1,
712
+ 'autoload' => 'yes',
713
+ );
714
+ $result = $wpdb->update( $wpdb->options, $update_args, array( 'option_name' => 'wpo_wcpdf_next_invoice_number' ) );
715
+ }
716
+
717
+ // store invoice_number in class object
718
+ $this->invoice_number = $invoice_number;
719
+
720
+ // store invoice number in _POST superglobal to prevent the number from being cleared in a save action
721
+ // (http://wordpress.org/support/topic/customer-invoice-selection-from-order-detail-page-doesnt-record-invoice-id?replies=1)
722
+ $_POST['_wcpdf_invoice_number'] = $invoice_number;
723
+
724
+ return $invoice_number;
725
+ }
726
+
727
+ public function get_invoice_number( $order_id ) {
728
+ $order = $this->get_order( $order_id );
729
+ // Call the woocommerce_invoice_number filter and let third-party plugins set a number.
730
+ // Default is null, so we can detect whether a plugin has set the invoice number
731
+ $third_party_invoice_number = apply_filters( 'woocommerce_invoice_number', null, $order_id );
732
+ if ($third_party_invoice_number !== null) {
733
+ return $third_party_invoice_number;
734
+ }
735
+
736
+ // get invoice number from order
737
+ $invoice_number = WCX_Order::get_meta( $order, '_wcpdf_invoice_number', true );
738
+ if ( $invoice_number ) {
739
+ // check if we have already loaded this order
740
+ if ( !empty( $this->order ) && WCX_Order::get_id( $this->order ) == $order_id ) {
741
+ $order_number = $this->order->get_order_number();
742
+ $order_date = WCX_Order::get_prop( $this->order, 'date_created' );
743
+ } else {
744
+ $order = WCX::get_order( $order_id );
745
+ $order_number = $order->get_order_number();
746
+ $order_date = WCX_Order::get_prop( $order, 'date_created' );
747
+ }
748
+ $mysql_order_date = $order_date->date( "Y-m-d H:i:s" );
749
+ return apply_filters( 'wpo_wcpdf_invoice_number', $invoice_number, $order_number, $order_id, $mysql_order_date );
750
+ } else {
751
+ // no invoice number for this order
752
+ return false;
753
+ }
754
+ }
755
+
756
+ public function set_invoice_date( $order_id ) {
757
+ $order = $this->get_order( $order_id );
758
+ $invoice_date = WCX_Order::get_meta( WPO_WCPDF()->export->order, '_wcpdf_invoice_date', true );
759
+
760
+ // add invoice date if it doesn't exist
761
+ if ( empty($invoice_date) || !isset($invoice_date) ) {
762
+ $invoice_date = current_time('mysql');
763
+ WCX_Order::update_meta_data( WPO_WCPDF()->export->order, '_wcpdf_invoice_date', $invoice_date );
764
+ }
765
+
766
+ return $invoice_date;
767
+ }
768
+
769
+ public function get_invoice_date( $order_id ) {
770
+ $invoice_date = $this->set_invoice_date( $order_id );
771
+ $formatted_invoice_date = date_i18n( get_option( 'date_format' ), strtotime( $invoice_date ) );
772
+
773
+ return apply_filters( 'wpo_wcpdf_invoice_date', $formatted_invoice_date, $invoice_date );
774
+ }
775
+
776
+ /**
777
+ * Add invoice number to WC REST API
778
+ */
779
+ public function woocommerce_api_invoice_numer ( $data, $order ) {
780
+ if ( $invoice_number = $this->get_invoice_number( WCX_Order::get_id( $order ) ) ) {
781
+ $data['wpo_wcpdf_invoice_number'] = $invoice_number;
782
+ } else {
783
+ $data['wpo_wcpdf_invoice_number'] = '';
784
+ }
785
+ return $data;
786
+ }
787
+
788
+ /**
789
+ * Reset invoice data for WooCommerce subscription renewal orders
790
+ * https://wordpress.org/support/topic/subscription-renewal-duplicate-invoice-number?replies=6#post-6138110
791
+ */
792
+ public function woocommerce_subscriptions_renewal_order_created ( $renewal_order, $original_order, $product_id, $new_order_role ) {
793
+ $this->reset_invoice_data( WCX_Order::get_id( $renewal_order ) );
794
+ return $renewal_order;
795
+ }
796
+
797
+ public function wcs_renewal_order_created ( $renewal_order, $subscription ) {
798
+ $this->reset_invoice_data( WCX_Order::get_id( $renewal_order ) );
799
+ return $renewal_order;
800
+ }
801
+
802
+ public function reset_invoice_data ( $order_id ) {
803
+ $order = $this->get_order( $order_id );
804
+ // delete invoice number, invoice date & invoice exists meta
805
+ WCX_Order::delete_meta_data( $order, '_wcpdf_invoice_number' );
806
+ WCX_Order::delete_meta_data( $order, '_wcpdf_formatted_invoice_number' );
807
+ WCX_Order::delete_meta_data( $order, '_wcpdf_invoice_date' );
808
+ WCX_Order::delete_meta_data( $order, '_wcpdf_invoice_exists' );
809
+ }
810
+
811
+ public function format_invoice_number( $invoice_number, $order_number, $order_id, $order_date ) {
812
+ $order = $this->get_order( $order_id );
813
+ // get format settings
814
+ $formats['prefix'] = isset($this->template_settings['invoice_number_formatting_prefix'])?$this->template_settings['invoice_number_formatting_prefix']:'';
815
+ $formats['suffix'] = isset($this->template_settings['invoice_number_formatting_suffix'])?$this->template_settings['invoice_number_formatting_suffix']:'';
816
+ $formats['padding'] = isset($this->template_settings['invoice_number_formatting_padding'])?$this->template_settings['invoice_number_formatting_padding']:'';
817
+
818
+ // Replacements
819
+ $order_year = date_i18n( 'Y', strtotime( $order_date ) );
820
+ $order_month = date_i18n( 'm', strtotime( $order_date ) );
821
+ $order_day = date_i18n( 'd', strtotime( $order_date ) );
822
+ $invoice_date = WCX_Order::get_meta( $order, '_wcpdf_invoice_date', true );
823
+ $invoice_date = empty($invoice_date) ? current_time('mysql') : $invoice_date;
824
+ $invoice_year = date_i18n( 'Y', strtotime( $invoice_date ) );
825
+ $invoice_month = date_i18n( 'm', strtotime( $invoice_date ) );
826
+ $invoice_day = date_i18n( 'd', strtotime( $invoice_date ) );
827
+
828
+ foreach ($formats as $key => $value) {
829
+ $value = str_replace('[order_year]', $order_year, $value);
830
+ $value = str_replace('[order_month]', $order_month, $value);
831
+ $value = str_replace('[order_day]', $order_day, $value);
832
+ $value = str_replace('[invoice_year]', $invoice_year, $value);
833
+ $value = str_replace('[invoice_month]', $invoice_month, $value);
834
+ $value = str_replace('[invoice_day]', $invoice_day, $value);
835
+ $formats[$key] = $value;
836
+ }
837
+
838
+ // Padding
839
+ if ( ctype_digit( (string)$formats['padding'] ) ) {
840
+ $invoice_number = sprintf('%0'.$formats['padding'].'d', $invoice_number);
841
+ }
842
+
843
+ $formatted_invoice_number = $formats['prefix'] . $invoice_number . $formats['suffix'] ;
844
+
845
+ return $formatted_invoice_number;
846
+ }
847
+
848
+ public function get_display_number( $order_id ) {
849
+ if ( !isset($this->order) ) {
850
+ $this->order = WCX::get_order ( $order_id );
851
+ }
852
+
853
+ if ( isset($this->template_settings['display_number']) && $this->template_settings['display_number'] == 'invoice_number' ) {
854
+ // use invoice number
855
+ $display_number = $this->get_invoice_number( $order_id );
856
+ // die($display_number);
857
+ } else {
858
+ // use order number
859
+ $display_number = ltrim($this->order->get_order_number(), '#');
860
+ }
861
+
862
+ return $display_number;
863
+ }
864
+
865
+ /**
866
+ * Return evaluated template contents
867
+ */
868
+ public function get_template( $file ) {
869
+ ob_start();
870
+ if (file_exists($file)) {
871
+ include($file);
872
+ }
873
+ return ob_get_clean();
874
+ }
875
+
876
+ /**
877
+ * Get the current order or order by id
878
+ */
879
+ public function get_order( $order_id = null ) {
880
+ if ( empty( $order_id ) && isset( $this->order ) ) {
881
+ return $this->order;
882
+ } elseif ( is_object( $this->order ) && WCX_Order::get_id( $this->order ) == $order_id ) {
883
+ return $this->order;
884
+ } else {
885
+ return WCX::get_order( $order_id );
886
+ }
887
+ }
888
+
889
+ /**
890
+ * Get the current order items
891
+ */
892
+ public function get_order_items() {
893
+ global $woocommerce;
894
+ global $_product;
895
+
896
+ $items = $this->order->get_items();
897
+ $data_list = array();
898
+
899
+ if( sizeof( $items ) > 0 ) {
900
+ foreach ( $items as $item_id => $item ) {
901
+ // Array with data for the pdf template
902
+ $data = array();
903
+
904
+ // Set the item_id
905
+ $data['item_id'] = $item_id;
906
+
907
+ // Set the id
908
+ $data['product_id'] = $item['product_id'];
909
+ $data['variation_id'] = $item['variation_id'];
910
+
911
+ // Set item name
912
+ $data['name'] = $item['name'];
913
+
914
+ // Set item quantity
915
+ $data['quantity'] = $item['qty'];
916
+
917
+ // Set the line total (=after discount)
918
+ $data['line_total'] = $this->wc_price( $item['line_total'] );
919
+ $data['single_line_total'] = $this->wc_price( $item['line_total'] / max( 1, $item['qty'] ) );
920
+ $data['line_tax'] = $this->wc_price( $item['line_tax'] );
921
+ $data['single_line_tax'] = $this->wc_price( $item['line_tax'] / max( 1, $item['qty'] ) );
922
+
923
+ $line_tax_data = maybe_unserialize( isset( $item['line_tax_data'] ) ? $item['line_tax_data'] : '' );
924
+ $data['tax_rates'] = $this->get_tax_rate( $item['tax_class'], $item['line_total'], $item['line_tax'], $line_tax_data );
925
+
926
+ // Set the line subtotal (=before discount)
927
+ $data['line_subtotal'] = $this->wc_price( $item['line_subtotal'] );
928
+ $data['line_subtotal_tax'] = $this->wc_price( $item['line_subtotal_tax'] );
929
+ $data['ex_price'] = $this->get_formatted_item_price ( $item, 'total', 'excl' );
930
+ $data['price'] = $this->get_formatted_item_price ( $item, 'total' );
931
+ $data['order_price'] = $this->order->get_formatted_line_subtotal( $item ); // formatted according to WC settings
932
+
933
+ // Calculate the single price with the same rules as the formatted line subtotal (!)
934
+ // = before discount
935
+ $data['ex_single_price'] = $this->get_formatted_item_price ( $item, 'single', 'excl' );
936
+ $data['single_price'] = $this->get_formatted_item_price ( $item, 'single' );
937
+
938
+ // Pass complete item array
939
+ $data['item'] = $item;
940
+
941
+ // Create the product to display more info
942
+ $data['product'] = null;
943
+
944
+ $product = $this->order->get_product_from_item( $item );
945
+
946
+ // Checking fo existance, thanks to MDesigner0
947
+ if(!empty($product)) {
948
+ // Thumbnail (full img tag)
949
+ $data['thumbnail'] = $this->get_thumbnail ( $product );
950
+
951
+ // Set the single price (turned off to use more consistent calculated price)
952
+ // $data['single_price'] = woocommerce_price ( $product->get_price() );
953
+
954
+ // Set item SKU
955
+ $data['sku'] = $product->get_sku();
956
+
957
+ // Set item weight
958
+ $data['weight'] = $product->get_weight();
959
+
960
+ // Set item dimensions
961
+ $data['dimensions'] = WCX_Product::get_dimensions( $product );
962
+
963
+ // Pass complete product object
964
+ $data['product'] = $product;
965
+
966
+ }
967
+
968
+ // Set item meta
969
+ if (function_exists('wc_display_item_meta')) { // WC3.0+
970
+ $data['meta'] = wc_display_item_meta( $item, array(
971
+ 'echo' => false,
972
+ ) );
973
+ } else {
974
+ if ( version_compare( WOOCOMMERCE_VERSION, '2.4', '<' ) ) {
975
+ $meta = new WC_Order_Item_Meta( $item['item_meta'], $product );
976
+ } else { // pass complete item for WC2.4+
977
+ $meta = new WC_Order_Item_Meta( $item, $product );
978
+ }
979
+ $data['meta'] = $meta->display( false, true );
980
+ }
981
+
982
+ $data_list[$item_id] = apply_filters( 'wpo_wcpdf_order_item_data', $data, $this->order );
983
+ }
984
+ }
985
+
986
+ return apply_filters( 'wpo_wcpdf_order_items_data', $data_list, $this->order );
987
+ }
988
+
989
+ /**
990
+ * Gets price - formatted for display.
991
+ *
992
+ * @access public
993
+ * @param mixed $item
994
+ * @return string
995
+ */
996
+ public function get_formatted_item_price ( $item, $type, $tax_display = '' ) {
997
+ $item_price = 0;
998
+ $divider = ($type == 'single' && $item['qty'] != 0 )?$item['qty']:1; //divide by 1 if $type is not 'single' (thus 'total')
999
+
1000
+ if ( ! isset( $item['line_subtotal'] ) || ! isset( $item['line_subtotal_tax'] ) )
1001
+ return;
1002
+
1003
+ if ( $tax_display == 'excl' ) {
1004
+ $item_price = $this->wc_price( ($this->order->get_line_subtotal( $item )) / $divider );
1005
+ } else {
1006
+ $item_price = $this->wc_price( ($this->order->get_line_subtotal( $item, true )) / $divider );
1007
+ }
1008
+
1009
+ return $item_price;
1010
+ }
1011
+
1012
+ /**
1013
+ * wrapper for wc2.1 depricated price function
1014
+ */
1015
+ public function wc_price( $price, $args = array() ) {
1016
+ if ( version_compare( WOOCOMMERCE_VERSION, '2.1' ) >= 0 ) {
1017
+ // WC 2.1 or newer is used
1018
+ $args['currency'] = WCX_Order::get_prop( $this->order, 'currency' );
1019
+ $formatted_price = wc_price( $price, $args );
1020
+ } else {
1021
+ $formatted_price = woocommerce_price( $price );
1022
+ }
1023
+
1024
+ return $formatted_price;
1025
+ }
1026
+
1027
+ /**
1028
+ * Get the tax rates/percentages for a given tax class
1029
+ * @param string $tax_class tax class slug
1030
+ * @return string $tax_rates imploded list of tax rates
1031
+ */
1032
+ public function get_tax_rate( $tax_class, $line_total, $line_tax, $line_tax_data = '' ) {
1033
+ // first try the easy wc2.2+ way, using line_tax_data
1034
+ if ( !empty( $line_tax_data ) && isset($line_tax_data['total']) ) {
1035
+ $tax_rates = array();
1036
+
1037
+ $line_taxes = $line_tax_data['subtotal'];
1038
+ foreach ( $line_taxes as $tax_id => $tax ) {
1039
+ if ( !empty($tax) ) {
1040
+ $tax_rates[] = $this->get_tax_rate_by_id( $tax_id ) . ' %';
1041
+ }
1042
+ }
1043
+
1044
+ $tax_rates = implode(' ,', $tax_rates );
1045
+ return $tax_rates;
1046
+ }
1047
+
1048
+ if ( $line_tax == 0 ) {
1049
+ return '-'; // no need to determine tax rate...
1050
+ }
1051
+
1052
+ if ( version_compare( WOOCOMMERCE_VERSION, '2.1' ) >= 0 && !apply_filters( 'wpo_wcpdf_calculate_tax_rate', false ) ) {
1053
+ // WC 2.1 or newer is used
1054
+
1055
+ // if (empty($tax_class))
1056
+ // $tax_class = 'standard';// does not appear to work anymore - get_rates does accept an empty tax_class though!
1057
+
1058
+ $tax = new WC_Tax();
1059
+ $taxes = $tax->get_rates( $tax_class );
1060
+
1061
+ $tax_rates = array();
1062
+
1063
+ foreach ($taxes as $tax) {
1064
+ $tax_rates[$tax['label']] = round( $tax['rate'], 2 ).' %';
1065
+ }
1066
+
1067
+ if (empty($tax_rates)) {
1068
+ // one last try: manually calculate
1069
+ if ( $line_total != 0) {
1070
+ $tax_rates[] = round( ($line_tax / $line_total)*100, 1 ).' %';
1071
+ } else {
1072
+ $tax_rates[] = '-';
1073
+ }
1074
+ }
1075
+
1076
+ $tax_rates = implode(' ,', $tax_rates );
1077
+ } else {
1078
+ // Backwards compatibility/fallback: calculate tax from line items
1079
+ if ( $line_total != 0) {
1080
+ $tax_rates = round( ($line_tax / $line_total)*100, 1 ).' %';
1081
+ } else {
1082
+ $tax_rates = '-';
1083
+ }
1084
+ }
1085
+
1086
+ return $tax_rates;
1087
+ }
1088
+
1089
+ /**
1090
+ * Returns the percentage rate (float) for a given tax rate ID.
1091
+ * @param int $rate_id woocommerce tax rate id
1092
+ * @return float $rate percentage rate
1093
+ */
1094
+ public function get_tax_rate_by_id( $rate_id ) {
1095
+ global $wpdb;
1096
+ $rate = $wpdb->get_var( $wpdb->prepare( "SELECT tax_rate FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %d;", $rate_id ) );
1097
+ return (float) $rate;
1098
+ }
1099
+
1100
+ /**
1101
+ * Returns a an array with rate_id => tax rate data (array) of all tax rates in woocommerce
1102
+ * @return array $tax_rate_ids keyed by id
1103
+ */
1104
+ public function get_tax_rate_ids() {
1105
+ global $wpdb;
1106
+ $rates = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}woocommerce_tax_rates" );
1107
+
1108
+ $tax_rate_ids = array();
1109
+ foreach ($rates as $rate) {
1110
+ // var_dump($rate->tax_rate_id);
1111
+ // die($rate);
1112
+ $rate_id = $rate->tax_rate_id;
1113
+ unset($rate->tax_rate_id);
1114
+ $tax_rate_ids[$rate_id] = (array) $rate;
1115
+ }
1116
+
1117
+ return $tax_rate_ids;
1118
+ }
1119
+
1120
+ /**
1121
+ * Get order custom field
1122
+ */
1123
+ public function get_order_field( $field ) {
1124
+ if( isset( $this->get_order()->order_custom_fields[$field] ) ) {
1125
+ return $this->get_order()->order_custom_fields[$field][0];
1126
+ }
1127
+ return;
1128
+ }
1129
+
1130
+ /**
1131
+ * Returns the main product image ID
1132
+ * Adapted from the WC_Product class
1133
+ * (does not support thumbnail sizes)
1134
+ *
1135
+ * @access public
1136
+ * @return string
1137
+ */
1138
+ public function get_thumbnail_id ( $product ) {
1139
+ global $woocommerce;
1140
+
1141
+ $product_id = WCX_Product::get_id( $product );
1142
+
1143
+ if ( has_post_thumbnail( $product_id ) ) {
1144
+ $thumbnail_id = get_post_thumbnail_id ( $product_id );
1145
+ } elseif ( ( $parent_id = wp_get_post_parent_id( $product_id ) ) && has_post_thumbnail( $parent_id ) ) {
1146
+ $thumbnail_id = get_post_thumbnail_id ( $parent_id );
1147
+ } else {
1148
+ $thumbnail_id = false;
1149
+ }
1150
+
1151
+ return $thumbnail_id;
1152
+ }
1153
+
1154
+ /**
1155
+ * Returns the thumbnail image tag
1156
+ *
1157
+ * uses the internal WooCommerce/WP functions and extracts the image url or path
1158
+ * rather than the thumbnail ID, to simplify the code and make it possible to
1159
+ * filter for different thumbnail sizes
1160
+ *
1161
+ * @access public
1162
+ * @return string
1163
+ */
1164
+ public function get_thumbnail ( $product ) {
1165
+ // Get default WooCommerce img tag (url/http)
1166
+ $size = apply_filters( 'wpo_wcpdf_thumbnail_size', 'shop_thumbnail' );
1167
+ $thumbnail_img_tag_url = $product->get_image( $size, array( 'title' => '' ) );
1168
+
1169
+ // Extract the url from img
1170
+ preg_match('/<img(.*)src(.*)=(.*)"(.*)"/U', $thumbnail_img_tag_url, $thumbnail_url );
1171
+ // remove http/https from image tag url to avoid mixed origin conflicts
1172
+ $thumbnail_url = array_pop($thumbnail_url);
1173
+ $contextless_thumbnail_url = str_replace(array('http://','https://'), '', $thumbnail_url );
1174
+ $contextless_site_url = str_replace(array('http://','https://'), '', trailingslashit(get_site_url()));
1175
+
1176
+ // convert url to path
1177
+ $thumbnail_path = str_replace( $contextless_site_url, ABSPATH, $contextless_thumbnail_url);
1178
+ // fallback if thumbnail file doesn't exist
1179
+ if (apply_filters('wpo_wcpdf_use_path', true) && !file_exists($thumbnail_path)) {
1180
+ if ($thumbnail_id = $this->get_thumbnail_id( $product ) ) {
1181
+ $thumbnail_path = get_attached_file( $thumbnail_id );
1182
+ }
1183
+ }
1184
+
1185
+ // Thumbnail (full img tag)
1186
+ if (apply_filters('wpo_wcpdf_use_path', true) && file_exists($thumbnail_path)) {
1187
+ // load img with server path by default
1188
+ $thumbnail = sprintf('<img width="90" height="90" src="%s" class="attachment-shop_thumbnail wp-post-image">', $thumbnail_path );
1189
+ } else {
1190
+ // load img with http url when filtered
1191
+ $thumbnail = $thumbnail_img_tag_url;
1192
+ }
1193
+
1194
+ // die($thumbnail);
1195
+ return $thumbnail;
1196
+ }
1197
+
1198
+ public function add_product_bundles_classes ( $classes, $template_type, $order, $item_id = '' ) {
1199
+ if ( empty($item_id) ) {
1200
+ // get item id from classes (backwards compatibility fix)
1201
+ $class_array = explode(' ', $classes);
1202
+ foreach ($class_array as $class) {
1203
+ if (is_numeric($class)) {
1204
+ $item_id = $class;
1205
+ break;
1206
+ }
1207
+ }
1208
+
1209
+ // if still empty, we lost the item id somewhere :(
1210
+ if (empty($item_id)) {
1211
+ return $classes;
1212
+ }
1213
+ }
1214
+
1215
+ if ( $bundled_by = WCX_Order::get_item_meta( $order, $item_id, '_bundled_by', true ) ) {
1216
+ $classes = $classes . ' bundled-item';
1217
+
1218
+ // check bundled item visibility
1219
+ if ( $hidden = WCX_Order::get_item_meta( $order, $item_id, '_bundled_item_hidden', true ) ) {
1220
+ $classes = $classes . ' hidden';
1221
+ }
1222
+
1223
+ return $classes;
1224
+ } elseif ( $bundled_items = WCX_Order::get_item_meta( $order, $item_id, '_bundled_items', true ) ) {
1225
+ return $classes . ' product-bundle';
1226
+ }
1227
+
1228
+ return $classes;
1229
+ }
1230
+
1231
+ public function add_chained_product_class ( $classes, $template_type, $order, $item_id = '' ) {
1232
+ if ( empty($item_id) ) {
1233
+ // get item id from classes (backwards compatibility fix)
1234
+ $class_array = explode(' ', $classes);
1235
+ foreach ($class_array as $class) {
1236
+ if (is_numeric($class)) {
1237
+ $item_id = $class;
1238
+ break;
1239
+ }
1240
+ }
1241
+
1242
+ // if still empty, we lost the item id somewhere :(
1243
+ if (empty($item_id)) {
1244
+ return $classes;
1245
+ }
1246
+ }
1247
+
1248
+ if ( $chained_product_of = WCX_Order::get_item_meta( $order, $item_id, '_chained_product_of', true ) ) {
1249
+ return $classes . ' chained-product';
1250
+ }
1251
+
1252
+ return $classes;
1253
+ }
1254
+
1255
+ /**
1256
+ * Filter plugin strings with qTranslate-X
1257
+ */
1258
+ public function qtranslatex_filters() {
1259
+ $use_filters = array(
1260
+ 'wpo_wcpdf_shop_name' => 20,
1261
+ 'wpo_wcpdf_shop_address' => 20,
1262
+ 'wpo_wcpdf_footer' => 20,
1263
+ 'wpo_wcpdf_order_items' => 20,
1264
+ 'wpo_wcpdf_payment_method' => 20,
1265
+ 'wpo_wcpdf_shipping_method' => 20,
1266
+ 'wpo_wcpdf_extra_1' => 20,
1267
+ 'wpo_wcpdf_extra_2' => 20,
1268
+ 'wpo_wcpdf_extra_3' => 20,
1269
+ );
1270
+
1271
+ foreach ( $use_filters as $name => $priority ) {
1272
+ add_filter( $name, 'qtranxf_useCurrentLanguageIfNotFoundUseDefaultLanguage', $priority );
1273
+ }
1274
+ }
1275
+
1276
+ /**
1277
+ * Use currency symbol font (when enabled in options)
1278
+ */
1279
+ public function use_currency_font ( $template_type ) {
1280
+ add_filter( 'woocommerce_currency_symbol', array( $this, 'wrap_currency_symbol' ), 10, 2);
1281
+ add_action( 'wpo_wcpdf_custom_styles', array($this, 'currency_symbol_font_styles' ) );
1282
+ }
1283
+
1284
+ public function wrap_currency_symbol( $currency_symbol, $currency ) {
1285
+ $currency_symbol = sprintf( '<span class="wcpdf-currency-symbol">%s</span>', $currency_symbol );
1286
+ return $currency_symbol;
1287
+ }
1288
+
1289
+ public function currency_symbol_font_styles () {
1290
+ ?>
1291
+ .wcpdf-currency-symbol { font-family: 'Currencies'; }
1292
+ <?php
1293
+ }
1294
+
1295
+ public function enable_debug () {
1296
+ error_reporting( E_ALL );
1297
+ ini_set( 'display_errors', 1 );
1298
+ }
1299
+
1300
+ /**
1301
+ * Log messages
1302
+ */
1303
+
1304
+ public function log( $order_id, $message ) {
1305
+ $current_date_time = date("Y-m-d H:i:s");
1306
+ $message = $order_id . ' ' . $current_date_time .' ' .$message ."\n";
1307
+ $file = $this->tmp_path() . 'log.txt';
1308
+
1309
+ file_put_contents($file, $message, FILE_APPEND);
1310
+ }
1311
+ }
1312
+
1313
+ }
includes/class-wcpdf-writepanels.php CHANGED
@@ -1,379 +1,384 @@
1
- <?php
2
- use WPO\WC\PDF_Invoices\Compatibility\WC_Core as WCX;
3
- use WPO\WC\PDF_Invoices\Compatibility\Order as WCX_Order;
4
- use WPO\WC\PDF_Invoices\Compatibility\Product as WCX_Product;
5
-
6
- defined( 'ABSPATH' ) or exit;
7
-
8
- /**
9
- * Writepanel class
10
- */
11
- if ( !class_exists( 'WooCommerce_PDF_Invoices_Writepanels' ) ) {
12
-
13
- class WooCommerce_PDF_Invoices_Writepanels {
14
- public $bulk_actions;
15
-
16
- /**
17
- * Constructor
18
- */
19
- public function __construct() {
20
- $this->general_settings = get_option('wpo_wcpdf_general_settings');
21
- $this->template_settings = get_option('wpo_wcpdf_template_settings');
22
-
23
- add_action( 'woocommerce_admin_order_actions_end', array( $this, 'add_listing_actions' ) );
24
- add_filter( 'manage_edit-shop_order_columns', array( $this, 'add_invoice_number_column' ), 999 );
25
- add_action( 'manage_shop_order_posts_custom_column', array( $this, 'invoice_number_column_data' ), 2 );
26
- add_action( 'add_meta_boxes_shop_order', array( $this, 'add_meta_boxes' ) );
27
- add_filter( 'woocommerce_my_account_my_orders_actions', array( $this, 'my_account_pdf_link' ), 10, 2 );
28
- add_action( 'admin_print_scripts', array( $this, 'add_scripts' ) );
29
- add_action( 'admin_print_styles', array( $this, 'add_styles' ) );
30
- add_action( 'admin_footer', array( $this, 'bulk_actions' ) );
31
-
32
- add_action( 'save_post', array( $this,'save_invoice_number_date' ) );
33
-
34
- add_filter( 'woocommerce_shop_order_search_fields', array( $this, 'search_fields' ) );
35
-
36
- $this->bulk_actions = array(
37
- 'invoice' => __( 'PDF Invoices', 'wpo_wcpdf' ),
38
- 'packing-slip' => __( 'PDF Packing Slips', 'wpo_wcpdf' ),
39
- );
40
- }
41
-
42
- /**
43
- * Add the styles
44
- */
45
- public function add_styles() {
46
- if( $this->is_order_edit_page() ) {
47
- wp_enqueue_style( 'thickbox' );
48
-
49
- wp_enqueue_style(
50
- 'wpo-wcpdf',
51
- WooCommerce_PDF_Invoices::$plugin_url . 'css/style.css',
52
- array(),
53
- WooCommerce_PDF_Invoices::$version
54
- );
55
-
56
- if ( version_compare( WOOCOMMERCE_VERSION, '2.1' ) >= 0 ) {
57
- // WC 2.1 or newer (MP6) is used: bigger buttons
58
- wp_enqueue_style(
59
- 'wpo-wcpdf-buttons',
60
- WooCommerce_PDF_Invoices::$plugin_url . 'css/style-buttons.css',
61
- array(),
62
- WooCommerce_PDF_Invoices::$version
63
- );
64
- } else {
65
- // legacy WC 2.0 styles
66
- wp_enqueue_style(
67
- 'wpo-wcpdf-buttons',
68
- WooCommerce_PDF_Invoices::$plugin_url . 'css/style-buttons-wc20.css',
69
- array(),
70
- WooCommerce_PDF_Invoices::$version
71
- );
72
- }
73
- }
74
- }
75
-
76
- /**
77
- * Add the scripts
78
- */
79
- public function add_scripts() {
80
- if( $this->is_order_edit_page() ) {
81
- wp_enqueue_script(
82
- 'wpo-wcpdf',
83
- WooCommerce_PDF_Invoices::$plugin_url . 'js/script.js',
84
- array( 'jquery' ),
85
- WooCommerce_PDF_Invoices::$version
86
- );
87
- wp_localize_script(
88
- 'wpo-wcpdf',
89
- 'wpo_wcpdf_ajax',
90
- array(
91
- // 'ajaxurl' => add_query_arg( 'action', 'generate_wpo_wcpdf', admin_url( 'admin-ajax.php' ) ), // URL to WordPress ajax handling page
92
- 'ajaxurl' => admin_url( 'admin-ajax.php' ), // URL to WordPress ajax handling page
93
- 'nonce' => wp_create_nonce('generate_wpo_wcpdf'),
94
- 'bulk_actions' => array_keys( apply_filters( 'wpo_wcpdf_bulk_actions', $this->bulk_actions ) ),
95
- )
96
- );
97
- }
98
- }
99
-
100
- /**
101
- * Is order page
102
- */
103
- public function is_order_edit_page() {
104
- global $post_type;
105
- if( $post_type == 'shop_order' ) {
106
- return true;
107
- } else {
108
- return false;
109
- }
110
- }
111
-
112
- /**
113
- * Add PDF actions to the orders listing
114
- */
115
- public function add_listing_actions( $order ) {
116
- // do not show buttons for trashed orders
117
- if ( WCX_Order::get_status( $order ) == 'trash' ) {
118
- return;
119
- }
120
-
121
- $listing_actions = array(
122
- 'invoice' => array (
123
- 'url' => wp_nonce_url( admin_url( 'admin-ajax.php?action=generate_wpo_wcpdf&template_type=invoice&order_ids=' . WCX_Order::get_id( $order ) ), 'generate_wpo_wcpdf' ),
124
- 'img' => WooCommerce_PDF_Invoices::$plugin_url . 'images/invoice.png',
125
- 'alt' => __( 'PDF Invoice', 'wpo_wcpdf' ),
126
- ),
127
- 'packing-slip' => array (
128
- 'url' => wp_nonce_url( admin_url( 'admin-ajax.php?action=generate_wpo_wcpdf&template_type=packing-slip&order_ids=' . WCX_Order::get_id( $order ) ), 'generate_wpo_wcpdf' ),
129
- 'img' => WooCommerce_PDF_Invoices::$plugin_url . 'images/packing-slip.png',
130
- 'alt' => __( 'PDF Packing Slip', 'wpo_wcpdf' ),
131
- ),
132
- );
133
-
134
- $listing_actions = apply_filters( 'wpo_wcpdf_listing_actions', $listing_actions, $order );
135
-
136
- foreach ($listing_actions as $action => $data) {
137
- ?>
138
- <a href="<?php echo $data['url']; ?>" class="button tips wpo_wcpdf <?php echo $action; ?>" target="_blank" alt="<?php echo $data['alt']; ?>" data-tip="<?php echo $data['alt']; ?>">
139
- <img src="<?php echo $data['img']; ?>" alt="<?php echo $data['alt']; ?>" width="16">
140
- </a>
141
- <?php
142
- }
143
- }
144
-
145
- /**
146
- * Create additional Shop Order column for Invoice Numbers
147
- * @param array $columns shop order columns
148
- */
149
- public function add_invoice_number_column( $columns ) {
150
- // Check user setting
151
- if ( !isset($this->general_settings['invoice_number_column'] ) ) {
152
- return $columns;
153
- }
154
-
155
- // put the column after the Status column
156
- $new_columns = array_slice($columns, 0, 2, true) +
157
- array( 'pdf_invoice_number' => __( 'Invoice Number', 'wpo_wcpdf' ) ) +
158
- array_slice($columns, 2, count($columns) - 1, true) ;
159
- return $new_columns;
160
- }
161
-
162
- /**
163
- * Display Invoice Number in Shop Order column (if available)
164
- * @param string $column column slug
165
- */
166
- public function invoice_number_column_data( $column ) {
167
- global $post, $the_order, $wpo_wcpdf;
168
-
169
- if ( $column == 'pdf_invoice_number' ) {
170
- if ( empty( $the_order ) || WCX_Order::get_id( $the_order ) != $post->ID ) {
171
- $order = WCX::get_order( $post->ID );
172
- echo $wpo_wcpdf->export->get_invoice_number( WCX_Order::get_id( $order ) );
173
- do_action( 'wcpdf_invoice_number_column_end', $order );
174
- } else {
175
- echo $wpo_wcpdf->export->get_invoice_number( WCX_Order::get_id( $the_order ) );
176
- do_action( 'wcpdf_invoice_number_column_end', $the_order );
177
- }
178
- }
179
- }
180
-
181
- /**
182
- * Display download link on My Account page
183
- */
184
- public function my_account_pdf_link( $actions, $order ) {
185
- $pdf_url = wp_nonce_url( admin_url( 'admin-ajax.php?action=generate_wpo_wcpdf&template_type=invoice&order_ids=' . WCX_Order::get_id( $order ) . '&my-account'), 'generate_wpo_wcpdf' );
186
-
187
- // check my account button settings
188
- if (isset($this->general_settings['my_account_buttons'])) {
189
- switch ($this->general_settings['my_account_buttons']) {
190
- case 'available':
191
- $invoice_allowed = WCX_Order::get_meta( $order, '_wcpdf_invoice_exists', true );
192
- break;
193
- case 'always':
194
- $invoice_allowed = true;
195
- break;
196
- case 'never':
197
- $invoice_allowed = false;
198
- break;
199
- case 'custom':
200
- if ( isset( $this->general_settings['my_account_restrict'] ) && in_array( WCX_Order::get_status( $order ), array_keys( $this->general_settings['my_account_restrict'] ) ) ) {
201
- $invoice_allowed = true;
202
- } else {
203
- $invoice_allowed = false;
204
- }
205
- break;
206
- }
207
- } else {
208
- // backwards compatibility
209
- $invoice_allowed = WCX_Order::get_meta( $order, '_wcpdf_invoice_exists', true );
210
- }
211
-
212
- // Check if invoice has been created already or if status allows download (filter your own array of allowed statuses)
213
- if ( $invoice_allowed || in_array(WCX_Order::get_status( $order ), apply_filters( 'wpo_wcpdf_myaccount_allowed_order_statuses', array() ) ) ) {
214
- $actions['invoice'] = array(
215
- 'url' => $pdf_url,
216
- 'name' => apply_filters( 'wpo_wcpdf_myaccount_button_text', __( 'Download invoice (PDF)', 'wpo_wcpdf' ) )
217
- );
218
- }
219
-
220
- return apply_filters( 'wpo_wcpdf_myaccount_actions', $actions, $order );
221
- }
222
-
223
- /**
224
- * Add the meta box on the single order page
225
- */
226
- public function add_meta_boxes() {
227
- // create PDF buttons
228
- add_meta_box(
229
- 'wpo_wcpdf-box',
230
- __( 'Create PDF', 'wpo_wcpdf' ),
231
- array( $this, 'sidebar_box_content' ),
232
- 'shop_order',
233
- 'side',
234
- 'default'
235
- );
236
-
237
- // Invoice number & date
238
- add_meta_box(
239
- 'wpo_wcpdf-data-input-box',
240
- __( 'PDF Invoice data', 'wpo_wcpdf' ),
241
- array( $this, 'data_input_box_content' ),
242
- 'shop_order',
243
- 'normal',
244
- 'default'
245
- );
246
- }
247
-
248
- /**
249
- * Create the meta box content on the single order page
250
- */
251
- public function sidebar_box_content( $post ) {
252
- global $post_id;
253
-
254
- $meta_actions = array(
255
- 'invoice' => array (
256
- 'url' => wp_nonce_url( admin_url( 'admin-ajax.php?action=generate_wpo_wcpdf&template_type=invoice&order_ids=' . $post_id ), 'generate_wpo_wcpdf' ),
257
- 'alt' => esc_attr__( 'PDF Invoice', 'wpo_wcpdf' ),
258
- 'title' => __( 'PDF Invoice', 'wpo_wcpdf' ),
259
- ),
260
- 'packing-slip' => array (
261
- 'url' => wp_nonce_url( admin_url( 'admin-ajax.php?action=generate_wpo_wcpdf&template_type=packing-slip&order_ids=' . $post_id ), 'generate_wpo_wcpdf' ),
262
- 'alt' => esc_attr__( 'PDF Packing Slip', 'wpo_wcpdf' ),
263
- 'title' => __( 'PDF Packing Slip', 'wpo_wcpdf' ),
264
- ),
265
- );
266
-
267
- $meta_actions = apply_filters( 'wpo_wcpdf_meta_box_actions', $meta_actions, $post_id );
268
-
269
- ?>
270
- <ul class="wpo_wcpdf-actions">
271
- <?php
272
- foreach ($meta_actions as $action => $data) {
273
- printf('<li><a href="%1$s" class="button" target="_blank" alt="%2$s">%3$s</a></li>', $data['url'], $data['alt'],$data['title']);
274
- }
275
- ?>
276
- </ul>
277
- <?php
278
- }
279
-
280
- /**
281
- * Add metabox for invoice number & date
282
- */
283
- public function data_input_box_content ( $post ) {
284
- $order = WCX::get_order( $post->ID );
285
- $invoice_exists = WCX_Order::get_meta( $order, '_wcpdf_invoice_exists', true );
286
- $invoice_number = WCX_Order::get_meta( $order, '_wcpdf_invoice_number', true );
287
- $invoice_date = WCX_Order::get_meta( $order, '_wcpdf_invoice_date', true );
288
-
289
- do_action( 'wpo_wcpdf_meta_box_start', $post->ID );
290
-
291
- ?>
292
- <h4><?php _e( 'Invoice', 'wpo_wcpdf' ) ?></h4>
293
- <p class="form-field _wcpdf_invoice_number_field ">
294
- <label for="_wcpdf_invoice_number"><?php _e( 'Invoice Number (unformatted!)', 'wpo_wcpdf' ); ?>:</label>
295
- <?php if (!empty($invoice_exists)) : ?>
296
- <input type="text" class="short" style="" name="_wcpdf_invoice_number" id="_wcpdf_invoice_number" value="<?php echo $invoice_number ?>">
297
- <?php else : ?>
298
- <input type="text" class="short" style="" name="_wcpdf_invoice_number" id="_wcpdf_invoice_number" value="<?php echo $invoice_number ?>" disabled="disabled" >
299
- <?php endif; ?>
300
- </p>
301
- <p class="form-field form-field-wide">
302
- <label for="wcpdf_invoice_date"><?php _e( 'Invoice Date:', 'wpo_wcpdf' ); ?></label>
303
- <?php if (!empty($invoice_exists)) : ?>
304
- <input type="text" class="date-picker-field" name="wcpdf_invoice_date" id="wcpdf_invoice_date" maxlength="10" value="<?php echo date_i18n( 'Y-m-d', strtotime( $invoice_date ) ); ?>" pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])" />@<input type="number" class="hour" placeholder="<?php _e( 'h', 'woocommerce' ) ?>" name="wcpdf_invoice_date_hour" id="wcpdf_invoice_date_hour" min="0" max="23" size="2" value="<?php echo date_i18n( 'H', strtotime( $invoice_date ) ); ?>" pattern="([01]?[0-9]{1}|2[0-3]{1})" />:<input type="number" class="minute" placeholder="<?php _e( 'm', 'woocommerce' ) ?>" name="wcpdf_invoice_date_minute" id="wcpdf_invoice_date_minute" min="0" max="59" size="2" value="<?php echo date_i18n( 'i', strtotime( $invoice_date ) ); ?>" pattern="[0-5]{1}[0-9]{1}" />
305
- <?php else : ?>
306
- <input type="text" class="date-picker-field" name="wcpdf_invoice_date" id="wcpdf_invoice_date" maxlength="10" disabled="disabled" value="" pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])" />@<input type="number" class="hour" disabled="disabled" placeholder="<?php _e( 'h', 'woocommerce' ) ?>" name="wcpdf_invoice_date_hour" id="wcpdf_invoice_date_hour" min="0" max="23" size="2" value="" pattern="([01]?[0-9]{1}|2[0-3]{1})" />:<input type="number" class="minute" placeholder="<?php _e( 'm', 'woocommerce' ) ?>" name="wcpdf_invoice_date_minute" id="wcpdf_invoice_date_minute" min="0" max="59" size="2" value="" pattern="[0-5]{1}[0-9]{1}" disabled="disabled" />
307
- <?php endif; ?>
308
- </p>
309
- <?php
310
-
311
- do_action( 'wpo_wcpdf_meta_box_end', $post->ID );
312
- }
313
-
314
- /**
315
- * Add actions to menu
316
- */
317
- public function bulk_actions() {
318
- global $post_type;
319
- $bulk_actions = apply_filters( 'wpo_wcpdf_bulk_actions', $this->bulk_actions );
320
-
321
- if ( 'shop_order' == $post_type ) {
322
- ?>
323
- <script type="text/javascript">
324
- jQuery(document).ready(function() {
325
- <?php foreach ($bulk_actions as $action => $title) { ?>
326
- jQuery('<option>').val('<?php echo $action; ?>').html('<?php echo esc_attr( $title ); ?>').appendTo("select[name='action'], select[name='action2']");
327
- <?php } ?>
328
- });
329
- </script>
330
- <?php
331
- }
332
- }
333
-
334
- /**
335
- * Save invoice number
336
- */
337
- public function save_invoice_number_date($post_id) {
338
- global $wpo_wcpdf;
339
- $post_type = get_post_type( $post_id );
340
- if( $post_type == 'shop_order' ) {
341
- $order = WCX::get_order( $post_id );
342
- if ( isset($_POST['_wcpdf_invoice_number']) ) {
343
- WCX_Order::update_meta_data( $order, '_wcpdf_invoice_number', stripslashes( $_POST['_wcpdf_invoice_number'] ) );
344
- WCX_Order::update_meta_data( $order, '_wcpdf_formatted_invoice_number', $wpo_wcpdf->export->get_invoice_number( $post_id ) );
345
- WCX_Order::update_meta_data( $order, '_wcpdf_invoice_exists', 1 );
346
- }
347
-
348
- if ( isset($_POST['wcpdf_invoice_date']) ) {
349
- if ( empty($_POST['wcpdf_invoice_date']) ) {
350
- WCX_Order::delete_meta_data( $order, '_wcpdf_invoice_date' );
351
- } else {
352
- $invoice_date = strtotime( $_POST['wcpdf_invoice_date'] . ' ' . (int) $_POST['wcpdf_invoice_date_hour'] . ':' . (int) $_POST['wcpdf_invoice_date_minute'] . ':00' );
353
- $invoice_date = date_i18n( 'Y-m-d H:i:s', $invoice_date );
354
- WCX_Order::update_meta_data( $order, '_wcpdf_invoice_date', $invoice_date );
355
- WCX_Order::update_meta_data( $order, '_wcpdf_invoice_exists', 1 );
356
- }
357
- }
358
-
359
- if (empty($_POST['wcpdf_invoice_date']) && isset($_POST['_wcpdf_invoice_number'])) {
360
- $invoice_date = date_i18n( 'Y-m-d H:i:s', time() );
361
- WCX_Order::update_meta_data( $order, '_wcpdf_invoice_date', $invoice_date );
362
- }
363
-
364
- if ( empty($_POST['wcpdf_invoice_date']) && empty($_POST['_wcpdf_invoice_number'])) {
365
- WCX_Order::delete_meta_data( $order, '_wcpdf_invoice_exists' );
366
- }
367
- }
368
- }
369
-
370
- /**
371
- * Add invoice number to order search scope
372
- */
373
- public function search_fields ( $custom_fields ) {
374
- $custom_fields[] = '_wcpdf_invoice_number';
375
- $custom_fields[] = '_wcpdf_formatted_invoice_number';
376
- return $custom_fields;
377
- }
378
- }
 
 
 
 
 
379
  }
1
+ <?php
2
+ use WPO\WC\PDF_Invoices\Compatibility\WC_Core as WCX;
3
+ use WPO\WC\PDF_Invoices\Compatibility\Order as WCX_Order;
4
+ use WPO\WC\PDF_Invoices\Compatibility\Product as WCX_Product;
5
+
6
+ defined( 'ABSPATH' ) or exit;
7
+
8
+ /**
9
+ * Writepanel class
10
+ */
11
+ if ( !class_exists( 'WooCommerce_PDF_Invoices_Writepanels' ) ) {
12
+
13
+ class WooCommerce_PDF_Invoices_Writepanels {
14
+ public $bulk_actions;
15
+
16
+ /**
17
+ * Constructor
18
+ */
19
+ public function __construct() {
20
+ $this->general_settings = get_option('wpo_wcpdf_general_settings');
21
+ $this->template_settings = get_option('wpo_wcpdf_template_settings');
22
+
23
+ add_action( 'woocommerce_admin_order_actions_end', array( $this, 'add_listing_actions' ) );
24
+ add_filter( 'manage_edit-shop_order_columns', array( $this, 'add_invoice_number_column' ), 999 );
25
+ add_action( 'manage_shop_order_posts_custom_column', array( $this, 'invoice_number_column_data' ), 2 );
26
+ add_action( 'add_meta_boxes_shop_order', array( $this, 'add_meta_boxes' ) );
27
+ add_filter( 'woocommerce_my_account_my_orders_actions', array( $this, 'my_account_pdf_link' ), 10, 2 );
28
+ add_action( 'admin_print_scripts', array( $this, 'add_scripts' ) );
29
+ add_action( 'admin_print_styles', array( $this, 'add_styles' ) );
30
+ add_action( 'admin_footer', array( $this, 'bulk_actions' ) );
31
+
32
+ add_action( 'save_post', array( $this,'save_invoice_number_date' ) );
33
+
34
+ add_filter( 'woocommerce_shop_order_search_fields', array( $this, 'search_fields' ) );
35
+
36
+ $this->bulk_actions = array(
37
+ 'invoice' => __( 'PDF Invoices', 'wpo_wcpdf' ),
38
+ 'packing-slip' => __( 'PDF Packing Slips', 'wpo_wcpdf' ),
39
+ );
40
+ }
41
+
42
+ /**
43
+ * Add the styles
44
+ */
45
+ public function add_styles() {
46
+ if( $this->is_order_edit_page() ) {
47
+ wp_enqueue_style( 'thickbox' );
48
+
49
+ wp_enqueue_style(
50
+ 'wpo-wcpdf',
51
+ WooCommerce_PDF_Invoices::$plugin_url . 'css/style.css',
52
+ array(),
53
+ WooCommerce_PDF_Invoices::$version
54
+ );
55
+
56
+ if ( version_compare( WOOCOMMERCE_VERSION, '2.1' ) >= 0 ) {
57
+ // WC 2.1 or newer (MP6) is used: bigger buttons
58
+ wp_enqueue_style(
59
+ 'wpo-wcpdf-buttons',
60
+ WooCommerce_PDF_Invoices::$plugin_url . 'css/style-buttons.css',
61
+ array(),
62
+ WooCommerce_PDF_Invoices::$version
63
+ );
64
+ } else {
65
+ // legacy WC 2.0 styles
66
+ wp_enqueue_style(
67
+ 'wpo-wcpdf-buttons',
68
+ WooCommerce_PDF_Invoices::$plugin_url . 'css/style-buttons-wc20.css',
69
+ array(),
70
+ WooCommerce_PDF_Invoices::$version
71
+ );
72
+ }
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Add the scripts
78
+ */
79
+ public function add_scripts() {
80
+ if( $this->is_order_edit_page() ) {
81
+ wp_enqueue_script(
82
+ 'wpo-wcpdf',
83
+ WooCommerce_PDF_Invoices::$plugin_url . 'js/script.js',
84
+ array( 'jquery' ),
85
+ WooCommerce_PDF_Invoices::$version
86
+ );
87
+ wp_localize_script(
88
+ 'wpo-wcpdf',
89
+ 'wpo_wcpdf_ajax',
90
+ array(
91
+ // 'ajaxurl' => add_query_arg( 'action', 'generate_wpo_wcpdf', admin_url( 'admin-ajax.php' ) ), // URL to WordPress ajax handling page
92
+ 'ajaxurl' => admin_url( 'admin-ajax.php' ), // URL to WordPress ajax handling page
93
+ 'nonce' => wp_create_nonce('generate_wpo_wcpdf'),
94
+ 'bulk_actions' => array_keys( apply_filters( 'wpo_wcpdf_bulk_actions', $this->bulk_actions ) ),
95
+ )
96
+ );
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Is order page
102
+ */
103
+ public function is_order_edit_page() {
104
+ global $post_type;
105
+ if( $post_type == 'shop_order' ) {
106
+ return true;
107
+ } else {
108
+ return false;
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Add PDF actions to the orders listing
114
+ */
115
+ public function add_listing_actions( $order ) {
116
+ // do not show buttons for trashed orders
117
+ if ( WCX_Order::get_status( $order ) == 'trash' ) {
118
+ return;
119
+ }
120
+
121
+ $listing_actions = array(
122
+ 'invoice' => array (
123
+ 'url' => wp_nonce_url( admin_url( 'admin-ajax.php?action=generate_wpo_wcpdf&template_type=invoice&order_ids=' . WCX_Order::get_id( $order ) ), 'generate_wpo_wcpdf' ),
124
+ 'img' => WooCommerce_PDF_Invoices::$plugin_url . 'images/invoice.png',
125
+ 'alt' => __( 'PDF Invoice', 'wpo_wcpdf' ),
126
+ ),
127
+ 'packing-slip' => array (
128
+ 'url' => wp_nonce_url( admin_url( 'admin-ajax.php?action=generate_wpo_wcpdf&template_type=packing-slip&order_ids=' . WCX_Order::get_id( $order ) ), 'generate_wpo_wcpdf' ),
129
+ 'img' => WooCommerce_PDF_Invoices::$plugin_url . 'images/packing-slip.png',
130
+ 'alt' => __( 'PDF Packing Slip', 'wpo_wcpdf' ),
131
+ ),
132
+ );
133
+
134
+ $listing_actions = apply_filters( 'wpo_wcpdf_listing_actions', $listing_actions, $order );
135
+
136
+ foreach ($listing_actions as $action => $data) {
137
+ ?>
138
+ <a href="<?php echo $data['url']; ?>" class="button tips wpo_wcpdf <?php echo $action; ?>" target="_blank" alt="<?php echo $data['alt']; ?>" data-tip="<?php echo $data['alt']; ?>">
139
+ <img src="<?php echo $data['img']; ?>" alt="<?php echo $data['alt']; ?>" width="16">
140
+ </a>
141
+ <?php
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Create additional Shop Order column for Invoice Numbers
147
+ * @param array $columns shop order columns
148
+ */
149
+ public function add_invoice_number_column( $columns ) {
150
+ // Check user setting
151
+ if ( !isset($this->general_settings['invoice_number_column'] ) ) {
152
+ return $columns;
153
+ }
154
+
155
+ // put the column after the Status column
156
+ $new_columns = array_slice($columns, 0, 2, true) +
157
+ array( 'pdf_invoice_number' => __( 'Invoice Number', 'wpo_wcpdf' ) ) +
158
+ array_slice($columns, 2, count($columns) - 1, true) ;
159
+ return $new_columns;
160
+ }
161
+
162
+ /**
163
+ * Display Invoice Number in Shop Order column (if available)
164
+ * @param string $column column slug
165
+ */
166
+ public function invoice_number_column_data( $column ) {
167
+ global $post, $the_order, $wpo_wcpdf;
168
+
169
+ if ( $column == 'pdf_invoice_number' ) {
170
+ if ( empty( $the_order ) || WCX_Order::get_id( $the_order ) != $post->ID ) {
171
+ $order = WCX::get_order( $post->ID );
172
+ echo $wpo_wcpdf->export->get_invoice_number( WCX_Order::get_id( $order ) );
173
+ do_action( 'wcpdf_invoice_number_column_end', $order );
174
+ } else {
175
+ echo $wpo_wcpdf->export->get_invoice_number( WCX_Order::get_id( $the_order ) );
176
+ do_action( 'wcpdf_invoice_number_column_end', $the_order );
177
+ }
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Display download link on My Account page
183
+ */
184
+ public function my_account_pdf_link( $actions, $order ) {
185
+ $pdf_url = wp_nonce_url( admin_url( 'admin-ajax.php?action=generate_wpo_wcpdf&template_type=invoice&order_ids=' . WCX_Order::get_id( $order ) . '&my-account'), 'generate_wpo_wcpdf' );
186
+
187
+ // check my account button settings
188
+ if (isset($this->general_settings['my_account_buttons'])) {
189
+ switch ($this->general_settings['my_account_buttons']) {
190
+ case 'available':
191
+ $invoice_allowed = WCX_Order::get_meta( $order, '_wcpdf_invoice_exists', true );
192
+ break;
193
+ case 'always':
194
+ $invoice_allowed = true;
195
+ break;
196
+ case 'never':
197
+ $invoice_allowed = false;
198
+ break;
199
+ case 'custom':
200
+ if ( isset( $this->general_settings['my_account_restrict'] ) && in_array( WCX_Order::get_status( $order ), array_keys( $this->general_settings['my_account_restrict'] ) ) ) {
201
+ $invoice_allowed = true;
202
+ } else {
203
+ $invoice_allowed = false;
204
+ }
205
+ break;
206
+ }
207
+ } else {
208
+ // backwards compatibility
209
+ $invoice_allowed = WCX_Order::get_meta( $order, '_wcpdf_invoice_exists', true );
210
+ }
211
+
212
+ // Check if invoice has been created already or if status allows download (filter your own array of allowed statuses)
213
+ if ( $invoice_allowed || in_array(WCX_Order::get_status( $order ), apply_filters( 'wpo_wcpdf_myaccount_allowed_order_statuses', array() ) ) ) {
214
+ $actions['invoice'] = array(
215
+ 'url' => $pdf_url,
216
+ 'name' => apply_filters( 'wpo_wcpdf_myaccount_button_text', __( 'Download invoice (PDF)', 'wpo_wcpdf' ) )
217
+ );
218
+ }
219
+
220
+ return apply_filters( 'wpo_wcpdf_myaccount_actions', $actions, $order );
221
+ }
222
+
223
+ /**
224
+ * Add the meta box on the single order page
225
+ */
226
+ public function add_meta_boxes() {
227
+ // create PDF buttons
228
+ add_meta_box(
229
+ 'wpo_wcpdf-box',
230
+ __( 'Create PDF', 'wpo_wcpdf' ),
231
+ array( $this, 'sidebar_box_content' ),
232
+ 'shop_order',
233
+ 'side',
234
+ 'default'
235
+ );
236
+
237
+ // Invoice number & date
238
+ add_meta_box(
239
+ 'wpo_wcpdf-data-input-box',
240
+ __( 'PDF Invoice data', 'wpo_wcpdf' ),
241
+ array( $this, 'data_input_box_content' ),
242
+ 'shop_order',
243
+ 'normal',
244
+ 'default'
245
+ );
246
+ }
247
+
248
+ /**
249
+ * Create the meta box content on the single order page
250
+ */
251
+ public function sidebar_box_content( $post ) {
252
+ global $post_id;
253
+
254
+ $meta_actions = array(
255
+ 'invoice' => array (
256
+ 'url' => wp_nonce_url( admin_url( 'admin-ajax.php?action=generate_wpo_wcpdf&template_type=invoice&order_ids=' . $post_id ), 'generate_wpo_wcpdf' ),
257
+ 'alt' => esc_attr__( 'PDF Invoice', 'wpo_wcpdf' ),
258
+ 'title' => __( 'PDF Invoice', 'wpo_wcpdf' ),
259
+ ),
260
+ 'packing-slip' => array (
261
+ 'url' => wp_nonce_url( admin_url( 'admin-ajax.php?action=generate_wpo_wcpdf&template_type=packing-slip&order_ids=' . $post_id ), 'generate_wpo_wcpdf' ),
262
+ 'alt' => esc_attr__( 'PDF Packing Slip', 'wpo_wcpdf' ),
263
+ 'title' => __( 'PDF Packing Slip', 'wpo_wcpdf' ),
264
+ ),
265
+ );
266
+
267
+ $meta_actions = apply_filters( 'wpo_wcpdf_meta_box_actions', $meta_actions, $post_id );
268
+
269
+ ?>
270
+ <ul class="wpo_wcpdf-actions">
271
+ <?php
272
+ foreach ($meta_actions as $action => $data) {
273
+ printf('<li><a href="%1$s" class="button" target="_blank" alt="%2$s">%3$s</a></li>', $data['url'], $data['alt'],$data['title']);
274
+ }
275
+ ?>
276
+ </ul>
277
+ <?php
278
+ }
279
+
280
+ /**
281
+ * Add metabox for invoice number & date
282
+ */
283
+ public function data_input_box_content ( $post ) {
284
+ $order = WCX::get_order( $post->ID );
285
+ $invoice_exists = WCX_Order::get_meta( $order, '_wcpdf_invoice_exists', true );
286
+ $invoice_number = WCX_Order::get_meta( $order, '_wcpdf_invoice_number', true );
287
+ $invoice_date = WCX_Order::get_meta( $order, '_wcpdf_invoice_date', true );
288
+
289
+ do_action( 'wpo_wcpdf_meta_box_start', $post->ID );
290
+
291
+ ?>
292
+ <h4><?php _e( 'Invoice', 'wpo_wcpdf' ) ?></h4>
293
+ <p class="form-field _wcpdf_invoice_number_field ">
294
+ <label for="_wcpdf_invoice_number"><?php _e( 'Invoice Number (unformatted!)', 'wpo_wcpdf' ); ?>:</label>
295
+ <?php if (!empty($invoice_exists)) : ?>
296
+ <input type="text" class="short" style="" name="_wcpdf_invoice_number" id="_wcpdf_invoice_number" value="<?php echo $invoice_number ?>">
297
+ <?php else : ?>
298
+ <input type="text" class="short" style="" name="_wcpdf_invoice_number" id="_wcpdf_invoice_number" value="<?php echo $invoice_number ?>" disabled="disabled" >
299
+ <?php endif; ?>
300
+ </p>
301
+ <p class="form-field form-field-wide">
302
+ <label for="wcpdf_invoice_date"><?php _e( 'Invoice Date:', 'wpo_wcpdf' ); ?></label>
303
+ <?php if (!empty($invoice_exists)) : ?>
304
+ <input type="text" class="date-picker-field" name="wcpdf_invoice_date" id="wcpdf_invoice_date" maxlength="10" value="<?php echo date_i18n( 'Y-m-d', strtotime( $invoice_date ) ); ?>" pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])" />@<input type="number" class="hour" placeholder="<?php _e( 'h', 'woocommerce' ) ?>" name="wcpdf_invoice_date_hour" id="wcpdf_invoice_date_hour" min="0" max="23" size="2" value="<?php echo date_i18n( 'H', strtotime( $invoice_date ) ); ?>" pattern="([01]?[0-9]{1}|2[0-3]{1})" />:<input type="number" class="minute" placeholder="<?php _e( 'm', 'woocommerce' ) ?>" name="wcpdf_invoice_date_minute" id="wcpdf_invoice_date_minute" min="0" max="59" size="2" value="<?php echo date_i18n( 'i', strtotime( $invoice_date ) ); ?>" pattern="[0-5]{1}[0-9]{1}" />
305
+ <?php else : ?>
306
+ <input type="text" class="date-picker-field" name="wcpdf_invoice_date" id="wcpdf_invoice_date" maxlength="10" disabled="disabled" value="" pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])" />@<input type="number" class="hour" disabled="disabled" placeholder="<?php _e( 'h', 'woocommerce' ) ?>" name="wcpdf_invoice_date_hour" id="wcpdf_invoice_date_hour" min="0" max="23" size="2" value="" pattern="([01]?[0-9]{1}|2[0-3]{1})" />:<input type="number" class="minute" placeholder="<?php _e( 'm', 'woocommerce' ) ?>" name="wcpdf_invoice_date_minute" id="wcpdf_invoice_date_minute" min="0" max="59" size="2" value="" pattern="[0-5]{1}[0-9]{1}" disabled="disabled" />
307
+ <?php endif; ?>
308
+ </p>
309
+ <?php
310
+
311
+ do_action( 'wpo_wcpdf_meta_box_end', $post->ID );
312
+ }
313
+
314
+ /**
315
+ * Add actions to menu
316
+ */
317
+ public function bulk_actions() {
318
+ global $post_type;
319
+ $bulk_actions = apply_filters( 'wpo_wcpdf_bulk_actions', $this->bulk_actions );
320
+
321
+ if ( 'shop_order' == $post_type ) {
322
+ ?>
323
+ <script type="text/javascript">
324
+ jQuery(document).ready(function() {
325
+ <?php foreach ($bulk_actions as $action => $title) { ?>
326
+ jQuery('<option>').val('<?php echo $action; ?>').html('<?php echo esc_attr( $title ); ?>').appendTo("select[name='action'], select[name='action2']");
327
+ <?php } ?>
328
+ });
329
+ </script>
330
+ <?php
331
+ }
332
+ }
333
+
334
+ /**
335
+ * Save invoice number
336
+ */
337
+ public function save_invoice_number_date($post_id) {
338
+ global $wpo_wcpdf;
339
+ $post_type = get_post_type( $post_id );
340
+ if( $post_type == 'shop_order' ) {
341
+ // bail if this is not an actual 'Save order' action
342
+ if (!isset($_POST['action']) || $_POST['action'] != 'editpost') {
343
+ return;
344
+ }
345
+
346
+ $order = WCX::get_order( $post_id );
347
+ if ( isset($_POST['_wcpdf_invoice_number']) ) {
348
+ WCX_Order::update_meta_data( $order, '_wcpdf_invoice_number', stripslashes( $_POST['_wcpdf_invoice_number'] ) );
349
+ WCX_Order::update_meta_data( $order, '_wcpdf_formatted_invoice_number', $wpo_wcpdf->export->get_invoice_number( $post_id ) );
350
+ WCX_Order::update_meta_data( $order, '_wcpdf_invoice_exists', 1 );
351
+ }
352
+
353
+ if ( isset($_POST['wcpdf_invoice_date']) ) {
354
+ if ( empty($_POST['wcpdf_invoice_date']) ) {
355
+ WCX_Order::delete_meta_data( $order, '_wcpdf_invoice_date' );
356
+ } else {
357
+ $invoice_date = strtotime( $_POST['wcpdf_invoice_date'] . ' ' . (int) $_POST['wcpdf_invoice_date_hour'] . ':' . (int) $_POST['wcpdf_invoice_date_minute'] . ':00' );
358
+ $invoice_date = date_i18n( 'Y-m-d H:i:s', $invoice_date );
359
+ WCX_Order::update_meta_data( $order, '_wcpdf_invoice_date', $invoice_date );
360
+ WCX_Order::update_meta_data( $order, '_wcpdf_invoice_exists', 1 );
361
+ }
362
+ }
363
+
364
+ if (empty($_POST['wcpdf_invoice_date']) && isset($_POST['_wcpdf_invoice_number'])) {
365
+ $invoice_date = date_i18n( 'Y-m-d H:i:s', time() );
366
+ WCX_Order::update_meta_data( $order, '_wcpdf_invoice_date', $invoice_date );
367
+ }
368
+
369
+ if ( empty($_POST['wcpdf_invoice_date']) && empty($_POST['_wcpdf_invoice_number'])) {
370
+ WCX_Order::delete_meta_data( $order, '_wcpdf_invoice_exists' );
371
+ }
372
+ }
373
+ }
374
+
375
+ /**
376
+ * Add invoice number to order search scope
377
+ */
378
+ public function search_fields ( $custom_fields ) {
379
+ $custom_fields[] = '_wcpdf_invoice_number';
380
+ $custom_fields[] = '_wcpdf_formatted_invoice_number';
381
+ return $custom_fields;
382
+ }
383
+ }
384
  }
readme.txt CHANGED
@@ -1,539 +1,543 @@
1
- === Plugin Name ===
2
- Contributors: pomegranate
3
- Tags: woocommerce, pdf, invoices, packing slips, print, delivery notes, invoice, packing slip, export, email, bulk, automatic
4
- Requires at least: 3.5
5
- Tested up to: 4.7
6
- Stable tag: 1.6.4
7
- License: GPLv2 or later
8
- License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
-
10
- Create, print & automatically email PDF invoices & packing slips for WooCommerce orders.
11
-
12
- == Description ==
13
-
14
- This WooCommerce extension automatically adds a PDF invoice to the order confirmation emails sent out to your customers. Includes a basic template (additional templates are available from [WP Overnight](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-premium-templates/)) as well as the possibility to modify/create your own templates. In addition, you can choose to download or print invoices and packing slips from the WooCommerce order admin.
15
-
16
- = Main features =
17
- * Automatically attach invoice PDF to WooCommerce emails of your choice
18
- * Download the PDF invoice / packing slip from the order admin page
19
- * Generate PDF invoices / packings slips in bulk
20
- * **Fully customizable** HTML/CSS invoice templates
21
- * Download invoices from the My Account page
22
- * Sequential invoice numbers - with custom formatting
23
- * **Available in: Czech, Dutch, English, Finnish, French, German, Hungarian, Italian, Japanese (see FAQ for adding custom fonts!), Norwegian, Polish, Romanian, Russian, Slovak, Slovenian, Spanish, Swedish & Ukrainian**
24
-
25
- In addition to this, we offer several premium extensions:
26
-
27
- * Create/email PDF Proforma Invoices, Credit Notes (for Refunds), email Packing Slips & more with [WooCommerce PDF Invoices & Packing Slips Professional](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-professional/)
28
- * Upload all invoices automatically to Dropbox with [WooCommerce PDF Invoices & Packing Slips to Dropbox](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-dropbox/)
29
- * Automatically send new orders or packing slips to your printer, as soon as the customer orders! [WooCommerce Automatic Order Printing](https://www.simbahosting.co.uk/s3/product/woocommerce-automatic-order-printing/?affiliates=2) (from our partners at Simba Hosting)
30
- * More advanced & stylish templates with [WooCommerce PDF Invoices & Packing Slips Premium Templates](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-premium-templates/)
31
-
32
- = Fully customizable =
33
- In addition to a number of default settings (including a custom header/logo) and several layout fields that you can use out of the box, the plugin contains HTML/CSS based templates that allow for customization & full control over the PDF output. Copy the templates to your theme folder and you don't have to worry that your customizations will be overwritten when you update the plugin.
34
-
35
- * Insert customer header image/logo
36
- * Modify shop data / footer / disclaimer etc. on the invoices & packing slips
37
- * Select paper size (Letter or A4)
38
- * Translation ready
39
-
40
- == Installation ==
41
-
42
- = Minimum Requirements =
43
-
44
- * WooCommerce 2.0 or later
45
- * WordPress 3.5 or later
46
-
47
- = Automatic installation =
48
- Automatic installation is the easiest option as WordPress handles the file transfers itself and you don't even need to leave your web browser. To do an automatic install of WooCommerce PDF Invoices & Packing Slips, log in to your WordPress admin panel, navigate to the Plugins menu and click Add New.
49
-
50
- In the search field type "WooCommerce PDF Invoices & Packing Slips" and click Search Plugins. You can install it by simply clicking Install Now. After clicking that link you will be asked if you're sure you want to install the plugin. Click yes and WordPress will automatically complete the installation. After installation has finished, click the 'activate plugin' link.
51
-
52
- = Manual installation via the WordPress interface =
53
- 1. Download the plugin zip file to your computer
54
- 2. Go to the WordPress admin panel menu Plugins > Add New
55
- 3. Choose upload
56
- 4. Upload the plugin zip file, the plugin will now be installed
57
- 5. After installation has finished, click the 'activate plugin' link
58
-
59
- = Manual installation via FTP =
60
- 1. Download the plugin file to your computer and unzip it
61
- 2. Using an FTP program, or your hosting control panel, upload the unzipped plugin folder to your WordPress installation's wp-content/plugins/ directory.
62
- 3. Activate the plugin from the Plugins menu within the WordPress admin.
63
-
64
- == Frequently Asked Questions ==
65
-
66
- = Where can I find the documentation? =
67
-
68
- [WooCommerce PDF Invoices & Packing Slips documentation](http://docs.wpovernight.com/woocommerce-pdf-invoices-packing-slips/)
69
-
70
- = It's not working! =
71
-
72
- Check out our step by step diagnostic instructions here: https://wordpress.org/support/topic/read-this-first-9/
73
-
74
-
75
-
76
-
77
-
78
- = Where can I find more templates? =
79
-
80
- Go to [wpovernight.com](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-premium-templates/) to checkout more templates! These include templates with more tax details and product thumbnails. Need a custom templates? Contact us at support@wpovernight.com for more information.
81
-
82
- = Can I create/send a proforma invoice or a credit note? =
83
- This is a feature of our Professional extension, which can be found at [wpovernight.com](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-professional/)
84
-
85
- = Can I contribute to the code? =
86
- You're more than welcome! This plugin is hosted on github, where you can post issues or make pull requests.
87
- https://github.com/wpovernight/woocommerce-pdf-invoices-packing-slips
88
-
89
- = How can I display the HTML/CSS source for debugging/developing templates? =
90
- There's a setting on the Status tab of the settings page that allows you to toggle HTML output. Don't forget to turn if off after you're done testing!
91
-
92
-
93
- == Screenshots ==
94
-
95
- 1. General settings page
96
- 2. Template settings page
97
- 3. Simple invoice PDF
98
- 4. Simple packing slip PDF
99
-
100
- == Changelog ==
101
-
102
- = 1.6.4 =
103
- * Fix: My account invoice button visibility
104
-
105
- = 1.6.3 =
106
- * Fix: Empty date handling
107
- * Fix: Shipping notes on refunds (reason for refund)
108
-
109
- = 1.6.2 =
110
- * Fix: TM Extra Product Options compatibility (in WC3.0)
111
- * Fix: Tax display in WC3.0
112
-
113
- = 1.6.1 =
114
- * Fix: Error with totals in credit notes
115
- * Fix: Always set invoice date when invoice is create (even display is disabled in the settings)
116
-
117
- = 1.6.0.2 =
118
- * Fix: Don't crash with PHP 5.2 or older (5.3 or higher required, 5.6 or higher recommended)
119
-
120
- = 1.6.0 =
121
- * WooCommerce 3.0 Compatible
122
- * **Requires PHP version 5.3 or higher**
123
- * Fix: Invoice number display in mobile view
124
- * Fix: Update formatted invoice number in order meta when number is altered
125
- * Fix: global plugin object loading in wrapped cron methods
126
- * Tweak: Avoid PHP7 scan false positives in DomPDF
127
-
128
- = 1.5.39 =
129
- * Feature: new template action hooks `wpo_wcpdf_before_document` & `wpo_wcpdf_after_document`
130
- * Tweak: In totals, emphasize order total rather than last item
131
- * Fix: User deprecation notices
132
- * Translations: Updated Slovenian
133
-
134
- = 1.5.38 =
135
- * Fix: Thumbnail path fallback
136
- * Fix: Edge/IE hour & minute pattern
137
- * Fix: Skip over non-order objects
138
- * Tweak: Let shop manager view My Account links
139
- * Dev: added `wpo_wcpdf_before_attachment_creation` action
140
- * Translations: Updated POT, Swedish, Dutch & Norwegian
141
-
142
- = 1.5.37 =
143
- * Feature: Added support for third party invoice numbers
144
- * Feature: Enable pre-invoice number click-to-edit
145
- * Fix: Review link for custom admins
146
- * Fix: PHP7 compatibility
147
- * Fix: Invoice date hour/minute pattern
148
- * Tweak: Multisite WooCommerce check optimization
149
-
150
- = 1.5.36 =
151
- * Translations: Fixed Romanian (incorrect "Factură Proforma" translation for "Invoice")
152
-
153
- = 1.5.35 =
154
- * Translations: Fixed "Includes %s" string for WC2.6+
155
-
156
- = 1.5.34 =
157
- * Fix: Document check that was introduced in 1.5.33 for disable free setting
158
-
159
- = 1.5.33 =
160
- * Tweak: Don't apply 'disable free' setting to packing slip attachment
161
- * Translations: Updated Romanian
162
-
163
- = 1.5.32 =
164
- * Fix: Updated currency font with Indian Rupee symbol
165
- * Translations: added Formal German (currently a copy of informal German)
166
-
167
- = 1.5.31 =
168
- * Feature: [invoice_day] or [order_day] in invoice number format
169
- * Fix: Link to hide all ads when premium extensions active
170
-
171
- = 1.5.30 =
172
- * Feature: Enable currency font for extended currency support
173
- * Fix: Font sync on plugin update
174
-
175
- = 1.5.29 =
176
- * Translations: Added Croation (Thanks Neven/Spine ICT!), updated French (Thanks Sabra!)
177
- * Tweak: filter shop address before checking if it's empty
178
- * Dev: added $order to `wpo_wcpdf_template_file` filter
179
-
180
- = 1.5.28 =
181
- * Tweak: the 'Next invoice number' is now stored separately in the database for faster and more reliable retrieval. Circumventing any caching, this should prevent duplicate invoice numbers.
182
- * Fix: Bulk actions plugin conflicts
183
- * Experimental: page numbers (use {{PAGE_NUM}} / {{PAGE_COUNT}} in your template)
184
-
185
- = 1.5.27 =
186
- * Feature: Use [invoice_year] and [invoice_month] placeholders in invoice number prefix/suffix
187
- * Feature: WooCommerce Order Status & Actions Manager emails compatibility
188
- * Feature: Add invoice number to WC REST API
189
- * Fix: Allow positive 'discounts' (price corrections)
190
- * Fix: Discounts rounding
191
- * Translations: Updated Finnish & Portugese & POT
192
-
193
- = 1.5.26 =
194
- * Feature: Automatically list all emails registered in WooCommerce
195
- * Feature: Reset invoice number yearly
196
- * Feature: WooCommerce Chained Products compatibility
197
- * Feature: WooCommerce Product Bundles visibility settings taken into account in invoice
198
- * Fix: Disable PDF creation from trashed order_ids
199
- * Tweak: Alert when no orders selected for bulk export (Props to Dartui!)
200
- * Tweak: PDF invoice settings always under WooCommerce menu (also for premium users)
201
- * Tweak: extra $item_id passed in row class filter
202
- * Translations: Updated Slovenian, Spanish, Dutch & POT file
203
-
204
- = 1.5.24 =
205
- * Hotfix: Subscriptions renewal filter arguments
206
-
207
- = 1.5.23 =
208
- * Fix: WooCommerce Subscriptons 2.0 deprecation notice.
209
- * Tweak: better qTranslate-X support
210
- * Tweak: filter for user privileges check (wpo_wcpdf_check_privs)
211
- * Translations: French translations fix
212
-
213
- = 1.5.22 =
214
- * Fix: Workaround for bug in WPML (which cleared all settings)
215
- * Translation: fixed Polish translation for invoice
216
-
217
- = 1.5.21 =
218
- * Translations: Added Estionan (thanks Tanel!)
219
- * Tweak: WC2.4 compatibility
220
-
221
- = 1.5.20 =
222
- * Feature: Option to 'never' display My Account invoice link
223
- * Fix: Order total for refunds in WC2.4
224
- * Fix: notice when no custom statuses selected for My Account display
225
- * Tweak: Product bundles styles
226
-
227
- = 1.5.19 =
228
- * Fix: Invoice number search (broke other custom searches)
229
-
230
- = 1.5.18 =
231
- * Fix: wpo_wcpdf_item_row_class packing slip filter arguments
232
-
233
- = 1.5.17 =
234
- * Feature: WooCommerce Product Bundles compatibility styles
235
- * Tweak: wpo_wcpdf_item_row_class as filter instead of action
236
-
237
- = 1.5.16 =
238
- * Feature: Search orders by invoice number (note: search on formatted invoice number only works for new orders)
239
- * Feature: Formatted invoice number stored in order
240
- * Tweak: Function parameters added to some of the filters
241
- * Tweak: WooCommerce 2.4 compatibility
242
- * Dev feature: action to add class to items table row (wpo_wcpdf_item_row_class)
243
- * Translations: Swedish updated (thanks Conney!)
244
- * Translations: Norwegian updated
245
-
246
- = 1.5.15 =
247
- * Fix: invoice number padding didn't work for values lower than 3
248
- * Tweak: WPML compatibility filter
249
- * Translations: Updated French (Thanks Nicolas!)
250
-
251
- = 1.5.14 =
252
- * Tweak: Invoice number & date edit fields moved to separate box on order edit page
253
- * Translations: Updated POT & Dutch
254
-
255
- = 1.5.13 =
256
- * Fix: Better address comparison to determine when to display alternate address
257
- * Tweak: Filter N/A addresses
258
- * Tweak: Use WooCommerce function for 2.3 discounts
259
- * Translations: Czech Updated (Thanks Ivo!)
260
- * Translations: French (minor fixes)
261
-
262
- = 1.5.12 =
263
- * Translations: added Danish, Updated POT & Italian
264
-
265
- = 1.5.11 =
266
- * Fix: Product text attributes (now checks key too)
267
- * Fix: Status page upload explanation typos
268
-
269
- = 1.5.10 =
270
- * Fix: Double check to make sure plugin doesn't attach to user emails
271
-
272
- = 1.5.9 =
273
- * Feature: Shorthand function to display product attributes: `$wpo_wcpdf->get_product_attribute( $attribute_name, $product )`
274
-
275
- = 1.5.8 =
276
- * Feature: disable invoice for free orders
277
- * Feature: action to insert data before & after item meta
278
- * Tweak: Added classes to sku & weight
279
- * Tweak: Hide payment method from totals (already shown in template)
280
- * Translations: Updated POT & Dutch
281
-
282
- = 1.5.7 =
283
- * Feature: Setting to show email address & phone number on invoice or packing slip (does not work on custom templates based on previous versions!)
284
-
285
- = 1.5.6 =
286
- * Feature: Setting to show shipping address on invoice (does not work on custom templates based on previous versions!)
287
- * Feature: My Account invoice download setting
288
- * Feature: several new template actions
289
- * Tweak: WooCommerce Bookings compatibility
290
- * Tweak: Gerenal stylesheet cleanup
291
- * Fix: temp path check/error on settings page
292
- * Fix: Document titles for credit notes and proforma (Pro)
293
- * Fix: Discount including tax
294
- * Fix: Special characters on item meta (requires WooCommerce 2.3.6)
295
- * Translations: Missing text domain on several strings
296
- * Translations: Updated POT & Dutch
297
-
298
- = 1.5.5 =
299
- * Fix: Check for incomplete line tax data (Subscriptions compatibility)
300
- * Fix: More precise template path instructions
301
- * Fix: duplicate stylesheet filter
302
- * Fix: Always prefer original order's billing address for refunds (WooCommerce EU VAT Number compatibility)
303
- * Translations: Updated German (MwSt. instead of formal Ust.)
304
- * Translations: Updated Dutch
305
-
306
- = 1.5.4 =
307
- * Tweak: include plugin version in style/script includes
308
- * Tweak: upload code cleanup
309
- * Fix: Parent invoice number (for Credit Notes in professional extension)
310
-
311
- = 1.5.3 =
312
- * Feature: add original order date value to order date filter
313
- * Feature: Work with line_tax_data when available
314
- * Feature: pass item_id to items
315
- * Tweak: later check for woocommerce active
316
- * Fix: do not try to validate empty settings (Status page settings)
317
- * Translations: Fixed Dutch typo
318
-
319
- = 1.5.2 =
320
- * Fix: fatal error when trying to activate with WooCommerce not installed yet.
321
- * Tweak: indentation fix on the Simple template
322
-
323
- = 1.5.1 =
324
- * Fix: prevent errors when upgrading
325
-
326
- = 1.5.0 =
327
- * Feature: All temporary files are now stored centrally in the WP uploads folder.
328
- * Feature: Debug settings in status panel (output errors & output to HTML)
329
- * Feature: Compatibility filter for WooCommerce Subscriptions (prevents duplicate invoice numbers)
330
- * Tweak: Pass order to totals filters
331
- * Translations: Updated POT
332
- * Translations: Updated Italian (Thanks Astrid!)
333
- * Translations: Updated Dutch
334
- * FAQ: instructions for placing a link on the thank you page
335
-
336
- = 1.4.14 =
337
- * Fix: fatal error when user registers at checkout (applies to credit notes only)
338
- * Translations: Updated German (Thanks Dietmar!)
339
- * Translations: Place your custom translations in wp-content/languages/woocommerce-pdf-invoices-packing-slips/wpo_wcpdf-LOCALE.mo to protect them from being overwritten by plugin updates.
340
-
341
- = 1.4.13 =
342
- * Feature: use separate file for all your template specific functions (template-functions.php)
343
- * Translations: Added Slovenian (thanks gregy1403!)
344
- * Translations: Updated Norwegian & Dutch.
345
- * Translations: Added Japanese - needs custom font!
346
- * FAQ: instructions on how to use custom fonts
347
-
348
- = 1.4.12 =
349
- * Fix: issues with post parent objects (WC2.1 and older)
350
-
351
- = 1.4.11 =
352
- * Small fix: bulk actions for specific i18n configurations
353
- * Tweak: total row key used as class in Simple template
354
-
355
- = 1.4.10 =
356
- * Fix: Invoice not attaching
357
- * Translations: Updated POT file
358
- * Translations: Updated Dutch, Norwegian, Polish, Brazilian Portugese, Romanian, Russian, Slovak, Spanish & Ukrainian (Many thanks to all the translators!)
359
- * Templates: added action hooks for easier customizations (`wpo_wcpdf_before_order_details`, `wpo_wcpdf_after_order_details`, `wpo_wcpdf_invoice_title` & `wpo_wcpdf_packing_slip_title`)
360
-
361
- = 1.4.9 =
362
- * Feature: Order number and date are now displayed by default in the Simple template (invoice number and date still optional)
363
- * Feature: Display Customer/Order notes with a simple shorthand (see FAQ)
364
- * Translations: Added Brazilian Portugese (thanks Victor Debone!)
365
- * Tweak: Fail more gracefully when there are errors during PDF generation
366
- * Tweak: added template type class to template output body
367
- * Tweak: cleaned up Simple template style.css
368
-
369
- = 1.4.8 =
370
- * Translations: Added Finnish (Thanks Sami Mäkelä/Contrast.fi!)
371
-
372
- = 1.4.7 =
373
- * Fix: check if image file exists locally, fallback to url if not (CDN compatibility)
374
- * Fix: make deleting invoice date possible
375
- * Fix: correct tmp folder check on status page
376
- * Translations: updated po/mo files
377
- * Tweak: changed settings capability requirement to `manage_woocommerce` (was: `manage_options`)
378
- * Tweak: better email attachment function
379
- * Tweak: prevent footer overlap (Simple template)
380
- * Tweak: fallback if `glob()` is not allowed on the server
381
- * Tweak: better custom template instructions (reflects path to actual (child-)theme)
382
-
383
- = 1.4.6 =
384
- * HOTFIX: WooCommerce 2.2 compatibility fix
385
- * Filter for PDF temp folder (wpo_wcpdf_tmp_path)
386
-
387
- = 1.4.5 =
388
- * Fix: Double date conversion for order date on invoice number filter (to avoid i18n date issues)
389
- * Fix: Template selector reset after update
390
- * Translations: added Norwegian (Thanks Aleksander!)
391
-
392
- = 1.4.4 =
393
- * Feature: Editable invoice date per order/invoice.
394
- * Feature: HTML is now allowed in footer and other settings fields.
395
- * Translations: Updated German (Thanks Nadine!)
396
- * Fix: template paths are now saved relative to the site base path (ABSPATH) to prevent errors when moving to another server
397
- * Tweak: Changed bulk action hook for better theme compatibility
398
- * Tweak: Newlines in custom checkout fields
399
-
400
- = 1.4.3 =
401
- * Feature: Added function to call custom fields more easily (see FAQ)
402
- * Feature: Change the my account button text via a filter (wpo_wcpdf_myaccount_button_text)
403
- * Translations: Added Danish (Thanks Mads!)
404
- * Tweak: only load PDF engine if it's not already loaded by another plugin
405
-
406
- = 1.4.2 =
407
- * Fix: Don't create invoice number when exporting packing slips
408
- * Fix: Division by zero for 0 quantity items
409
-
410
- = 1.4.1 =
411
- * Translations: Added Polish (Thanks Mike!)
412
- * Fix: Invoice number formatting notices in debug mode
413
-
414
- = 1.4.0 =
415
- * Feature: Invoice numbers can now be given a prefix, suffix or padding on the settings page!
416
- * Filter: `wpo_wcpdf_email_allowed_statuses` to attach pdf to custom order status emails
417
- * Tweak: Sequential Order Numbers Pro compatibility
418
- * Tweak: Filenames are now automatically sanitized to prevent issues with illegal characters
419
-
420
- = 1.3.2 =
421
- * Fix: error on wpo_wcpdf_email_attachment filter when $pdf_path not set
422
-
423
- = 1.3.1 =
424
- * Fix: invoice number was cleared when Order Actions were being used when an invoice number was not set
425
- * Translations: Updated Slovak (Thanks Jozef!)
426
- * Translations: Added Czech (Thanks CubiQ!)
427
-
428
- = 1.3.0 =
429
- * Feature: Added 'status' panel for better problem diagnosis
430
- * Tweak: split create & get invoice number calls to prevent slow database calls from causing number skipping
431
- * Translations: Added Romanian (Thanks Leonardo!)
432
- * Translations: Added Slovak (Thanks Oleg!)
433
-
434
- = 1.2.13 =
435
- * Feature: added filter for custom email attachment condition (wpo_wcpdf_custom_email_condition)
436
- * Fix: warning for tax implode
437
-
438
- = 1.2.12 =
439
- * Fix: hyperlink underline (was more like a strikethrough)
440
-
441
- = 1.2.11 =
442
- * Translations: Fixed German spelling error
443
- * Translations: Updated Swedish (Thanks Fredrik!)
444
-
445
- = 1.2.10 =
446
- * Translations: Added Swedish (Thanks Jonathan!)
447
- * Fix: Line-height issue (on some systems, the space between lines was very high)
448
-
449
- = 1.2.9 =
450
- * Fix: bug where 'standard' tax class would not display in some cases
451
- * Fix: bug that caused the totals to jump for some font sizes
452
- * Fix: WC2.1 deprecated totals function
453
- * Fix: If multiple taxes were set up with the same name, only one would display (Simple template was not affected)
454
-
455
- = 1.2.8 =
456
- * Template: Body line-height defined to prevent character jumping with italic texts
457
- * Fix: Open Sans now included in plugin package (fixes font issues for servers with allow_url_fopen disabled)
458
-
459
- = 1.2.7 =
460
- * Translations: POT, DE & NL updated
461
- * Fix: Removed stray span tag in totals table
462
-
463
- = 1.2.6 =
464
- * Translations: Spanish update (thanks prepu!)
465
- * Fix: More advanced checks to determine if a customer can download the invoice (including a status filter)
466
-
467
- = 1.2.5 =
468
- * Feature: Optional Invoice Number column for the orders listing
469
- * Feature: Better support for international characters
470
- * Translations: Added Russian & Ukrainian translation (thanks Oleg!)
471
- * Translations: Updated Spanish (Thanks Manuel!) and Dutch translations & POT file
472
- * Tweak: Memory limit notice
473
- * Tweak: Filename name now includes invoice number (when configured in the settings)
474
-
475
- = 1.2.4 =
476
- * Feature: Set which type of emails you want to attach the invoice to
477
-
478
- = 1.2.3 =
479
- * Feature: Manually edit invoice number on the edit order screen
480
- * Feature: Set the first (/next) invoice number on the settings screen
481
- * Fix: Bug where invoice number would be generated twice due to slow database calls
482
- * Fix: php strict warnings
483
-
484
- = 1.2.2 =
485
- * Feature: Simple template now uses Open Sans to include more international special characters
486
- * Feature: Implemented filters for paper size & orientation ([read here](http://wordpress.org/support/topic/select-a5-paper-size-for-packing-slips?replies=5#post-5211129))
487
- * Tweak: PDF engine updated (dompdf 0.6.0)
488
- * Tweak: Download PDF link on the my account page is now only shown when an invoice is already created by the admin or automatically, to prevent unwanted invoice created (creating problems with european laws).
489
-
490
- = 1.2.1 =
491
- * Fix: shipping & fees functions didn't output correctly with the tax set to 'incl'
492
-
493
- = 1.2.0 =
494
- * Feature: Sequential invoice numbers (set upon invoice creation).
495
- * Feature: Invoice date (set upon invoice creation).
496
-
497
- = 1.1.6 =
498
- * Feature: Hungarian translations added - thanks Joseph!
499
- * Tweak: Better debug code.
500
- * Tweak: Error reporting when templates not found.
501
- * Fix: tax rate calculation for free items.
502
-
503
- = 1.1.5 =
504
- * Feature: German translations added - thanks Christian!
505
- * Fix: dompdf 0.6.0 proved to be less stable, so switching back to beta3 for now.
506
-
507
- = 1.1.4 =
508
- * Fix: Template paths on windows servers were not properly saved (stripslashes), resulting in an empty output.
509
-
510
- = 1.1.3 =
511
- * Feature: PDF engine (dompdf) updated to 0.6.0 for better stability and utf-8 support.
512
- * Tweak: Local server path is used for header image for better compatibility with server settings.
513
- * Fix: several small bugs.
514
-
515
- = 1.1.2 =
516
- * Feature: Totals can now be called with simpler template functions
517
- * Feature: Italian translations - thanks max66max!
518
- * Tweak: improved memory performance
519
-
520
- = 1.1.1 =
521
- * Feature: French translations - thanks Guillaume!
522
-
523
- = 1.1.0 =
524
- * Feature: Fees can now also be called ex. VAT
525
- * Feature: Invoices can now be downloaded from the My Account page
526
- * Feature: Spanish translation & POT file included
527
- * Fix: ternary statements that caused an error
528
-
529
- = 1.0.1 =
530
- * Tweak: Packing slip now displays shipping address instead of billing address
531
- * Tweak: Variation data is now displayed by default
532
-
533
- = 1.0.0 =
534
- * First release
535
-
536
- == Upgrade Notice ==
537
-
538
- = 1.6.4 =
539
- Important: Version 1.6 requires PHP 5.3 or higher to run!
 
 
 
 
1
+ === Plugin Name ===
2
+ Contributors: pomegranate
3
+ Tags: woocommerce, pdf, invoices, packing slips, print, delivery notes, invoice, packing slip, export, email, bulk, automatic
4
+ Requires at least: 3.5
5
+ Tested up to: 4.7
6
+ Stable tag: 1.6.5
7
+ License: GPLv2 or later
8
+ License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
+
10
+ Create, print & automatically email PDF invoices & packing slips for WooCommerce orders.
11
+
12
+ == Description ==
13
+
14
+ This WooCommerce extension automatically adds a PDF invoice to the order confirmation emails sent out to your customers. Includes a basic template (additional templates are available from [WP Overnight](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-premium-templates/)) as well as the possibility to modify/create your own templates. In addition, you can choose to download or print invoices and packing slips from the WooCommerce order admin.
15
+
16
+ = Main features =
17
+ * Automatically attach invoice PDF to WooCommerce emails of your choice
18
+ * Download the PDF invoice / packing slip from the order admin page
19
+ * Generate PDF invoices / packings slips in bulk
20
+ * **Fully customizable** HTML/CSS invoice templates
21
+ * Download invoices from the My Account page
22
+ * Sequential invoice numbers - with custom formatting
23
+ * **Available in: Czech, Dutch, English, Finnish, French, German, Hungarian, Italian, Japanese (see FAQ for adding custom fonts!), Norwegian, Polish, Romanian, Russian, Slovak, Slovenian, Spanish, Swedish & Ukrainian**
24
+
25
+ In addition to this, we offer several premium extensions:
26
+
27
+ * Create/email PDF Proforma Invoices, Credit Notes (for Refunds), email Packing Slips & more with [WooCommerce PDF Invoices & Packing Slips Professional](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-professional/)
28
+ * Upload all invoices automatically to Dropbox with [WooCommerce PDF Invoices & Packing Slips to Dropbox](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-dropbox/)
29
+ * Automatically send new orders or packing slips to your printer, as soon as the customer orders! [WooCommerce Automatic Order Printing](https://www.simbahosting.co.uk/s3/product/woocommerce-automatic-order-printing/?affiliates=2) (from our partners at Simba Hosting)
30
+ * More advanced & stylish templates with [WooCommerce PDF Invoices & Packing Slips Premium Templates](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-premium-templates/)
31
+
32
+ = Fully customizable =
33
+ In addition to a number of default settings (including a custom header/logo) and several layout fields that you can use out of the box, the plugin contains HTML/CSS based templates that allow for customization & full control over the PDF output. Copy the templates to your theme folder and you don't have to worry that your customizations will be overwritten when you update the plugin.
34
+
35
+ * Insert customer header image/logo
36
+ * Modify shop data / footer / disclaimer etc. on the invoices & packing slips
37
+ * Select paper size (Letter or A4)
38
+ * Translation ready
39
+
40
+ == Installation ==
41
+
42
+ = Minimum Requirements =
43
+
44
+ * WooCommerce 2.0 or later
45
+ * WordPress 3.5 or later
46
+
47
+ = Automatic installation =
48
+ Automatic installation is the easiest option as WordPress handles the file transfers itself and you don't even need to leave your web browser. To do an automatic install of WooCommerce PDF Invoices & Packing Slips, log in to your WordPress admin panel, navigate to the Plugins menu and click Add New.
49
+
50
+ In the search field type "WooCommerce PDF Invoices & Packing Slips" and click Search Plugins. You can install it by simply clicking Install Now. After clicking that link you will be asked if you're sure you want to install the plugin. Click yes and WordPress will automatically complete the installation. After installation has finished, click the 'activate plugin' link.
51
+
52
+ = Manual installation via the WordPress interface =
53
+ 1. Download the plugin zip file to your computer
54
+ 2. Go to the WordPress admin panel menu Plugins > Add New
55
+ 3. Choose upload
56
+ 4. Upload the plugin zip file, the plugin will now be installed
57
+ 5. After installation has finished, click the 'activate plugin' link
58
+
59
+ = Manual installation via FTP =
60
+ 1. Download the plugin file to your computer and unzip it
61
+ 2. Using an FTP program, or your hosting control panel, upload the unzipped plugin folder to your WordPress installation's wp-content/plugins/ directory.
62
+ 3. Activate the plugin from the Plugins menu within the WordPress admin.
63
+
64
+ == Frequently Asked Questions ==
65
+
66
+ = Where can I find the documentation? =
67
+
68
+ [WooCommerce PDF Invoices & Packing Slips documentation](http://docs.wpovernight.com/woocommerce-pdf-invoices-packing-slips/)
69
+
70
+ = It's not working! =
71
+
72
+ Check out our step by step diagnostic instructions here: https://wordpress.org/support/topic/read-this-first-9/
73
+
74
+
75
+
76
+
77
+
78
+ = Where can I find more templates? =
79
+
80
+ Go to [wpovernight.com](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-premium-templates/) to checkout more templates! These include templates with more tax details and product thumbnails. Need a custom templates? Contact us at support@wpovernight.com for more information.
81
+
82
+ = Can I create/send a proforma invoice or a credit note? =
83
+ This is a feature of our Professional extension, which can be found at [wpovernight.com](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-professional/)
84
+
85
+ = Can I contribute to the code? =
86
+ You're more than welcome! This plugin is hosted on github, where you can post issues or make pull requests.
87
+ https://github.com/wpovernight/woocommerce-pdf-invoices-packing-slips
88
+
89
+ = How can I display the HTML/CSS source for debugging/developing templates? =
90
+ There's a setting on the Status tab of the settings page that allows you to toggle HTML output. Don't forget to turn if off after you're done testing!
91
+
92
+
93
+ == Screenshots ==
94
+
95
+ 1. General settings page
96
+ 2. Template settings page
97
+ 3. Simple invoice PDF
98
+ 4. Simple packing slip PDF
99
+
100
+ == Changelog ==
101
+
102
+ = 1.6.5 =
103
+ * Fix: Duplicate invoice numbers when bulk completing orders (WC3.0)
104
+ * Fix: Hidden Invoice date when order refunded
105
+
106
+ = 1.6.4 =
107
+ * Fix: My account invoice button visibility
108
+
109
+ = 1.6.3 =
110
+ * Fix: Empty date handling
111
+ * Fix: Shipping notes on refunds (reason for refund)
112
+
113
+ = 1.6.2 =
114
+ * Fix: TM Extra Product Options compatibility (in WC3.0)
115
+ * Fix: Tax display in WC3.0
116
+
117
+ = 1.6.1 =
118
+ * Fix: Error with totals in credit notes
119
+ * Fix: Always set invoice date when invoice is create (even display is disabled in the settings)
120
+
121
+ = 1.6.0.2 =
122
+ * Fix: Don't crash with PHP 5.2 or older (5.3 or higher required, 5.6 or higher recommended)
123
+
124
+ = 1.6.0 =
125
+ * WooCommerce 3.0 Compatible
126
+ * **Requires PHP version 5.3 or higher**
127
+ * Fix: Invoice number display in mobile view
128
+ * Fix: Update formatted invoice number in order meta when number is altered
129
+ * Fix: global plugin object loading in wrapped cron methods
130
+ * Tweak: Avoid PHP7 scan false positives in DomPDF
131
+
132
+ = 1.5.39 =
133
+ * Feature: new template action hooks `wpo_wcpdf_before_document` & `wpo_wcpdf_after_document`
134
+ * Tweak: In totals, emphasize order total rather than last item
135
+ * Fix: User deprecation notices
136
+ * Translations: Updated Slovenian
137
+
138
+ = 1.5.38 =
139
+ * Fix: Thumbnail path fallback
140
+ * Fix: Edge/IE hour & minute pattern
141
+ * Fix: Skip over non-order objects
142
+ * Tweak: Let shop manager view My Account links
143
+ * Dev: added `wpo_wcpdf_before_attachment_creation` action
144
+ * Translations: Updated POT, Swedish, Dutch & Norwegian
145
+
146
+ = 1.5.37 =
147
+ * Feature: Added support for third party invoice numbers
148
+ * Feature: Enable pre-invoice number click-to-edit
149
+ * Fix: Review link for custom admins
150
+ * Fix: PHP7 compatibility
151
+ * Fix: Invoice date hour/minute pattern
152
+ * Tweak: Multisite WooCommerce check optimization
153
+
154
+ = 1.5.36 =
155
+ * Translations: Fixed Romanian (incorrect "Factură Proforma" translation for "Invoice")
156
+
157
+ = 1.5.35 =
158
+ * Translations: Fixed "Includes %s" string for WC2.6+
159
+
160
+ = 1.5.34 =
161
+ * Fix: Document check that was introduced in 1.5.33 for disable free setting
162
+
163
+ = 1.5.33 =
164
+ * Tweak: Don't apply 'disable free' setting to packing slip attachment
165
+ * Translations: Updated Romanian
166
+
167
+ = 1.5.32 =
168
+ * Fix: Updated currency font with Indian Rupee symbol
169
+ * Translations: added Formal German (currently a copy of informal German)
170
+
171
+ = 1.5.31 =
172
+ * Feature: [invoice_day] or [order_day] in invoice number format
173
+ * Fix: Link to hide all ads when premium extensions active
174
+
175
+ = 1.5.30 =
176
+ * Feature: Enable currency font for extended currency support
177
+ * Fix: Font sync on plugin update
178
+
179
+ = 1.5.29 =
180
+ * Translations: Added Croation (Thanks Neven/Spine ICT!), updated French (Thanks Sabra!)
181
+ * Tweak: filter shop address before checking if it's empty
182
+ * Dev: added $order to `wpo_wcpdf_template_file` filter
183
+
184
+ = 1.5.28 =
185
+ * Tweak: the 'Next invoice number' is now stored separately in the database for faster and more reliable retrieval. Circumventing any caching, this should prevent duplicate invoice numbers.
186
+ * Fix: Bulk actions plugin conflicts
187
+ * Experimental: page numbers (use {{PAGE_NUM}} / {{PAGE_COUNT}} in your template)
188
+
189
+ = 1.5.27 =
190
+ * Feature: Use [invoice_year] and [invoice_month] placeholders in invoice number prefix/suffix
191
+ * Feature: WooCommerce Order Status & Actions Manager emails compatibility
192
+ * Feature: Add invoice number to WC REST API
193
+ * Fix: Allow positive 'discounts' (price corrections)
194
+ * Fix: Discounts rounding
195
+ * Translations: Updated Finnish & Portugese & POT
196
+
197
+ = 1.5.26 =
198
+ * Feature: Automatically list all emails registered in WooCommerce
199
+ * Feature: Reset invoice number yearly
200
+ * Feature: WooCommerce Chained Products compatibility
201
+ * Feature: WooCommerce Product Bundles visibility settings taken into account in invoice
202
+ * Fix: Disable PDF creation from trashed order_ids
203
+ * Tweak: Alert when no orders selected for bulk export (Props to Dartui!)
204
+ * Tweak: PDF invoice settings always under WooCommerce menu (also for premium users)
205
+ * Tweak: extra $item_id passed in row class filter
206
+ * Translations: Updated Slovenian, Spanish, Dutch & POT file
207
+
208
+ = 1.5.24 =
209
+ * Hotfix: Subscriptions renewal filter arguments
210
+
211
+ = 1.5.23 =
212
+ * Fix: WooCommerce Subscriptons 2.0 deprecation notice.
213
+ * Tweak: better qTranslate-X support
214
+ * Tweak: filter for user privileges check (wpo_wcpdf_check_privs)
215
+ * Translations: French translations fix
216
+
217
+ = 1.5.22 =
218
+ * Fix: Workaround for bug in WPML (which cleared all settings)
219
+ * Translation: fixed Polish translation for invoice
220
+
221
+ = 1.5.21 =
222
+ * Translations: Added Estionan (thanks Tanel!)
223
+ * Tweak: WC2.4 compatibility
224
+
225
+ = 1.5.20 =
226
+ * Feature: Option to 'never' display My Account invoice link
227
+ * Fix: Order total for refunds in WC2.4
228
+ * Fix: notice when no custom statuses selected for My Account display
229
+ * Tweak: Product bundles styles
230
+
231
+ = 1.5.19 =
232
+ * Fix: Invoice number search (broke other custom searches)
233
+
234
+ = 1.5.18 =
235
+ * Fix: wpo_wcpdf_item_row_class packing slip filter arguments
236
+
237
+ = 1.5.17 =
238
+ * Feature: WooCommerce Product Bundles compatibility styles
239
+ * Tweak: wpo_wcpdf_item_row_class as filter instead of action
240
+
241
+ = 1.5.16 =
242
+ * Feature: Search orders by invoice number (note: search on formatted invoice number only works for new orders)
243
+ * Feature: Formatted invoice number stored in order
244
+ * Tweak: Function parameters added to some of the filters
245
+ * Tweak: WooCommerce 2.4 compatibility
246
+ * Dev feature: action to add class to items table row (wpo_wcpdf_item_row_class)
247
+ * Translations: Swedish updated (thanks Conney!)
248
+ * Translations: Norwegian updated
249
+
250
+ = 1.5.15 =
251
+ * Fix: invoice number padding didn't work for values lower than 3
252
+ * Tweak: WPML compatibility filter
253
+ * Translations: Updated French (Thanks Nicolas!)
254
+
255
+ = 1.5.14 =
256
+ * Tweak: Invoice number & date edit fields moved to separate box on order edit page
257
+ * Translations: Updated POT & Dutch
258
+
259
+ = 1.5.13 =
260
+ * Fix: Better address comparison to determine when to display alternate address
261
+ * Tweak: Filter N/A addresses
262
+ * Tweak: Use WooCommerce function for 2.3 discounts
263
+ * Translations: Czech Updated (Thanks Ivo!)
264
+ * Translations: French (minor fixes)
265
+
266
+ = 1.5.12 =
267
+ * Translations: added Danish, Updated POT & Italian
268
+
269
+ = 1.5.11 =
270
+ * Fix: Product text attributes (now checks key too)
271
+ * Fix: Status page upload explanation typos
272
+
273
+ = 1.5.10 =
274
+ * Fix: Double check to make sure plugin doesn't attach to user emails
275
+
276
+ = 1.5.9 =
277
+ * Feature: Shorthand function to display product attributes: `$wpo_wcpdf->get_product_attribute( $attribute_name, $product )`
278
+
279
+ = 1.5.8 =
280
+ * Feature: disable invoice for free orders
281
+ * Feature: action to insert data before & after item meta
282
+ * Tweak: Added classes to sku & weight
283
+ * Tweak: Hide payment method from totals (already shown in template)
284
+ * Translations: Updated POT & Dutch
285
+
286
+ = 1.5.7 =
287
+ * Feature: Setting to show email address & phone number on invoice or packing slip (does not work on custom templates based on previous versions!)
288
+
289
+ = 1.5.6 =
290
+ * Feature: Setting to show shipping address on invoice (does not work on custom templates based on previous versions!)
291
+ * Feature: My Account invoice download setting
292
+ * Feature: several new template actions
293
+ * Tweak: WooCommerce Bookings compatibility
294
+ * Tweak: Gerenal stylesheet cleanup
295
+ * Fix: temp path check/error on settings page
296
+ * Fix: Document titles for credit notes and proforma (Pro)
297
+ * Fix: Discount including tax
298
+ * Fix: Special characters on item meta (requires WooCommerce 2.3.6)
299
+ * Translations: Missing text domain on several strings
300
+ * Translations: Updated POT & Dutch
301
+
302
+ = 1.5.5 =
303
+ * Fix: Check for incomplete line tax data (Subscriptions compatibility)
304
+ * Fix: More precise template path instructions
305
+ * Fix: duplicate stylesheet filter
306
+ * Fix: Always prefer original order's billing address for refunds (WooCommerce EU VAT Number compatibility)
307
+ * Translations: Updated German (MwSt. instead of formal Ust.)
308
+ * Translations: Updated Dutch
309
+
310
+ = 1.5.4 =
311
+ * Tweak: include plugin version in style/script includes
312
+ * Tweak: upload code cleanup
313
+ * Fix: Parent invoice number (for Credit Notes in professional extension)
314
+
315
+ = 1.5.3 =
316
+ * Feature: add original order date value to order date filter
317
+ * Feature: Work with line_tax_data when available
318
+ * Feature: pass item_id to items
319
+ * Tweak: later check for woocommerce active
320
+ * Fix: do not try to validate empty settings (Status page settings)
321
+ * Translations: Fixed Dutch typo
322
+
323
+ = 1.5.2 =
324
+ * Fix: fatal error when trying to activate with WooCommerce not installed yet.
325
+ * Tweak: indentation fix on the Simple template
326
+
327
+ = 1.5.1 =
328
+ * Fix: prevent errors when upgrading
329
+
330
+ = 1.5.0 =
331
+ * Feature: All temporary files are now stored centrally in the WP uploads folder.
332
+ * Feature: Debug settings in status panel (output errors & output to HTML)
333
+ * Feature: Compatibility filter for WooCommerce Subscriptions (prevents duplicate invoice numbers)
334
+ * Tweak: Pass order to totals filters
335
+ * Translations: Updated POT
336
+ * Translations: Updated Italian (Thanks Astrid!)
337
+ * Translations: Updated Dutch
338
+ * FAQ: instructions for placing a link on the thank you page
339
+
340
+ = 1.4.14 =
341
+ * Fix: fatal error when user registers at checkout (applies to credit notes only)
342
+ * Translations: Updated German (Thanks Dietmar!)
343
+ * Translations: Place your custom translations in wp-content/languages/woocommerce-pdf-invoices-packing-slips/wpo_wcpdf-LOCALE.mo to protect them from being overwritten by plugin updates.
344
+
345
+ = 1.4.13 =
346
+ * Feature: use separate file for all your template specific functions (template-functions.php)
347
+ * Translations: Added Slovenian (thanks gregy1403!)
348
+ * Translations: Updated Norwegian & Dutch.
349
+ * Translations: Added Japanese - needs custom font!
350
+ * FAQ: instructions on how to use custom fonts
351
+
352
+ = 1.4.12 =
353
+ * Fix: issues with post parent objects (WC2.1 and older)
354
+
355
+ = 1.4.11 =
356
+ * Small fix: bulk actions for specific i18n configurations
357
+ * Tweak: total row key used as class in Simple template
358
+
359
+ = 1.4.10 =
360
+ * Fix: Invoice not attaching
361
+ * Translations: Updated POT file
362
+ * Translations: Updated Dutch, Norwegian, Polish, Brazilian Portugese, Romanian, Russian, Slovak, Spanish & Ukrainian (Many thanks to all the translators!)
363
+ * Templates: added action hooks for easier customizations (`wpo_wcpdf_before_order_details`, `wpo_wcpdf_after_order_details`, `wpo_wcpdf_invoice_title` & `wpo_wcpdf_packing_slip_title`)
364
+
365
+ = 1.4.9 =
366
+ * Feature: Order number and date are now displayed by default in the Simple template (invoice number and date still optional)
367
+ * Feature: Display Customer/Order notes with a simple shorthand (see FAQ)
368
+ * Translations: Added Brazilian Portugese (thanks Victor Debone!)
369
+ * Tweak: Fail more gracefully when there are errors during PDF generation
370
+ * Tweak: added template type class to template output body
371
+ * Tweak: cleaned up Simple template style.css
372
+
373
+ = 1.4.8 =
374
+ * Translations: Added Finnish (Thanks Sami Mäkelä/Contrast.fi!)
375
+
376
+ = 1.4.7 =
377
+ * Fix: check if image file exists locally, fallback to url if not (CDN compatibility)
378
+ * Fix: make deleting invoice date possible
379
+ * Fix: correct tmp folder check on status page
380
+ * Translations: updated po/mo files
381
+ * Tweak: changed settings capability requirement to `manage_woocommerce` (was: `manage_options`)
382
+ * Tweak: better email attachment function
383
+ * Tweak: prevent footer overlap (Simple template)
384
+ * Tweak: fallback if `glob()` is not allowed on the server
385
+ * Tweak: better custom template instructions (reflects path to actual (child-)theme)
386
+
387
+ = 1.4.6 =
388
+ * HOTFIX: WooCommerce 2.2 compatibility fix
389
+ * Filter for PDF temp folder (wpo_wcpdf_tmp_path)
390
+
391
+ = 1.4.5 =
392
+ * Fix: Double date conversion for order date on invoice number filter (to avoid i18n date issues)
393
+ * Fix: Template selector reset after update
394
+ * Translations: added Norwegian (Thanks Aleksander!)
395
+
396
+ = 1.4.4 =
397
+ * Feature: Editable invoice date per order/invoice.
398
+ * Feature: HTML is now allowed in footer and other settings fields.
399
+ * Translations: Updated German (Thanks Nadine!)
400
+ * Fix: template paths are now saved relative to the site base path (ABSPATH) to prevent errors when moving to another server
401
+ * Tweak: Changed bulk action hook for better theme compatibility
402
+ * Tweak: Newlines in custom checkout fields
403
+
404
+ = 1.4.3 =
405
+ * Feature: Added function to call custom fields more easily (see FAQ)
406
+ * Feature: Change the my account button text via a filter (wpo_wcpdf_myaccount_button_text)
407
+ * Translations: Added Danish (Thanks Mads!)
408
+ * Tweak: only load PDF engine if it's not already loaded by another plugin
409
+
410
+ = 1.4.2 =
411
+ * Fix: Don't create invoice number when exporting packing slips
412
+ * Fix: Division by zero for 0 quantity items
413
+
414
+ = 1.4.1 =
415
+ * Translations: Added Polish (Thanks Mike!)
416
+ * Fix: Invoice number formatting notices in debug mode
417
+
418
+ = 1.4.0 =
419
+ * Feature: Invoice numbers can now be given a prefix, suffix or padding on the settings page!
420
+ * Filter: `wpo_wcpdf_email_allowed_statuses` to attach pdf to custom order status emails
421
+ * Tweak: Sequential Order Numbers Pro compatibility
422
+ * Tweak: Filenames are now automatically sanitized to prevent issues with illegal characters
423
+
424
+ = 1.3.2 =
425
+ * Fix: error on wpo_wcpdf_email_attachment filter when $pdf_path not set
426
+
427
+ = 1.3.1 =
428
+ * Fix: invoice number was cleared when Order Actions were being used when an invoice number was not set
429
+ * Translations: Updated Slovak (Thanks Jozef!)
430
+ * Translations: Added Czech (Thanks CubiQ!)
431
+
432
+ = 1.3.0 =
433
+ * Feature: Added 'status' panel for better problem diagnosis
434
+ * Tweak: split create & get invoice number calls to prevent slow database calls from causing number skipping
435
+ * Translations: Added Romanian (Thanks Leonardo!)
436
+ * Translations: Added Slovak (Thanks Oleg!)
437
+
438
+ = 1.2.13 =
439
+ * Feature: added filter for custom email attachment condition (wpo_wcpdf_custom_email_condition)
440
+ * Fix: warning for tax implode
441
+
442
+ = 1.2.12 =
443
+ * Fix: hyperlink underline (was more like a strikethrough)
444
+
445
+ = 1.2.11 =
446
+ * Translations: Fixed German spelling error
447
+ * Translations: Updated Swedish (Thanks Fredrik!)
448
+
449
+ = 1.2.10 =
450
+ * Translations: Added Swedish (Thanks Jonathan!)
451
+ * Fix: Line-height issue (on some systems, the space between lines was very high)
452
+
453
+ = 1.2.9 =
454
+ * Fix: bug where 'standard' tax class would not display in some cases
455
+ * Fix: bug that caused the totals to jump for some font sizes
456
+ * Fix: WC2.1 deprecated totals function
457
+ * Fix: If multiple taxes were set up with the same name, only one would display (Simple template was not affected)
458
+
459
+ = 1.2.8 =
460
+ * Template: Body line-height defined to prevent character jumping with italic texts
461
+ * Fix: Open Sans now included in plugin package (fixes font issues for servers with allow_url_fopen disabled)
462
+
463
+ = 1.2.7 =
464
+ * Translations: POT, DE & NL updated
465
+ * Fix: Removed stray span tag in totals table
466
+
467
+ = 1.2.6 =
468
+ * Translations: Spanish update (thanks prepu!)
469
+ * Fix: More advanced checks to determine if a customer can download the invoice (including a status filter)
470
+
471
+ = 1.2.5 =
472
+ * Feature: Optional Invoice Number column for the orders listing
473
+ * Feature: Better support for international characters
474
+ * Translations: Added Russian & Ukrainian translation (thanks Oleg!)
475
+ * Translations: Updated Spanish (Thanks Manuel!) and Dutch translations & POT file
476
+ * Tweak: Memory limit notice
477
+ * Tweak: Filename name now includes invoice number (when configured in the settings)
478
+
479
+ = 1.2.4 =
480
+ * Feature: Set which type of emails you want to attach the invoice to
481
+
482
+ = 1.2.3 =
483
+ * Feature: Manually edit invoice number on the edit order screen
484
+ * Feature: Set the first (/next) invoice number on the settings screen
485
+ * Fix: Bug where invoice number would be generated twice due to slow database calls
486
+ * Fix: php strict warnings
487
+
488
+ = 1.2.2 =
489
+ * Feature: Simple template now uses Open Sans to include more international special characters
490
+ * Feature: Implemented filters for paper size & orientation ([read here](http://wordpress.org/support/topic/select-a5-paper-size-for-packing-slips?replies=5#post-5211129))
491
+ * Tweak: PDF engine updated (dompdf 0.6.0)
492
+ * Tweak: Download PDF link on the my account page is now only shown when an invoice is already created by the admin or automatically, to prevent unwanted invoice created (creating problems with european laws).
493
+
494
+ = 1.2.1 =
495
+ * Fix: shipping & fees functions didn't output correctly with the tax set to 'incl'
496
+
497
+ = 1.2.0 =
498
+ * Feature: Sequential invoice numbers (set upon invoice creation).
499
+ * Feature: Invoice date (set upon invoice creation).
500
+
501
+ = 1.1.6 =
502
+ * Feature: Hungarian translations added - thanks Joseph!
503
+ * Tweak: Better debug code.
504
+ * Tweak: Error reporting when templates not found.
505
+ * Fix: tax rate calculation for free items.
506
+
507
+ = 1.1.5 =
508
+ * Feature: German translations added - thanks Christian!
509
+ * Fix: dompdf 0.6.0 proved to be less stable, so switching back to beta3 for now.
510
+
511
+ = 1.1.4 =
512
+ * Fix: Template paths on windows servers were not properly saved (stripslashes), resulting in an empty output.
513
+
514
+ = 1.1.3 =
515
+ * Feature: PDF engine (dompdf) updated to 0.6.0 for better stability and utf-8 support.
516
+ * Tweak: Local server path is used for header image for better compatibility with server settings.
517
+ * Fix: several small bugs.
518
+
519
+ = 1.1.2 =
520
+ * Feature: Totals can now be called with simpler template functions
521
+ * Feature: Italian translations - thanks max66max!
522
+ * Tweak: improved memory performance
523
+
524
+ = 1.1.1 =
525
+ * Feature: French translations - thanks Guillaume!
526
+
527
+ = 1.1.0 =
528
+ * Feature: Fees can now also be called ex. VAT
529
+ * Feature: Invoices can now be downloaded from the My Account page
530
+ * Feature: Spanish translation & POT file included
531
+ * Fix: ternary statements that caused an error
532
+
533
+ = 1.0.1 =
534
+ * Tweak: Packing slip now displays shipping address instead of billing address
535
+ * Tweak: Variation data is now displayed by default
536
+
537
+ = 1.0.0 =
538
+ * First release
539
+
540
+ == Upgrade Notice ==
541
+
542
+ = 1.6.5 =
543
+ Important: Version 1.6 requires PHP 5.3 or higher to run!
woocommerce-pdf-invoices-packingslips.php CHANGED
@@ -1,590 +1,590 @@
1
- <?php
2
- /**
3
- * Plugin Name: WooCommerce PDF Invoices & Packing Slips
4
- * Plugin URI: http://www.wpovernight.com
5
- * Description: Create, print & email PDF invoices & packing slips for WooCommerce orders.
6
- * Version: 1.6.4
7
- * Author: Ewout Fernhout
8
- * Author URI: http://www.wpovernight.com
9
- * License: GPLv2 or later
10
- * License URI: http://www.opensource.org/licenses/gpl-license.php
11
- * Text Domain: wpo_wcpdf
12
- */
13
- defined( 'ABSPATH' ) or exit;
14
-
15
- if ( !class_exists( 'WooCommerce_PDF_Invoices' ) ) {
16
-
17
- class WooCommerce_PDF_Invoices {
18
-
19
- public static $plugin_prefix;
20
- public static $plugin_url;
21
- public static $plugin_path;
22
- public static $plugin_basename;
23
- public static $version;
24
-
25
- public $writepanels;
26
- public $settings;
27
- public $export;
28
-
29
- protected static $_instance = null;
30
-
31
- /**
32
- * Main Plugin Instance
33
- *
34
- * Ensures only one instance of plugin is loaded or can be loaded.
35
- */
36
- public static function instance() {
37
- if ( is_null( self::$_instance ) ) {
38
- self::$_instance = new self();
39
- }
40
- return self::$_instance;
41
- }
42
-
43
- /**
44
- * Constructor
45
- */
46
- public function __construct() {
47
- self::$plugin_prefix = 'wpo_wcpdf_';
48
- self::$plugin_basename = plugin_basename(__FILE__);
49
- self::$plugin_url = plugin_dir_url(self::$plugin_basename);
50
- self::$plugin_path = trailingslashit(dirname(__FILE__));
51
- self::$version = '1.6.4';
52
-
53
- // load the localisation & classes
54
- add_action( 'plugins_loaded', array( $this, 'translations' ) ); // or use init?
55
- add_action( 'init', array( $this, 'load_classes' ) );
56
-
57
- // run lifecycle methods
58
- if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
59
- // check if upgrading from versionless (1.4.14 and older)
60
- if ( get_option('wpo_wcpdf_general_settings') && get_option('wpo_wcpdf_version') === false ) {
61
- // tag 'versionless', so that we can apply necessary upgrade settings
62
- add_option( 'wpo_wcpdf_version', 'versionless' );
63
- }
64
-
65
- add_action( 'wp_loaded', array( $this, 'do_install' ) );
66
- }
67
- }
68
-
69
- /**
70
- * Load the translation / textdomain files
71
- *
72
- * Note: the first-loaded translation file overrides any following ones if the same translation is present
73
- */
74
- public function translations() {
75
- $locale = apply_filters( 'plugin_locale', get_locale(), 'wpo_wcpdf' );
76
- $dir = trailingslashit( WP_LANG_DIR );
77
-
78
- /**
79
- * Frontend/global Locale. Looks in:
80
- *
81
- * - WP_LANG_DIR/woocommerce-pdf-invoices-packing-slips/wpo_wcpdf-LOCALE.mo
82
- * - WP_LANG_DIR/plugins/wpo_wcpdf-LOCALE.mo
83
- * - woocommerce-pdf-invoices-packing-slips/languages/wpo_wcpdf-LOCALE.mo (which if not found falls back to:)
84
- * - WP_LANG_DIR/plugins/wpo_wcpdf-LOCALE.mo
85
- */
86
- load_textdomain( 'wpo_wcpdf', $dir . 'woocommerce-pdf-invoices-packing-slips/wpo_wcpdf-' . $locale . '.mo' );
87
- load_textdomain( 'wpo_wcpdf', $dir . 'plugins/wpo_wcpdf-' . $locale . '.mo' );
88
- load_plugin_textdomain( 'wpo_wcpdf', false, dirname( self::$plugin_basename ) . '/languages' );
89
- }
90
-
91
- /**
92
- * Load the main plugin classes and functions
93
- */
94
- public function includes() {
95
- include_once( 'includes/class-wcpdf-settings.php' );
96
- include_once( 'includes/class-wcpdf-writepanels.php' );
97
- include_once( 'includes/class-wcpdf-export.php' );
98
- include_once( 'includes/class-wcpdf-functions.php' );
99
-
100
- // compatibility classes
101
- include_once( 'includes/compatibility/abstract-wc-data-compatibility.php' );
102
- include_once( 'includes/compatibility/class-wc-date-compatibility.php' );
103
- include_once( 'includes/compatibility/class-wc-core-compatibility.php' );
104
- include_once( 'includes/compatibility/class-wc-order-compatibility.php' );
105
- include_once( 'includes/compatibility/class-wc-product-compatibility.php' );
106
- }
107
-
108
-
109
- /**
110
- * Instantiate classes when woocommerce is activated
111
- */
112
- public function load_classes() {
113
- if ( $this->is_woocommerce_activated() === false ) {
114
- add_action( 'admin_notices', array ( $this, 'need_woocommerce' ) );
115
- return;
116
- }
117
-
118
- if ( version_compare( PHP_VERSION, '5.3', '<' ) ) {
119
- add_action( 'admin_notices', array ( $this, 'required_php_version' ) );
120
- return;
121
- }
122
-
123
- // all systems ready - GO!
124
- $this->includes();
125
- $this->settings = new WooCommerce_PDF_Invoices_Settings();
126
- $this->writepanels = new WooCommerce_PDF_Invoices_Writepanels();
127
- $this->export = new WooCommerce_PDF_Invoices_Export();
128
- $this->functions = new WooCommerce_PDF_Invoices_Functions();
129
- }
130
-
131
- /**
132
- * Check if woocommerce is activated
133
- */
134
- public function is_woocommerce_activated() {
135
- $blog_plugins = get_option( 'active_plugins', array() );
136
- $site_plugins = is_multisite() ? (array) maybe_unserialize( get_site_option('active_sitewide_plugins' ) ) : array();
137
-
138
- if ( in_array( 'woocommerce/woocommerce.php', $blog_plugins ) || isset( $site_plugins['woocommerce/woocommerce.php'] ) ) {
139
- return true;
140
- } else {
141
- return false;
142
- }
143
- }
144
-
145
- /**
146
- * WooCommerce not active notice.
147
- *
148
- * @return string Fallack notice.
149
- */
150
-
151
- public function need_woocommerce() {
152
- $error = sprintf( __( 'WooCommerce PDF Invoices & Packing Slips requires %sWooCommerce%s to be installed & activated!' , 'wpo_wcpdf' ), '<a href="http://wordpress.org/extend/plugins/woocommerce/">', '</a>' );
153
-
154
- $message = '<div class="error"><p>' . $error . '</p></div>';
155
-
156
- echo $message;
157
- }
158
-
159
- /**
160
- * PHP version requirement notice
161
- */
162
-
163
- public function required_php_version() {
164
- $error = __( 'WooCommerce PDF Invoices & Packing Slips requires PHP 5.3 or higher (5.6 or higher recommended).', 'wpo_wcpdf' );
165
- $how_to_update = __( 'How to update your PHP version', 'wpo_wcpdf' );
166
- $message = sprintf('<div class="error"><p>%s</p><p><a href="%s">%s</a></p></div>', $error, 'http://docs.wpovernight.com/general/how-to-update-your-php-version/', $how_to_update);
167
-
168
- echo $message;
169
- }
170
-
171
- /** Lifecycle methods *******************************************************
172
- * Because register_activation_hook only runs when the plugin is manually
173
- * activated by the user, we're checking the current version against the
174
- * version stored in the database
175
- ****************************************************************************/
176
-
177
- /**
178
- * Handles version checking
179
- */
180
- public function do_install() {
181
- // only install when woocommerce is active and PHP version 5.3 or higher
182
- if ( !$this->is_woocommerce_activated() || version_compare( PHP_VERSION, '5.3', '<' ) ) {
183
- return;
184
- }
185
-
186
- $version_setting = 'wpo_wcpdf_version';
187
- $installed_version = get_option( $version_setting );
188
-
189
- // installed version lower than plugin version?
190
- if ( version_compare( $installed_version, self::$version, '<' ) ) {
191
-
192
- if ( ! $installed_version ) {
193
- $this->install();
194
- } else {
195
- $this->upgrade( $installed_version );
196
- }
197
-
198
- // new version number
199
- update_option( $version_setting, self::$version );
200
- }
201
- }
202
-
203
-
204
- /**
205
- * Plugin install method. Perform any installation tasks here
206
- */
207
- protected function install() {
208
- // Create temp folders
209
- $tmp_base = $this->export->get_tmp_base();
210
-
211
- // check if tmp folder exists => if not, initialize
212
- if ( !@is_dir( $tmp_base ) ) {
213
- $this->export->init_tmp( $tmp_base );
214
- }
215
-
216
- }
217
-
218
-
219
- /**
220
- * Plugin upgrade method. Perform any required upgrades here
221
- *
222
- * @param string $installed_version the currently installed version
223
- */
224
- protected function upgrade( $installed_version ) {
225
- if ( $installed_version == 'versionless') { // versionless = 1.4.14 and older
226
- // We're upgrading from an old version, so we're enabling the option to use the plugin tmp folder.
227
- // This is not per se the 'best' solution, but the good thing is that nothing is changed
228
- // and nothing will be broken (that wasn't broken before)
229
- $default = array( 'old_tmp' => 1 );
230
- update_option( 'wpo_wcpdf_debug_settings', $default );
231
- }
232
-
233
- // sync fonts on every upgrade!
234
- $debug_settings = get_option( 'wpo_wcpdf_debug_settings' ); // get temp setting
235
-
236
- // do not copy if old_tmp function active! (double check for slow databases)
237
- if ( !( isset($debug_settings['old_tmp']) || $installed_version == 'versionless' ) ) {
238
- $tmp_base = $this->export->get_tmp_base();
239
-
240
- // check if tmp folder exists => if not, initialize
241
- if ( !@is_dir( $tmp_base ) ) {
242
- $this->export->init_tmp( $tmp_base );
243
- }
244
-
245
- $font_path = $tmp_base . 'fonts/';
246
- $this->export->copy_fonts( $font_path );
247
- }
248
-
249
- // 1.5.28 update: copy next invoice number to separate setting
250
- if ( $installed_version == 'versionless' || version_compare( $installed_version, '1.5.28', '<' ) ) {
251
- $template_settings = get_option( 'wpo_wcpdf_template_settings' );
252
- $next_invoice_number = isset($template_settings['next_invoice_number'])?$template_settings['next_invoice_number']:'';
253
- update_option( 'wpo_wcpdf_next_invoice_number', $next_invoice_number );
254
- }
255
- }
256
-
257
- /***********************************************************************/
258
- /********************** GENERAL TEMPLATE FUNCTIONS *********************/
259
- /***********************************************************************/
260
-
261
- /**
262
- * Get template name from slug
263
- */
264
- public function get_template_name ( $template_type ) {
265
- return $this->functions->get_template_name( $template_type );
266
- }
267
-
268
- /**
269
- * Output template styles
270
- */
271
- public function template_styles() {
272
- $this->functions->template_styles();
273
- }
274
-
275
- /**
276
- * Return logo id
277
- */
278
- public function get_header_logo_id() {
279
- return $this->functions->get_header_logo_id();
280
- }
281
-
282
- /**
283
- * Show logo html
284
- */
285
- public function header_logo() {
286
- $this->functions->header_logo();
287
- }
288
-
289
- /**
290
- * Return/Show custom company name or default to blog name
291
- */
292
- public function get_shop_name() {
293
- return $this->functions->get_shop_name();
294
- }
295
- public function shop_name() {
296
- $this->functions->shop_name();
297
- }
298
-
299
- /**
300
- * Return/Show shop/company address if provided
301
- */
302
- public function get_shop_address() {
303
- return $this->functions->get_shop_address();
304
- }
305
- public function shop_address() {
306
- $this->functions->shop_address();
307
- }
308
-
309
- /**
310
- * Check if billing address and shipping address are equal
311
- */
312
- public function ships_to_different_address() {
313
- return $this->functions->ships_to_different_address();
314
- }
315
-
316
- /**
317
- * Return/Show billing address
318
- */
319
- public function get_billing_address() {
320
- return $this->functions->get_billing_address();
321
- }
322
- public function billing_address() {
323
- $this->functions->billing_address();
324
- }
325
-
326
- /**
327
- * Return/Show billing email
328
- */
329
- public function get_billing_email() {
330
- return $this->functions->get_billing_email();
331
- }
332
- public function billing_email() {
333
- $this->functions->billing_email();
334
- }
335
-
336
- /**
337
- * Return/Show billing phone
338
- */
339
- public function get_billing_phone() {
340
- return $this->functions->get_billing_phone();
341
- }
342
- public function billing_phone() {
343
- $this->functions->billing_phone();
344
- }
345
-
346
- /**
347
- * Return/Show shipping address
348
- */
349
- public function get_shipping_address() {
350
- return $this->functions->get_shipping_address();
351
- }
352
- public function shipping_address() {
353
- $this->functions->shipping_address();
354
- }
355
-
356
- /**
357
- * Return/Show a custom field
358
- */
359
- public function get_custom_field( $field_name ) {
360
- return $this->functions->get_custom_field( $field_name );
361
- }
362
- public function custom_field( $field_name, $field_label = '', $display_empty = false ) {
363
- $this->functions->custom_field( $field_name, $field_label, $display_empty );
364
- }
365
-
366
- /**
367
- * Return/Show order notes
368
- */
369
- public function get_order_notes( $filter = 'customer' ) {
370
- return $this->functions->get_order_notes( $filter );
371
- }
372
- public function order_notes( $filter = 'customer' ) {
373
- $this->functions->order_notes( $filter );
374
- }
375
-
376
- /**
377
- * Return/Show the current date
378
- */
379
- public function get_current_date() {
380
- return $this->functions->get_current_date();
381
- }
382
- public function current_date() {
383
- $this->functions->current_date();
384
- }
385
-
386
- /**
387
- * Return/Show payment method
388
- */
389
- public function get_payment_method() {
390
- return $this->functions->get_payment_method();
391
- }
392
- public function payment_method() {
393
- $this->functions->payment_method();
394
- }
395
-
396
- /**
397
- * Return/Show shipping method
398
- */
399
- public function get_shipping_method() {
400
- return $this->functions->get_shipping_method();
401
- }
402
- public function shipping_method() {
403
- $this->functions->shipping_method();
404
- }
405
-
406
- /**
407
- * Return/Show order number
408
- */
409
- public function get_order_number() {
410
- return $this->functions->get_order_number();
411
- }
412
- public function order_number() {
413
- $this->functions->order_number();
414
- }
415
-
416
- /**
417
- * Return/Show invoice number
418
- */
419
- public function get_invoice_number() {
420
- return $this->functions->get_invoice_number();
421
- }
422
- public function invoice_number() {
423
- $this->functions->invoice_number();
424
- }
425
-
426
- /**
427
- * Return/Show the order date
428
- */
429
- public function get_order_date() {
430
- return $this->functions->get_order_date();
431
- }
432
- public function order_date() {
433
- $this->functions->order_date();
434
- }
435
-
436
- /**
437
- * Return/Show the invoice date
438
- */
439
- public function get_invoice_date() {
440
- return $this->functions->get_invoice_date();
441
- }
442
- public function invoice_date() {
443
- $this->functions->invoice_date();
444
- }
445
-
446
- /**
447
- * Return the order items
448
- */
449
- public function get_order_items() {
450
- return $this->functions->get_order_items();
451
- }
452
-
453
- /**
454
- * Return/show product attribute
455
- */
456
- public function get_product_attribute( $attribute_name, $product ) {
457
- return $this->functions->get_product_attribute( $attribute_name, $product );
458
- }
459
- public function product_attribute( $attribute_name, $product ) {
460
- $this->functions->product_attribute( $attribute_name, $product );
461
- }
462
-
463
-
464
- /**
465
- * Return the order totals listing
466
- */
467
- public function get_woocommerce_totals() {
468
- return $this->functions->get_woocommerce_totals();
469
- }
470
-
471
- /**
472
- * Return/show the order subtotal
473
- */
474
- public function get_order_subtotal( $tax = 'excl', $discount = 'incl' ) { // set $tax to 'incl' to include tax, same for $discount
475
- return $this->functions->get_order_subtotal( $tax, $discount );
476
- }
477
- public function order_subtotal( $tax = 'excl', $discount = 'incl' ) {
478
- $this->functions->order_subtotal( $tax, $discount );
479
- }
480
-
481
- /**
482
- * Return/show the order shipping costs
483
- */
484
- public function get_order_shipping( $tax = 'excl' ) { // set $tax to 'incl' to include tax
485
- return $this->functions->get_order_shipping( $tax );
486
- }
487
- public function order_shipping( $tax = 'excl' ) {
488
- $this->functions->order_shipping( $tax );
489
- }
490
-
491
- /**
492
- * Return/show the total discount
493
- */
494
- public function get_order_discount( $type = 'total', $tax = 'incl' ) {
495
- return $this->functions->get_order_discount( $type, $tax );
496
- }
497
- public function order_discount( $type = 'total', $tax = 'incl' ) {
498
- $this->functions->order_discount( $type, $tax );
499
- }
500
-
501
- /**
502
- * Return the order fees
503
- */
504
- public function get_order_fees( $tax = 'excl' ) {
505
- return $this->functions->get_order_fees( $tax );
506
- }
507
-
508
- /**
509
- * Return the order taxes
510
- */
511
- public function get_order_taxes() {
512
- return $this->functions->get_order_taxes();
513
- }
514
-
515
- /**
516
- * Return/show the order grand total
517
- */
518
- public function get_order_grand_total( $tax = 'incl' ) {
519
- return $this->functions->get_order_grand_total( $tax );
520
- }
521
- public function order_grand_total( $tax = 'incl' ) {
522
- $this->functions->order_grand_total( $tax );
523
- }
524
-
525
-
526
- /**
527
- * Return/Show shipping notes
528
- */
529
- public function get_shipping_notes() {
530
- return $this->functions->get_shipping_notes();
531
- }
532
- public function shipping_notes() {
533
- $this->functions->shipping_notes();
534
- }
535
-
536
-
537
- /**
538
- * Return/Show shop/company footer imprint, copyright etc.
539
- */
540
- public function get_footer() {
541
- return $this->functions->get_footer();
542
- }
543
- public function footer() {
544
- $this->functions->footer();
545
- }
546
-
547
- /**
548
- * Return/Show Extra field 1
549
- */
550
- public function get_extra_1() {
551
- return $this->functions->get_extra_1();
552
- }
553
- public function extra_1() {
554
- $this->functions->extra_1();
555
- }
556
-
557
- /**
558
- * Return/Show Extra field 2
559
- */
560
- public function get_extra_2() {
561
- return $this->functions->get_extra_2();
562
- }
563
- public function extra_2() {
564
- $this->functions->extra_2();
565
- }
566
-
567
- /**
568
- * Return/Show Extra field 3
569
- */
570
- public function get_extra_3() {
571
- return $this->functions->get_extra_3();
572
- }
573
- public function extra_3() {
574
- $this->functions->extra_3();
575
- }
576
- }
577
- }
578
-
579
- /**
580
- * Returns the main instance of WooCommerce PDF Invoices & Packing Slips to prevent the need to use globals.
581
- *
582
- * @since 1.6
583
- * @return WPO_WCPDF
584
- */
585
- function WPO_WCPDF() {
586
- return WooCommerce_PDF_Invoices::instance();
587
- }
588
-
589
- // Global for backwards compatibility.
590
  $GLOBALS['wpo_wcpdf'] = WPO_WCPDF();
1
+ <?php
2
+ /**
3
+ * Plugin Name: WooCommerce PDF Invoices & Packing Slips
4
+ * Plugin URI: http://www.wpovernight.com
5
+ * Description: Create, print & email PDF invoices & packing slips for WooCommerce orders.
6
+ * Version: 1.6.5
7
+ * Author: Ewout Fernhout
8
+ * Author URI: http://www.wpovernight.com
9
+ * License: GPLv2 or later
10
+ * License URI: http://www.opensource.org/licenses/gpl-license.php
11
+ * Text Domain: wpo_wcpdf
12
+ */
13
+ defined( 'ABSPATH' ) or exit;
14
+
15
+ if ( !class_exists( 'WooCommerce_PDF_Invoices' ) ) {
16
+
17
+ class WooCommerce_PDF_Invoices {
18
+
19
+ public static $plugin_prefix;
20
+ public static $plugin_url;
21
+ public static $plugin_path;
22
+ public static $plugin_basename;
23
+ public static $version;
24
+
25
+ public $writepanels;
26
+ public $settings;
27
+ public $export;
28
+
29
+ protected static $_instance = null;
30
+
31
+ /**
32
+ * Main Plugin Instance
33
+ *
34
+ * Ensures only one instance of plugin is loaded or can be loaded.
35
+ */
36
+ public static function instance() {
37
+ if ( is_null( self::$_instance ) ) {
38
+ self::$_instance = new self();
39
+ }
40
+ return self::$_instance;
41
+ }
42
+
43
+ /**
44
+ * Constructor
45
+ */
46
+ public function __construct() {
47
+ self::$plugin_prefix = 'wpo_wcpdf_';
48
+ self::$plugin_basename = plugin_basename(__FILE__);
49
+ self::$plugin_url = plugin_dir_url(self::$plugin_basename);
50
+ self::$plugin_path = trailingslashit(dirname(__FILE__));
51
+ self::$version = '1.6.5';
52
+
53
+ // load the localisation & classes
54
+ add_action( 'plugins_loaded', array( $this, 'translations' ) ); // or use init?
55
+ add_action( 'init', array( $this, 'load_classes' ) );
56
+
57
+ // run lifecycle methods
58
+ if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
59
+ // check if upgrading from versionless (1.4.14 and older)
60
+ if ( get_option('wpo_wcpdf_general_settings') && get_option('wpo_wcpdf_version') === false ) {
61
+ // tag 'versionless', so that we can apply necessary upgrade settings
62
+ add_option( 'wpo_wcpdf_version', 'versionless' );
63
+ }
64
+
65
+ add_action( 'wp_loaded', array( $this, 'do_install' ) );
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Load the translation / textdomain files
71
+ *
72
+ * Note: the first-loaded translation file overrides any following ones if the same translation is present
73
+ */
74
+ public function translations() {
75
+ $locale = apply_filters( 'plugin_locale', get_locale(), 'wpo_wcpdf' );
76
+ $dir = trailingslashit( WP_LANG_DIR );
77
+
78
+ /**
79
+ * Frontend/global Locale. Looks in:
80
+ *
81
+ * - WP_LANG_DIR/woocommerce-pdf-invoices-packing-slips/wpo_wcpdf-LOCALE.mo
82
+ * - WP_LANG_DIR/plugins/wpo_wcpdf-LOCALE.mo
83
+ * - woocommerce-pdf-invoices-packing-slips/languages/wpo_wcpdf-LOCALE.mo (which if not found falls back to:)
84
+ * - WP_LANG_DIR/plugins/wpo_wcpdf-LOCALE.mo
85
+ */
86
+ load_textdomain( 'wpo_wcpdf', $dir . 'woocommerce-pdf-invoices-packing-slips/wpo_wcpdf-' . $locale . '.mo' );
87
+ load_textdomain( 'wpo_wcpdf', $dir . 'plugins/wpo_wcpdf-' . $locale . '.mo' );
88
+ load_plugin_textdomain( 'wpo_wcpdf', false, dirname( self::$plugin_basename ) . '/languages' );
89
+ }
90
+
91
+ /**
92
+ * Load the main plugin classes and functions
93
+ */
94
+ public function includes() {
95
+ include_once( 'includes/class-wcpdf-settings.php' );
96
+ include_once( 'includes/class-wcpdf-writepanels.php' );
97
+ include_once( 'includes/class-wcpdf-export.php' );
98
+ include_once( 'includes/class-wcpdf-functions.php' );
99
+
100
+ // compatibility classes
101
+ include_once( 'includes/compatibility/abstract-wc-data-compatibility.php' );
102
+ include_once( 'includes/compatibility/class-wc-date-compatibility.php' );
103
+ include_once( 'includes/compatibility/class-wc-core-compatibility.php' );
104
+ include_once( 'includes/compatibility/class-wc-order-compatibility.php' );
105
+ include_once( 'includes/compatibility/class-wc-product-compatibility.php' );
106
+ }
107
+
108
+
109
+ /**
110
+ * Instantiate classes when woocommerce is activated
111
+ */
112
+ public function load_classes() {
113
+ if ( $this->is_woocommerce_activated() === false ) {
114
+ add_action( 'admin_notices', array ( $this, 'need_woocommerce' ) );
115
+ return;
116
+ }
117
+
118
+ if ( version_compare( PHP_VERSION, '5.3', '<' ) ) {
119
+ add_action( 'admin_notices', array ( $this, 'required_php_version' ) );
120
+ return;
121
+ }
122
+
123
+ // all systems ready - GO!
124
+ $this->includes();
125
+ $this->settings = new WooCommerce_PDF_Invoices_Settings();
126
+ $this->writepanels = new WooCommerce_PDF_Invoices_Writepanels();
127
+ $this->export = new WooCommerce_PDF_Invoices_Export();
128
+ $this->functions = new WooCommerce_PDF_Invoices_Functions();
129
+ }
130
+
131
+ /**
132
+ * Check if woocommerce is activated
133
+ */
134
+ public function is_woocommerce_activated() {
135
+ $blog_plugins = get_option( 'active_plugins', array() );
136
+ $site_plugins = is_multisite() ? (array) maybe_unserialize( get_site_option('active_sitewide_plugins' ) ) : array();
137
+
138
+ if ( in_array( 'woocommerce/woocommerce.php', $blog_plugins ) || isset( $site_plugins['woocommerce/woocommerce.php'] ) ) {
139
+ return true;
140
+ } else {
141
+ return false;
142
+ }
143
+ }
144
+
145
+ /**
146
+ * WooCommerce not active notice.
147
+ *
148
+ * @return string Fallack notice.
149
+ */
150
+
151
+ public function need_woocommerce() {
152
+ $error = sprintf( __( 'WooCommerce PDF Invoices & Packing Slips requires %sWooCommerce%s to be installed & activated!' , 'wpo_wcpdf' ), '<a href="http://wordpress.org/extend/plugins/woocommerce/">', '</a>' );
153
+
154
+ $message = '<div class="error"><p>' . $error . '</p></div>';
155
+
156
+ echo $message;
157
+ }
158
+
159
+ /**
160
+ * PHP version requirement notice
161
+ */
162
+
163
+ public function required_php_version() {
164
+ $error = __( 'WooCommerce PDF Invoices & Packing Slips requires PHP 5.3 or higher (5.6 or higher recommended).', 'wpo_wcpdf' );
165
+ $how_to_update = __( 'How to update your PHP version', 'wpo_wcpdf' );
166
+ $message = sprintf('<div class="error"><p>%s</p><p><a href="%s">%s</a></p></div>', $error, 'http://docs.wpovernight.com/general/how-to-update-your-php-version/', $how_to_update);
167
+
168
+ echo $message;
169
+ }
170
+
171
+ /** Lifecycle methods *******************************************************
172
+ * Because register_activation_hook only runs when the plugin is manually
173
+ * activated by the user, we're checking the current version against the
174
+ * version stored in the database
175
+ ****************************************************************************/
176
+
177
+ /**
178
+ * Handles version checking
179
+ */
180
+ public function do_install() {
181
+ // only install when woocommerce is active and PHP version 5.3 or higher
182
+ if ( !$this->is_woocommerce_activated() || version_compare( PHP_VERSION, '5.3', '<' ) ) {
183
+ return;
184
+ }
185
+
186
+ $version_setting = 'wpo_wcpdf_version';
187
+ $installed_version = get_option( $version_setting );
188
+
189
+ // installed version lower than plugin version?
190
+ if ( version_compare( $installed_version, self::$version, '<' ) ) {
191
+
192
+ if ( ! $installed_version ) {
193
+ $this->install();
194
+ } else {
195
+ $this->upgrade( $installed_version );
196
+ }
197
+
198
+ // new version number
199
+ update_option( $version_setting, self::$version );
200
+ }
201
+ }
202
+
203
+
204
+ /**
205
+ * Plugin install method. Perform any installation tasks here
206
+ */
207
+ protected function install() {
208
+ // Create temp folders
209
+ $tmp_base = $this->export->get_tmp_base();
210
+
211
+ // check if tmp folder exists => if not, initialize
212
+ if ( !@is_dir( $tmp_base ) ) {
213
+ $this->export->init_tmp( $tmp_base );
214
+ }
215
+
216
+ }
217
+
218
+
219
+ /**
220
+ * Plugin upgrade method. Perform any required upgrades here
221
+ *
222
+ * @param string $installed_version the currently installed version
223
+ */
224
+ protected function upgrade( $installed_version ) {
225
+ if ( $installed_version == 'versionless') { // versionless = 1.4.14 and older
226
+ // We're upgrading from an old version, so we're enabling the option to use the plugin tmp folder.
227
+ // This is not per se the 'best' solution, but the good thing is that nothing is changed
228
+ // and nothing will be broken (that wasn't broken before)
229
+ $default = array( 'old_tmp' => 1 );
230
+ update_option( 'wpo_wcpdf_debug_settings', $default );
231
+ }
232
+
233
+ // sync fonts on every upgrade!
234
+ $debug_settings = get_option( 'wpo_wcpdf_debug_settings' ); // get temp setting
235
+
236
+ // do not copy if old_tmp function active! (double check for slow databases)
237
+ if ( !( isset($debug_settings['old_tmp']) || $installed_version == 'versionless' ) ) {
238
+ $tmp_base = $this->export->get_tmp_base();
239
+
240
+ // check if tmp folder exists => if not, initialize
241
+ if ( !@is_dir( $tmp_base ) ) {
242
+ $this->export->init_tmp( $tmp_base );
243
+ }
244
+
245
+ $font_path = $tmp_base . 'fonts/';
246
+ $this->export->copy_fonts( $font_path );
247
+ }
248
+
249
+ // 1.5.28 update: copy next invoice number to separate setting
250
+ if ( $installed_version == 'versionless' || version_compare( $installed_version, '1.5.28', '<' ) ) {
251
+ $template_settings = get_option( 'wpo_wcpdf_template_settings' );
252
+ $next_invoice_number = isset($template_settings['next_invoice_number'])?$template_settings['next_invoice_number']:'';
253
+ update_option( 'wpo_wcpdf_next_invoice_number', $next_invoice_number );
254
+ }
255
+ }
256
+
257
+ /***********************************************************************/
258
+ /********************** GENERAL TEMPLATE FUNCTIONS *********************/
259
+ /***********************************************************************/
260
+
261
+ /**
262
+ * Get template name from slug
263
+ */
264
+ public function get_template_name ( $template_type ) {
265
+ return $this->functions->get_template_name( $template_type );
266
+ }
267
+
268
+ /**
269
+ * Output template styles
270
+ */
271
+ public function template_styles() {
272
+ $this->functions->template_styles();
273
+ }
274
+
275
+ /**
276
+ * Return logo id
277
+ */
278
+ public function get_header_logo_id() {
279
+ return $this->functions->get_header_logo_id();
280
+ }
281
+
282
+ /**
283
+ * Show logo html
284
+ */
285
+ public function header_logo() {
286
+ $this->functions->header_logo();
287
+ }
288
+
289
+ /**
290
+ * Return/Show custom company name or default to blog name
291
+ */
292
+ public function get_shop_name() {
293
+ return $this->functions->get_shop_name();
294
+ }
295
+ public function shop_name() {
296
+ $this->functions->shop_name();
297
+ }
298
+
299
+ /**
300
+ * Return/Show shop/company address if provided
301
+ */
302
+ public function get_shop_address() {
303
+ return $this->functions->get_shop_address();
304
+ }
305
+ public function shop_address() {
306
+ $this->functions->shop_address();
307
+ }
308
+
309
+ /**
310
+ * Check if billing address and shipping address are equal
311
+ */
312
+ public function ships_to_different_address() {
313
+ return $this->functions->ships_to_different_address();
314
+ }
315
+
316
+ /**
317
+ * Return/Show billing address
318
+ */
319
+ public function get_billing_address() {
320
+ return $this->functions->get_billing_address();
321
+ }
322
+ public function billing_address() {
323
+ $this->functions->billing_address();
324
+ }
325
+
326
+ /**
327
+ * Return/Show billing email
328
+ */
329
+ public function get_billing_email() {
330
+ return $this->functions->get_billing_email();
331
+ }
332
+ public function billing_email() {
333
+ $this->functions->billing_email();
334
+ }
335
+
336
+ /**
337
+ * Return/Show billing phone
338
+ */
339
+ public function get_billing_phone() {
340
+ return $this->functions->get_billing_phone();
341
+ }
342
+ public function billing_phone() {
343
+ $this->functions->billing_phone();
344
+ }
345
+
346
+ /**
347
+ * Return/Show shipping address
348
+ */
349
+ public function get_shipping_address() {
350
+ return $this->functions->get_shipping_address();
351
+ }
352
+ public function shipping_address() {
353
+ $this->functions->shipping_address();
354
+ }
355
+
356
+ /**
357
+ * Return/Show a custom field
358
+ */
359
+ public function get_custom_field( $field_name ) {
360
+ return $this->functions->get_custom_field( $field_name );
361
+ }
362
+ public function custom_field( $field_name, $field_label = '', $display_empty = false ) {
363
+ $this->functions->custom_field( $field_name, $field_label, $display_empty );
364
+ }
365
+
366
+ /**
367
+ * Return/Show order notes
368
+ */
369
+ public function get_order_notes( $filter = 'customer' ) {
370
+ return $this->functions->get_order_notes( $filter );
371
+ }
372
+ public function order_notes( $filter = 'customer' ) {
373
+ $this->functions->order_notes( $filter );
374
+ }
375
+
376
+ /**
377
+ * Return/Show the current date
378
+ */
379
+ public function get_current_date() {
380
+ return $this->functions->get_current_date();
381
+ }
382
+ public function current_date() {
383
+ $this->functions->current_date();
384
+ }
385
+
386
+ /**
387
+ * Return/Show payment method
388
+ */
389
+ public function get_payment_method() {
390
+ return $this->functions->get_payment_method();
391
+ }
392
+ public function payment_method() {
393
+ $this->functions->payment_method();
394
+ }
395
+
396
+ /**
397
+ * Return/Show shipping method
398
+ */
399
+ public function get_shipping_method() {
400
+ return $this->functions->get_shipping_method();
401
+ }
402
+ public function shipping_method() {
403
+ $this->functions->shipping_method();
404
+ }
405
+
406
+ /**
407
+ * Return/Show order number
408
+ */
409
+ public function get_order_number() {
410
+ return $this->functions->get_order_number();
411
+ }
412
+ public function order_number() {
413
+ $this->functions->order_number();
414
+ }
415
+
416
+ /**
417
+ * Return/Show invoice number
418
+ */
419
+ public function get_invoice_number() {
420
+ return $this->functions->get_invoice_number();
421
+ }
422
+ public function invoice_number() {
423
+ $this->functions->invoice_number();
424
+ }
425
+
426
+ /**
427
+ * Return/Show the order date
428
+ */
429
+ public function get_order_date() {
430
+ return $this->functions->get_order_date();
431
+ }
432
+ public function order_date() {
433
+ $this->functions->order_date();
434
+ }
435
+
436
+ /**
437
+ * Return/Show the invoice date
438
+ */
439
+ public function get_invoice_date() {
440
+ return $this->functions->get_invoice_date();
441
+ }
442
+ public function invoice_date() {
443
+ $this->functions->invoice_date();
444
+ }
445
+
446
+ /**
447
+ * Return the order items
448
+ */
449
+ public function get_order_items() {
450
+ return $this->functions->get_order_items();
451
+ }
452
+
453
+ /**
454
+ * Return/show product attribute
455
+ */
456
+ public function get_product_attribute( $attribute_name, $product ) {
457
+ return $this->functions->get_product_attribute( $attribute_name, $product );
458
+ }
459
+ public function product_attribute( $attribute_name, $product ) {
460
+ $this->functions->product_attribute( $attribute_name, $product );
461
+ }
462
+
463
+
464
+ /**
465
+ * Return the order totals listing
466
+ */
467
+ public function get_woocommerce_totals() {
468
+ return $this->functions->get_woocommerce_totals();
469
+ }
470
+
471
+ /**
472
+ * Return/show the order subtotal
473
+ */
474
+ public function get_order_subtotal( $tax = 'excl', $discount = 'incl' ) { // set $tax to 'incl' to include tax, same for $discount
475
+ return $this->functions->get_order_subtotal( $tax, $discount );
476
+ }
477
+ public function order_subtotal( $tax = 'excl', $discount = 'incl' ) {
478
+ $this->functions->order_subtotal( $tax, $discount );
479
+ }
480
+
481
+ /**
482
+ * Return/show the order shipping costs
483
+ */
484
+ public function get_order_shipping( $tax = 'excl' ) { // set $tax to 'incl' to include tax
485
+ return $this->functions->get_order_shipping( $tax );
486
+ }
487
+ public function order_shipping( $tax = 'excl' ) {
488
+ $this->functions->order_shipping( $tax );
489
+ }
490
+
491
+ /**
492
+ * Return/show the total discount
493
+ */
494
+ public function get_order_discount( $type = 'total', $tax = 'incl' ) {
495
+ return $this->functions->get_order_discount( $type, $tax );
496
+ }
497
+ public function order_discount( $type = 'total', $tax = 'incl' ) {
498
+ $this->functions->order_discount( $type, $tax );
499
+ }
500
+
501
+ /**
502
+ * Return the order fees
503
+ */
504
+ public function get_order_fees( $tax = 'excl' ) {
505
+ return $this->functions->get_order_fees( $tax );
506
+ }
507
+
508
+ /**
509
+ * Return the order taxes
510
+ */
511
+ public function get_order_taxes() {
512
+ return $this->functions->get_order_taxes();
513
+ }
514
+
515
+ /**
516
+ * Return/show the order grand total
517
+ */
518
+ public function get_order_grand_total( $tax = 'incl' ) {
519
+ return $this->functions->get_order_grand_total( $tax );
520
+ }
521
+ public function order_grand_total( $tax = 'incl' ) {
522
+ $this->functions->order_grand_total( $tax );
523
+ }
524
+
525
+
526
+ /**
527
+ * Return/Show shipping notes
528
+ */
529
+ public function get_shipping_notes() {
530
+ return $this->functions->get_shipping_notes();
531
+ }
532
+ public function shipping_notes() {
533
+ $this->functions->shipping_notes();
534
+ }
535
+
536
+
537
+ /**
538
+ * Return/Show shop/company footer imprint, copyright etc.
539
+ */
540
+ public function get_footer() {
541
+ return $this->functions->get_footer();
542
+ }
543
+ public function footer() {
544
+ $this->functions->footer();
545
+ }
546
+
547
+ /**
548
+ * Return/Show Extra field 1
549
+ */
550
+ public function get_extra_1() {
551
+ return $this->functions->get_extra_1();
552
+ }
553
+ public function extra_1() {
554
+ $this->functions->extra_1();
555
+ }
556
+
557
+ /**
558
+ * Return/Show Extra field 2
559
+ */
560
+ public function get_extra_2() {
561
+ return $this->functions->get_extra_2();
562
+ }
563
+ public function extra_2() {
564
+ $this->functions->extra_2();
565
+ }
566
+
567
+ /**
568
+ * Return/Show Extra field 3
569
+ */
570
+ public function get_extra_3() {
571
+ return $this->functions->get_extra_3();
572
+ }
573
+ public function extra_3() {
574
+ $this->functions->extra_3();
575
+ }
576
+ }
577
+ }
578
+
579
+ /**
580
+ * Returns the main instance of WooCommerce PDF Invoices & Packing Slips to prevent the need to use globals.
581
+ *
582
+ * @since 1.6
583
+ * @return WPO_WCPDF
584
+ */
585
+ function WPO_WCPDF() {
586
+ return WooCommerce_PDF_Invoices::instance();
587
+ }
588
+
589
+ // Global for backwards compatibility.
590
  $GLOBALS['wpo_wcpdf'] = WPO_WCPDF();