WooCommerce PDF Invoices & Packing Slips - Version 2.4.0

Version Description

  • Feature: Option to use order number & date for invoice number & date
  • Fix: prevent errors during update when WC not active
  • Fix: don't auto create invoice number when manually entered & directly changing order status
  • Fix: invoice tax amount for refunded orders (in combination with WooCommerce tax setting "as a single total")
  • Tweak: Default to today's date when editing empty invoice date
Download this release

Release Info

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

Code changes from version 2.3.5 to 2.4.0

assets/js/admin-script.js CHANGED
@@ -1,31 +1,39 @@
1
- jQuery( function( $ ) {
2
- $('.edit-next-number').click( function( event ) {
3
- // enable input & show save button
4
- $( this ).hide();
5
- $( this ).siblings( 'input' ).prop('disabled', false);
6
- $( this ).siblings( '.save-next-number.button').show();
7
- });
8
-
9
- $('.save-next-number').click( function( event ) {
10
- $input = $( this ).siblings( 'input' );
11
- $input.addClass('ajax-waiting');
12
- var data = {
13
- security: $input.data('nonce'),
14
- action: "wpo_wcpdf_set_next_number",
15
- store: $input.data('store'),
16
- number: $input.val(),
17
- };
18
-
19
- xhr = $.ajax({
20
- type: 'POST',
21
- url: wpo_wcpdf_admin.ajaxurl,
22
- data: data,
23
- success: function( response ) {
24
- $input.removeClass('ajax-waiting');
25
- $input.siblings( '.edit-next-number' ).show();
26
- $input.prop('disabled', 'disabled');
27
- $input.siblings( '.save-next-number.button').hide();
28
- }
29
- });
30
- });
 
 
 
 
 
 
 
 
31
  });
1
+ jQuery( function( $ ) {
2
+ $('.edit-next-number').click( function( event ) {
3
+ // enable input & show save button
4
+ $( this ).hide();
5
+ $( this ).siblings( 'input' ).prop('disabled', false);
6
+ $( this ).siblings( '.save-next-number.button').show();
7
+ });
8
+
9
+ $('.save-next-number').click( function( event ) {
10
+ $input = $( this ).siblings( 'input' );
11
+ $input.addClass('ajax-waiting');
12
+ var data = {
13
+ security: $input.data('nonce'),
14
+ action: "wpo_wcpdf_set_next_number",
15
+ store: $input.data('store'),
16
+ number: $input.val(),
17
+ };
18
+
19
+ xhr = $.ajax({
20
+ type: 'POST',
21
+ url: wpo_wcpdf_admin.ajaxurl,
22
+ data: data,
23
+ success: function( response ) {
24
+ $input.removeClass('ajax-waiting');
25
+ $input.siblings( '.edit-next-number' ).show();
26
+ $input.prop('disabled', 'disabled');
27
+ $input.siblings( '.save-next-number.button').hide();
28
+ }
29
+ });
30
+ });
31
+
32
+ $("[name='wpo_wcpdf_documents_settings_invoice[display_number]']").change(function (event) {
33
+ if ($(this).val() == 'order_number') {
34
+ $(this).closest('td').find('.description').slideDown();
35
+ } else {
36
+ $(this).closest('td').find('.description').hide();
37
+ }
38
+ }).change();
39
  });
includes/class-wcpdf-admin.php CHANGED
@@ -20,7 +20,7 @@ class Admin {
20
  add_action( 'admin_footer', array( $this, 'bulk_actions' ) );
21
  add_filter( 'woocommerce_shop_order_search_fields', array( $this, 'search_fields' ) );
22
 
23
- add_action( 'save_post', array( $this,'save_invoice_number_date' ) );
24
 
25
  // manually send emails
26
  // WooCommerce core processes order actions at priority 50
@@ -408,7 +408,7 @@ class Admin {
408
  <?php if ( $invoice->exists() && !empty($invoice_date) ) : ?>
409
  <input type="text" class="date-picker-field" name="wcpdf_invoice_date" id="wcpdf_invoice_date" maxlength="10" value="<?php echo $invoice_date->date_i18n( 'Y-m-d' ); ?>" pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])" disabled="disabled"/>@<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 $invoice_date->date_i18n( 'H' ) ?>" 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 $invoice_date->date_i18n( 'i' ); ?>" pattern="[0-5]{1}[0-9]{1}" />
410
  <?php else : ?>
411
- <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" />
412
  <?php endif; ?>
413
  </p>
414
  </div>
