diff --git a/TODO b/TODO index 773ac613be64a7f1cd0480c319534bac348d79ff..5509c5d5cd7e3e3833d2fecd4cca0f9b4cb526f7 100644 --- a/TODO +++ b/TODO @@ -38,3 +38,18 @@ woocommerce_new_order', $order_id ); Order created in BackEnd: Created: wp_insert_post => Assign placeholder (no address yet) Modified: + + +TODO: +Check order/invoice numbers for renewals: https://wordpress.org/support/topic/subscription-renewal-duplicate-invoice-number?replies=6#post-6138110 + + + + +INVOICES: +========= + +woocommerce-pdf-invoices-packing-slips: +-) Uses order_meta: _wcpdf_invoice_number => Set it manually to prevent the plugin from using its own counter +-) Calls filter wpo_wcpdf_invoice_number($invoice_number, $order_number, $order_id, $order_data) to format the invoice number. +-) Triggers before PDF creation: wp_wcpdf_before_pdf, wpo_wcpdf_process_order_ids, wpo_wcpdf_process_template_order($template_type, $order_id) diff --git a/assets/js/ordernumber-config.js b/assets/js/ordernumber-config.js index bd8421abd50eac19394937932c58eec4f99d4fa4..6d11565d2fa304ecad9078e5e5af5680333f0807 100644 --- a/assets/js/ordernumber-config.js +++ b/assets/js/ordernumber-config.js @@ -7,9 +7,23 @@ jQuery( function ( $ ) { if ($(this).is(':checked')) { $('#ordernumber_format').closest('tr').show(); $('#ordernumber_global').closest('tr').show(); + $('#ordernumber-countertable-ordernumber').closest('tr').show(); } else { $('#ordernumber_format').closest('tr').hide(); $('#ordernumber_global').closest('tr').hide(); + $('#ordernumber-countertable-ordernumber').closest('tr').hide(); + } + }).change(); + + $('input#customize_invoice').change(function() { + if ($(this).is(':checked')) { + $('#invoice_format').closest('tr').show(); + $('#invoice_global').closest('tr').show(); + $('#ordernumber-countertable-invoice').closest('tr').show(); + } else { + $('#invoice_format').closest('tr').hide(); + $('#invoice_global').closest('tr').hide(); + $('#ordernumber-countertable-invoice').closest('tr').hide(); } }).change(); diff --git a/library/ordernumber_helper.php b/library/ordernumber_helper.php index ecfb0f99ec4f45c5e60760d2d656ed9fad7e4be2..6b926399482f470a424a0150d6f55e579c2ad1f9 100644 --- a/library/ordernumber_helper.php +++ b/library/ordernumber_helper.php @@ -39,6 +39,14 @@ class OrdernumberHelper { $this->registerCallback ("setupDateTimeReplacements", array($this, "setupDateTimeReplacements")); } + static function getHelper() { + static $helper = null; + if (!$helper) { + $helper = new OrdernumberHelper(); + } + return $helper; + } + function getStyle($key) { if (isset($this->_styles[$key])) { return $this->_styles[$key]; @@ -388,7 +396,7 @@ class OrdernumberHelper { public function counter_modification_create_table($type, $counters) { $html=array(); $html[] = "<img src='" . $this->urlPath ('images', 'loading.gif') . "' class='ordernumber-loading' />"; - $html[] = "<table class=\"ordernumber-countertable " . $this->getStyle('counter-table-class') . "\" " . $this->getStyle('counter-table-style') . ">"; + $html[] = "<table class=\"ordernumber-countertable " . $this->getStyle('counter-table-class') . "\" " . $this->getStyle('counter-table-style') . " id='ordernumber-countertable-" . $type . "'>"; $html[] = "<thead>"; $html[] = " <tr>"; $html[] = " <th class='counter_format'>" . $this->__ ('PLG_ORDERNUMBER_COUNTERLIST_HEADER_COUNTER')."</th>"; @@ -560,4 +568,30 @@ var ajax_ordernumber = ' . json_encode($json) . '; return $js; } + public function ajax_counter_delete($type, $counter) { + // TODO: Check if counter value has changed meanwhile + $json = array('action' => 'delete_counter', 'success' => 0); + $json['success'] = $this->deleteCounter($type, $counter); + return $json; + } + + public function ajax_counter_add ($type, $counter, $value) { + // TODO: Check if counter value has changed meanwhile + $json = array('action' => 'add_counter', 'success' => 0); + if ($this->getCounter($type, $counter, -1) != -1) { + // Counter already exists => error message + $json['error'] = sprintf($this->__('Counter "%s" already exists, cannot create again.'), $counter); + } else { + $json['success'] = $this->setCounter($type, $counter, $value); + $json['row'] = $this->counter_modification_create_row($type, $counter, $value); + } + return $json; + } + + public function ajax_counter_set ($type, $counter, $value) { + $json = array('action' => 'set_counter', 'success' => 0); + $json['success'] = $this->setCounter($type, $counter, $value); + $json['row'] = $this->counter_modification_create_row($type, $counter, $value); + return $json; + } } diff --git a/ordernumbers_woocommerce.php b/ordernumbers_woocommerce.php index d6b2c6c8d392fb1b3216a451f9c8950081604a4f..656aa22de21c4b842a08fc8cbf0502cfa434a8b3 100644 --- a/ordernumbers_woocommerce.php +++ b/ordernumbers_woocommerce.php @@ -64,9 +64,11 @@ class OpenToolsOrdernumbers { // When a new order is created, we immediately assign the order number: add_action( 'wp_insert_post', array(&$this, 'check_assignNumber'), 10, 3); -// add_action( 'save_post', array(&$this, 'check_assignNumber'), 10, 3); // The filter to actually return the order number for the given order add_filter ('woocommerce_order_number', array(&$this, 'get_ordernumber'), 10, 2/*<= Also get the order object! */); + + + $this->thirdparty_wpo_wcpdf_init(); } @@ -101,8 +103,7 @@ class OpenToolsOrdernumbers { ), array( 'title' => $this->helper->__( 'Use global counter'), - 'desc' => $this->helper->__( 'A global counter is independent of the number format and will never reset. A non-global counter runs within the number format and will start from the inital value whenever any of the variables used in the format changes (to be precise: a new counter will be used, so it is possible to have multiple counters running in parallel).'), - 'desc_tip' => true, + 'desc' => $this->helper->__( 'A global counter never resets. Non-global counters run within each number format and reset whenever any variable changes.'), 'id' => 'ordernumber_global', 'type' => 'checkbox', 'default' => 'no', @@ -117,6 +118,51 @@ class OpenToolsOrdernumbers { ), array( 'type' => 'sectionend', 'id' => 'ordernumber_options' ), + + /** + * Invoice number settings + */ + + array( + 'name' => $this->helper->__( 'Advanced Invoice Numbers'), + 'desc' => $this->helper->__('This plugin currently supports modifying the invoice number formats of the following invoicing plugins: <a href="https://wordpress.org/plugins/woocommerce-pdf-invoices-packing-slips/">WooCommerce PDF Invoices & Packing Slips</a>'), + 'type' => 'title', + 'id' => 'invoice_options' + ), + + array( + 'name' => $this->helper->__( 'Customize Invoice Numbers'), + 'desc' => $this->helper->__( 'Check to use custom invoice numbers rather than the default format of your invoicing plugin.'), + 'id' => 'customize_invoice', + 'type' => 'checkbox', + 'default' => 'no' + ), + array( + 'title' => $this->helper->__( 'Invoice number format'), + 'desc' => $this->helper->__( 'The format for the invoice numbers (variables can be entered as [...], the counter is indicated by the #). To use a different counter name than displayed, put the custom counter name after a |, e.g. "[year]-[month]/#|[year]" to use the month in the invoice number, but reset the counter only yearly. Advanced settings for the counter can be added as [#####:start/step], e.g. [#:100] to start new counters at 100, or [#/5] to increment the counter by 5. The number of # in the format determine how many digits are shown at least, e.g. [########] will always show at least 8 digits for the counter, e.g. 00000014.'), + 'desc_tip' => true, + 'id' => 'invoice_format', + 'default' => '#', + 'type' => 'text', + 'css' => 'width: 100%', + ), + array( + 'title' => $this->helper->__( 'Use global counter'), + 'desc' => $this->helper->__( 'A global counter never resets. Non-global counters run within each number format and reset whenever any variable changes.'), + 'id' => 'invoice_global', + 'type' => 'checkbox', + 'default' => 'no', + ), + array( + 'name' => $this->helper->__( 'All invoice number counters'), + 'desc' => $this->helper->__( 'View and modify the current counter values. The counter value is the value used for the previous number. All changes are immediately applied!'), + 'desc_tip' => true, + 'id' => 'ordernumber_counters', + 'type' => 'ordernumber_counters', + 'nrtype' => 'invoice', + ), + array( 'type' => 'sectionend', 'id' => 'invoice_options' ), + // TODO: customize order password, and other numbers! array( @@ -131,19 +177,16 @@ class OpenToolsOrdernumbers { ), array( 'type' => 'sectionend', 'id' => 'ordernumber_variables' ), - array( - 'name' => $this->helper->__( 'Current Counters'), - 'type' => 'title', - 'id' => 'ordernumber_counters' - ), - array( 'type' => 'sectionend', 'id' => 'ordernumber_counters' ), - ); // Default options add_option ('customize_ordernumber', 'no'); add_option ('ordernumber_format', "#"); add_option ('ordernumber_global', 'no'); + + add_option ('customize_invoice', 'no'); + add_option ('invoice_format', "#"); + add_option ('invoice_global', 'no'); add_option ('ordernumber_variables', array()); } @@ -294,30 +337,17 @@ class OpenToolsOrdernumbers { */ public function counter_delete_callback() { - $json = array('action' => 'delete_counter', 'success' => 0); - $json['success'] = $this->helper->deleteCounter($_POST['nrtype'], $_POST['counter']); + $json = $this->helper->ajax_counter_delete($_POST['nrtype'], $_POST['counter']); wp_send_json($json); } public function counter_add_callback () { - $type = $_POST['nrtype']; - $format = $_POST['counter']; - $value = isset($_POST['value'])?$_POST['value']:"0"; - $json = array('action' => 'add_counter', 'success' => 0); - if ($this->helper->getCounter($type, $format, -1) != -1) { - // Counter already exists => error message - $json['error'] = sprintf($this->helper->__('Counter "%s" already exists, cannot create again.'), $format); - } else { - $json['success'] = $this->helper->setCounter($type, $format, $value); - $json['row'] = $this->helper->counter_modification_create_row($type, $format, $value); - } + $json = $this->helper->ajax_counter_add($_POST['nrtype'], $_POST['counter'], isset($_POST['value'])?$_POST['value']:"0"); wp_send_json($json); } public function counter_set_callback () { - $json = array('action' => 'set_counter', 'success' => 0); - $json['success'] = $this->helper->setCounter($_POST['nrtype'], $_POST['counter'], $_POST['value']); - $json['row'] = $this->helper->counter_modification_create_row($_POST['nrtype'], $_POST['counter'], $_POST['value']); + $json = $this->helper->ajax_counter_set($_POST['nrtype'], $_POST['counter'], $_POST['value']); wp_send_json($json); } @@ -460,6 +490,15 @@ class OpenToolsOrdernumbers { return $number; } + function get_or_create_number($orderid, $order, $type = 'ordernumber') { + $stored_number = get_post_meta( $orderid, $this->ordernumber_meta.$type, true); + if (!empty($stored_number)) { + return $stored_number; + } else { + return $this->generateNumber($orderid, $order, $type); + } + } + /** * The generic function to retrieve a particular number */ @@ -479,9 +518,81 @@ class OpenToolsOrdernumbers { /** * Callback function for Woocommerce to retrieve the ordernumber for an order + * The hook to customize order numbers (requests the order number from the database; + * creates a new ordernumber if no entry exists in the database) */ function get_ordernumber($orderid, $order) { - return $this->get_number($orderid, $order); + return $this->get_number($orderid, $order, 'ordernumber'); } + + /** ************************************************************ + * Support for WPO WooCommerce PDF Invoices and Packaging Slips + ** ************************************************************ + * + * - Invoice numbers are stored in the _wcpdf_invoice_number post meta + * - the filter wpo_wcpdf_invoice_number($invoice_number, $order_number, $order_id, $order_data) is called + * to format the (existing) invoice number retrieved from that post meta + * - The action wpo_wcpdf_process_template_order($template_type, $order_id) is called right before + * the invoice is created. There we can already set the _wcpdf_invoice_number post meta with our own value + */ + + /** + * Initialize support for WPO WooCommerce PDF Invoices and Packaging Slips + */ + protected function thirdparty_wpo_wcpdf_init() { + // TODO: Wheck whether the woocommerce-pdf-invoices-packing-slips plugin is installed at all + add_filter ('wpo_wcpdf_invoice_number', array($this, 'thirdparty_wpo_wcpdf_invoice_number'), 30, 4); + add_action ('wpo_wcpdf_process_template_order', array($this, 'thirdparty_wpo_wcpdf_create_number'), 10, 2); + // Disable the invoice number-related controls in the config of the other plugin + add_action ('woocommerce_page_wpo_wcpdf_options_page', array($this, 'thirdparty_wpo_wcpdf_remove_options')); + } + + /** + * Support for WPO WooCommere PDF Invoices and Packaging Slips + * Filter to return the invoice number => simply return the first argument unchanged (was already + * created in the correct format, no need to format it now again) + */ + function thirdparty_wpo_wcpdf_invoice_number($invoice_number, $order_number, $order_id, $order_data) { + $nr = $this->get_number($order_id, $order_data, 'invoice'); + if ($nr == $order_id) { + // No number was found, so the default is the order id => reset to invoice number + return $invoice_number; + } else { + return $nr; + } + } + /** + * The action to actually create the number and store it as post meta with the order + */ + function thirdparty_wpo_wcpdf_create_number($type, $orderid) { + if ($type=='invoice' && (get_option('customize_'.$type, 'no')!='no') ) { + $_of = new WC_Order_Factory(); + $order = $_of->get_order($orderid); + $number = $this->get_or_create_number($orderid, $order, $type); + // TODO: Store the invoice number counter in _wcpdf_invoice_number and the custom invoice + // number in the opentools meta, because the plugin assumes the number to be numeric... + update_post_meta( $orderid, '_wcpdf_invoice_number', $number ); + } + } + + /** + * The action that is called for the WPO WCPDF invoice plugin only, when the options page is loaded. + * If this plugin is enabled and invoice numbers are configured, we simply remove all invoice number-specific + * settings, because this plugin will be responsible.... + */ + function thirdparty_wpo_wcpdf_remove_options() { + global $wp_settings_fields; + if (get_option('customize_invoice', 'no')!='no') { + $wp_settings_fields['wpo_wcpdf_template_settings']['invoice']['display_number']['title'] = $this->helper->__('Display invoice number'); + $wp_settings_fields['wpo_wcpdf_template_settings']['invoice']['display_number']['args']['description'] = $this->helper->__('The <a href="admin.php?page=wc-settings&tab=checkout§ion=ordernumber">Open Tools Ordernumber plugin</a> has invoice numbers enabled and will generate invoice numbers for this plugin.' ); + unset($wp_settings_fields['wpo_wcpdf_template_settings']['invoice']['next_invoice_number']); + unset($wp_settings_fields['wpo_wcpdf_template_settings']['invoice']['invoice_number_formatting']); + } else { + $wp_settings_fields['wpo_wcpdf_template_settings']['invoice']['display_number']['args']['description'] = $this->helper->__('To let the Open Tools ordernumber plugin create invoice numbers with your desired format, please enable invoices in <a href="admin.php?page=wc-settings&tab=checkout§ion=ordernumber">that plugin\'s configuration page</a>.' ); + } + } + + + }