@@ -447,7 +447,7 @@ class Admin {
447
  /**
448
  * Save invoice number
449
  */
450
- public function save_invoice_number_date($post_id) {
451
  $post_type = get_post_type( $post_id );
452
  if( $post_type == 'shop_order' ) {
453
  // bail if this is not an actual 'Save order' action
20
  add_action( 'admin_footer', array( $this, 'bulk_actions' ) );
21
  add_filter( 'woocommerce_shop_order_search_fields', array( $this, 'search_fields' ) );
22
 
23
+ add_action( 'woocommerce_process_shop_order_meta', array( $this,'save_invoice_number_date' ), 10, 2 );
24
 
25
  // manually send emails
26
  // WooCommerce core processes order actions at priority 50
408
  <?php if ( $invoice->exists() && !empty($invoice_date) ) : ?>
409
  <input type="text" class="date-picker-field" name="wcpdf_invoice_date" id="wcpdf_invoice_date" maxlength="10" value="<?php echo $invoice_date->date_i18n( 'Y-m-d' ); ?>" pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])" disabled="disabled"/>@<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 $invoice_date->date_i18n( 'H' ) ?>" 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 $invoice_date->date_i18n( 'i' ); ?>" pattern="[0-5]{1}[0-9]{1}" />
410
  <?php else : ?>
411
+ <input type="text" class="date-picker-field" name="wcpdf_invoice_date" id="wcpdf_invoice_date" maxlength="10" disabled="disabled" value="<?php echo date_i18n( 'Y-m-d' ); ?>" 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" />
412
  <?php endif; ?>
413
  </p>
414
  </div>
447
  /**
448
  * Save invoice number
449
  */
450
+ public function save_invoice_number_date($post_id, $post) {
451
  $post_type = get_post_type( $post_id );
452
  if( $post_type == 'shop_order' ) {
453
  // bail if this is not an actual 'Save order' action
includes/class-wcpdf-main.php CHANGED
@@ -226,7 +226,7 @@ class Main {
226
  }
227
  }
228
 
229
- return $document_types;
230
  }
231
 
232
  /**
226
  }
227
  }
228
 
229
+ return apply_filters( 'wpo_wcpdf_document_types_for_email', $document_types, $email_id, $order );
230
  }
231
 
232
  /**
includes/documents/abstract-wcpdf-order-document-methods.php CHANGED
@@ -692,7 +692,7 @@ abstract class Order_Document_Methods extends Order_Document {
692
  }
693
 
694
  public function get_tax_rates_from_order( $order ) {
695
- if ( !empty( $order ) && method_exists( $order, 'get_version' ) && version_compare( $order->get_version(), '3.7', '>=' ) ) {
696
  $tax_rates = array();
697
  $tax_items = $order->get_items( array('tax') );
698
 
@@ -855,7 +855,7 @@ abstract class Order_Document_Methods extends Order_Document {
855
  $tax_string_array[] = sprintf( '%s %s', $tax_amount, $tax->label );
856
  }
857
  } else {
858
- $tax_string_array[] = sprintf( '%s %s', wc_price( $this->order->get_total_tax() - $this->order->get_total_tax_refunded(), array( 'currency' => WCX_Order::get_prop( $this->order, 'currency' ) ) ), WC()->countries->tax_or_vat() );
859
  }
860
  if ( ! empty( $tax_string_array ) ) {
861
  if ( version_compare( WOOCOMMERCE_VERSION, '2.6', '>=' ) ) {
692
  }
693
 
694
  public function get_tax_rates_from_order( $order ) {
695
+ if ( !empty( $order ) && method_exists( $order, 'get_version' ) && version_compare( $order->get_version(), '3.7', '>=' ) && version_compare( WC_VERSION, '3.7', '>=' ) ) {
696
  $tax_rates = array();
697
  $tax_items = $order->get_items( array('tax') );
698
 
855
  $tax_string_array[] = sprintf( '%s %s', $tax_amount, $tax->label );
856
  }
857
  } else {
858
+ $tax_string_array[] = sprintf( '%s %s', wc_price( $this->order->get_total_tax(), array( 'currency' => WCX_Order::get_prop( $this->order, 'currency' ) ) ), WC()->countries->tax_or_vat() );
859
  }
860
  if ( ! empty( $tax_string_array ) ) {
861
  if ( version_compare( WOOCOMMERCE_VERSION, '2.6', '>=' ) ) {
includes/documents/abstract-wcpdf-order-document.php CHANGED
@@ -748,6 +748,11 @@ abstract class Order_Document {
748
  $mailer = WC()->mailer();
749
  } else {
750
  global $woocommerce;
 
 
 
 
 
751
  $mailer = $woocommerce->mailer();
752
  }
753
  $wc_emails = $mailer->get_emails();
748
  $mailer = WC()->mailer();
749
  } else {
750
  global $woocommerce;
751
+
752
+ if ( empty( $woocommerce ) ) { // bail if WooCommerce not active
753
+ return apply_filters( 'wpo_wcpdf_wc_emails', array() );
754
+ }
755
+
756
  $mailer = $woocommerce->mailer();
757
  }
758
  $wc_emails = $mailer->get_emails();
includes/documents/class-wcpdf-invoice.php CHANGED
@@ -65,7 +65,12 @@ class Invoice extends Order_Document_Methods {
65
  WCX_Order::update_meta_data( $this->order, "_wcpdf_{$this->slug}_settings", $settings );
66
  }
67
 
68
- $this->set_date( current_time( 'timestamp', true ) );
 
 
 
 
 
69
  $this->init_number();
70
  }
71
 
@@ -79,6 +84,11 @@ class Invoice extends Order_Document_Methods {
79
  if ( apply_filters( 'woocommerce_invoice_number_by_plugin', false ) || apply_filters( 'wpo_wcpdf_external_invoice_number_enabled', false, $this ) ) {
80
  $invoice_number = apply_filters( 'woocommerce_generate_invoice_number', null, $this->order );
81
  $invoice_number = apply_filters( 'wpo_wcpdf_external_invoice_number', $invoice_number, $this );
 
 
 
 
 
82
  if ( is_numeric($invoice_number) || $invoice_number instanceof Document_Number ) {
83
  $this->set_number( $invoice_number );
84
  } else {
@@ -87,7 +97,7 @@ class Invoice extends Order_Document_Methods {
87
  $formatted_number = $invoice_number;
88
  $number = (int) preg_replace('/\D/', '', $invoice_number);
89
  $invoice_number = compact( 'number', 'formatted_number' );
90
- $this->set_number( $invoice_number );
91
  }
92
  return $invoice_number;
93
  }
@@ -235,24 +245,38 @@ class Invoice extends Order_Document_Methods {
235
  'type' => 'setting',
236
  'id' => 'display_date',
237
  'title' => __( 'Display invoice date', 'woocommerce-pdf-invoices-packing-slips' ),
238
- 'callback' => 'checkbox',
239
  'section' => 'invoice',
240
  'args' => array(
241
- 'option_name' => $option_name,
242
- 'id' => 'display_date',
243
- 'value' => 'invoice_date',
 
 
 
 
244
  )
245
  ),
246
  array(
247
  'type' => 'setting',
248
  'id' => 'display_number',
249
  'title' => __( 'Display invoice number', 'woocommerce-pdf-invoices-packing-slips' ),
250
- 'callback' => 'checkbox',
251
  'section' => 'invoice',
252
  'args' => array(
253
- 'option_name' => $option_name,
254
- 'id' => 'display_number',
255
- 'value' => 'invoice_number',
 
 
 
 
 
 
 
 
 
 
256
  )
257
  ),
258
  array(
65
  WCX_Order::update_meta_data( $this->order, "_wcpdf_{$this->slug}_settings", $settings );
66
  }
67
 
68
+ if ( isset( $this->settings['display_date'] ) && $this->settings['display_date'] == 'order_date' && !empty( $this->order ) ) {
69
+ $this->set_date( WCX_Order::get_prop( $this->order, 'date_created' ) );
70
+ } else {
71
+ $this->set_date( current_time( 'timestamp', true ) );
72
+ }
73
+
74
  $this->init_number();
75
  }
76
 
84
  if ( apply_filters( 'woocommerce_invoice_number_by_plugin', false ) || apply_filters( 'wpo_wcpdf_external_invoice_number_enabled', false, $this ) ) {
85
  $invoice_number = apply_filters( 'woocommerce_generate_invoice_number', null, $this->order );
86
  $invoice_number = apply_filters( 'wpo_wcpdf_external_invoice_number', $invoice_number, $this );
87
+ } elseif ( isset( $this->settings['display_number'] ) && $this->settings['display_number'] == 'order_number' && !empty( $this->order ) ) {
88
+ $invoice_number = $this->order->get_order_number();
89
+ }
90
+
91
+ if ( !empty( $invoice_number ) ) { // overriden by plugin or set to order number
92
  if ( is_numeric($invoice_number) || $invoice_number instanceof Document_Number ) {
93
  $this->set_number( $invoice_number );
94
  } else {
97
  $formatted_number = $invoice_number;
98
  $number = (int) preg_replace('/\D/', '', $invoice_number);
99
  $invoice_number = compact( 'number', 'formatted_number' );
100
+ $this->set_number( $invoice_number );
101
  }
102
  return $invoice_number;
103
  }
245
  'type' => 'setting',
246
  'id' => 'display_date',
247
  'title' => __( 'Display invoice date', 'woocommerce-pdf-invoices-packing-slips' ),
248
+ 'callback' => 'select',
249
  'section' => 'invoice',
250
  'args' => array(
251
+ 'option_name' => $option_name,
252
+ 'id' => 'display_date',
253
+ 'options' => array(
254
+ '' => __( 'No' , 'woocommerce-pdf-invoices-packing-slips' ),
255
+ 'invoice_date' => __( 'Invoice Date' , 'woocommerce-pdf-invoices-packing-slips' ),
256
+ 'order_date' => __( 'Order Date' , 'woocommerce-pdf-invoices-packing-slips' ),
257
+ ),
258
  )
259
  ),
260
  array(
261
  'type' => 'setting',
262
  'id' => 'display_number',
263
  'title' => __( 'Display invoice number', 'woocommerce-pdf-invoices-packing-slips' ),
264
+ 'callback' => 'select',
265
  'section' => 'invoice',
266
  'args' => array(
267
+ 'option_name' => $option_name,
268
+ 'id' => 'display_number',
269
+ 'options' => array(
270
+ '' => __( 'No' , 'woocommerce-pdf-invoices-packing-slips' ),
271
+ 'invoice_number' => __( 'Invoice Number' , 'woocommerce-pdf-invoices-packing-slips' ),
272
+ 'order_number' => __( 'Order Number' , 'woocommerce-pdf-invoices-packing-slips' ),
273
+ ),
274
+ 'description' => sprintf(
275
+ '<strong>%s</strong> %s <a href="https://docs.wpovernight.com/woocommerce-pdf-invoices-packing-slips/invoice-numbers-explained/#why-is-the-pdf-invoice-number-different-from-the-woocommerce-order-number">%s</a>',
276
+ __( 'Warning!', 'woocommerce-pdf-invoices-packing-slips' ),
277
+ __( 'Using the Order Number as invoice number is not recommended as this may lead to gaps in the invoice number sequence (even when order numbers are sequential).', 'woocommerce-pdf-invoices-packing-slips' ),
278
+ __( 'More information', 'woocommerce-pdf-invoices-packing-slips' )
279
+ ),
280
  )
281
  ),
282
  array(
includes/wcpdf-functions.php CHANGED
@@ -127,6 +127,9 @@ function wcpdf_get_pdf_maker( $html, $settings = array() ) {
127
  }
128
 
129
  function wcpdf_pdf_headers( $filename, $mode = 'inline', $pdf = null ) {
 
 
 
130
  switch ($mode) {
131
  case 'download':
132
  header('Content-Description: File Transfer');
127
  }
128
 
129
  function wcpdf_pdf_headers( $filename, $mode = 'inline', $pdf = null ) {
130
+ if ( ! headers_sent() ) {
131
+ header_remove();
132
+ }
133
  switch ($mode) {
134
  case 'download':
135
  header('Content-Description: File Transfer');
readme.txt CHANGED
@@ -5,7 +5,7 @@ Tags: woocommerce, pdf, invoices, packing slips, print, delivery notes, invoice,
5
  Requires at least: 3.5
6
  Tested up to: 5.3
7
  Requires PHP: 5.3
8
- Stable tag: 2.3.5
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
@@ -102,6 +102,14 @@ There's a setting on the Status tab of the settings page that allows you to togg
102
 
103
  == Changelog ==
104
 
 
 
 
 
 
 
 
 
105
  = 2.3.5 =
106
  * Feature: Accept single order ID for wcpdf_get_document function
107
  * Feature: Filter to change number store for invoice
5
  Requires at least: 3.5
6
  Tested up to: 5.3
7
  Requires PHP: 5.3
8
+ Stable tag: 2.4.0
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
102
 
103
  == Changelog ==
104
 
105
+ = 2.4.0 =
106
+ * Feature: Option to use order number & date for invoice number & date
107
+ * Fix: prevent errors during update when WC not active
108
+ * Fix: don't auto create invoice number when manually entered & directly changing order status
109
+ * Fix: invoice tax amount for refunded orders (in combination with WooCommerce tax setting "as a single total")
110
+ * Tweak: Default to today's date when editing empty invoice date
111
+
112
+
113
  = 2.3.5 =
114
  * Feature: Accept single order ID for wcpdf_get_document function
115
  * Feature: Filter to change number store for invoice
woocommerce-pdf-invoices-packingslips.php CHANGED
@@ -3,7 +3,7 @@
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: 2.3.5
7
  * Author: Ewout Fernhout
8
  * Author URI: http://www.wpovernight.com
9
  * License: GPLv2 or later
@@ -21,7 +21,7 @@ if ( !class_exists( 'WPO_WCPDF' ) ) :
21
 
22
  class WPO_WCPDF {
23
 
24
- public $version = '2.3.5';
25
  public $plugin_basename;
26
  public $legacy_mode;
27
 
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: 2.4.0
7
  * Author: Ewout Fernhout
8
  * Author URI: http://www.wpovernight.com
9
  * License: GPLv2 or later
21
 
22
  class WPO_WCPDF {
23
 
24
+ public $version = '2.4.0';
25
  public $plugin_basename;
26
  public $legacy_mode;
27