settings = array( array( 'name' => __( 'Advanced Order Numbers', 'woocommerce-advanced-ordernumbers' ), 'desc' => __( 'Configure the format and the counters of the order numbers in WooCommerce.', 'woocommerce-advanced-ordernumbers' ), 'type' => 'title', 'id' => 'ordernumber_options' ), array( 'name' => __( 'Customize Order Numbers', 'woocommerce-advanced-ordernumbers' ), 'desc' => __( 'Check to use custom order numbers rather than the default wordpress post ID.', 'woocommerce-advanced-ordernumbers' ), 'id' => 'customize_ordernumber', 'type' => 'checkbox', 'default' => 'no' ), array( 'title' => __( 'Order number format', 'woocommerce-advanced-ordernumbers' ), 'desc' => __( 'The format for the order numbers (variables can be entered as [...], the counter is indicated by the #). To use a different counter name than displayed, put the custom counter name after a |, e.g. "[year]-[month]/#|[year]" to use the month in the order number, but reset the counter only yearly.', 'woocommerce-advanced-ordernumbers' ), 'desc_tip' => true, 'id' => 'ordernumber_format', 'default' => '#', 'type' => 'text', ), array( 'title' => __( 'Global', 'woocommerce-advanced-ordernumbers' ), 'desc' => __( 'Counter Scope', 'woocommerce-advanced-ordernumbers' ), 'id' => 'ordernumber_global', 'type' => 'checkbox', 'default' => 'no' ), array( 'title' => __( 'Counter Digits', 'woocommerce-advanced-ordernumbers' ), 'desc' => __( 'Minimum number of digits for the number', 'woocommerce-advanced-ordernumbers' ), 'desc_tip' => true, 'id' => 'ordernumber_padding', 'type' => 'number', 'default' => '0' ), array( 'title' => __( 'Counter Start', 'woocommerce-advanced-ordernumbers' ), 'desc' => __( 'Start value for each new counter', 'woocommerce-advanced-ordernumbers' ), 'desc_tip' => true, 'id' => 'ordernumber_start', 'type' => 'number', 'default' => '1' ), array( 'title' => __( 'Counter step', 'woocommerce-advanced-ordernumbers' ), 'desc' => __( 'By how much the counter will be increased after each order. Typically 1.', 'woocommerce-advanced-ordernumbers' ), 'desc_tip' => true, 'id' => 'ordernumber_step', 'type' => 'number', 'default' => '1' ), array( 'type' => 'sectionend', 'id' => 'ordernumber_options' ), // TODO: customize order password, and other numbers! ); // Default options add_option ('customize_ordernumber', '0'); add_option ('ordernumber_format', "#"); add_option ('ordernumber_global', '0'); add_option ('ordernumber_padding', '1'); add_option ('ordernumber_start', '1'); add_option ('ordernumber_step', '1'); // register actions add_filter( 'woocommerce_get_sections_checkout', array($this, 'add_admin_section')); // Add the ordernumber post meta to the search in the backend add_filter( 'woocommerce_shop_order_search_fields', array($this, 'order_search_fields')); // The checkout page assumes all subpages are payment gateways, so we have to override this: add_action( 'woocommerce_settings_checkout', array( $this, 'output' ) ); add_action( 'woocommerce_settings_save_checkout', array( $this, 'save' ) ); // register filters add_filter("plugin_action_links_$plugin", array(&$this, 'plugin_settings_link')); add_filter ('woocommerce_order_number', array(&$this, 'get_ordernumber'), 10, 2/*<= Also get the order object! */); } // Activate the plugin public static function activate() {} // Deactivate the plugin public static function deactivate() {} function add_admin_section($sections) { $sections['ordernumber'] = __( 'Order Numbers', 'woocommerce-advanced-ordernumbers'); return $sections; } public function output() { global $current_section; if ($current_section == 'ordernumber') { $settings = $this->settings; WC_Admin_Settings::output_fields( $settings ); } } public function save() { global $current_section; if ($current_section == 'ordernumber') { $settings = $this->settings; WC_Admin_Settings::save_fields( $settings ); } } /** * Hook to add the order numer post meta field to the searchable field * (so the admin can search for the order number in the backend) */ public function order_search_fields($fields) { $fields[] = $this->ordernumber_meta; return $fields; } /** * Counter handling (simple loading/storing counters), storing them as options */ function _getCounter($type, $format, $start=1) { $count = get_option ('ordernumber-counter-'.$type.'-'.$format, $start); return $count; } // Insert new counter value into the db or update existing one function _setCounter($type, $format, $value) { return update_option('ordernumber-counter-'.$type.'-'.$format, $value); } /** * Variable replacements: * - Random strings/digits * - Order properties * - User properties */ /* Return a random "string" of the given length taken from the given alphabet */ static function randomString($alphabet, $len) { $alen = strlen($alphabet); $r = ""; for ($n=0; $n<$len; $n++) { $r .= $alphabet[mt_rand(0, $alen-1)]; } return $r; } function replaceRandom ($match) { /* the regexp matches (random)(Type)(Len) as match, Type and Len is optional */ $len = ($match[3]?$match[3]:1); // Fallback: If no Type is given, use Digit $alphabet = "0123456789"; // Select the correct alphabet depending on Type switch (strtolower($match[2])) { case "digit": $alphabet = "0123456789"; break; case "hex": $alphabet = "0123456789abcdef"; break; case "letter": $alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; break; case "uletter": $alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; break; case "lletter": $alphabet = "abcdefghijklmnopqrstuvwxyz"; break; case "alphanum": $alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; break; } return self::randomString ($alphabet, $len); } /* replace the variables in the given format. $type indicates the type of number, currently only 'ordernumber', because WooCommerce does not support invoices or customer numbers. We might allow the shop owner to customize the order password, though. */ function replace_fields ($fmt, $type, $order) { // First, replace all randomXXX[n] fields. This needs to be done with a regexp and a callback: $fmt = preg_replace_callback ('/\[(random)(.*?)([0-9]*?)\]/', array($this, 'replaceRandom'), $fmt); $reps = array ( "[year]" => date ("Y"), "[year2]" => date ("y"), "[month]" => date("m"), "[day]" => date("d"), "[hour]" => date("H"), "[hour12]" => date("h"), "[ampm]" => date("a"), "[minute]" => date("i"), "[second]" => date("s"), "[orderid]" => $order->id, "[userid]" => $order->get_user_id(), ); $reps["[ipaddress]"] = $order->customer_ip_address; $reps["[orderstatus]"] = $order->get_status(); $reps["[email]"] = $order->billing_email; $reps["[firstname]"] = $order->billing_first_name; $reps["[lastname]"] = $order->billing_last_name; $reps["[company]"] = $order->billing_company; $reps["[zip]"] = $order->billing_postcode; $reps["[postcode]"] = $order->billing_postcode; $reps["[city]"] = $order->billing_city; $country = $order->billing_country; $state = $order->billing_state; $allcountries = WC()->countries->get_countries(); $states = WC()->countries->get_states($country); $reps["[country]"] = $country; $reps["[countryname]"] = ( isset( $allcountries[ $country ] ) ) ? $allcountries[ $country ] : $country; $reps["[state]"] = $state; $reps["[statename]"] = ( $country && $state && isset( $states[ $country ][ $state ] ) ) ? $states[ $country ][ $state ] : $state; $reps["[articles]"] = $order->get_item_count(); $reps["[currency]"] = $order->get_order_currency(); $reps["[downloadpermitted]"] = $order->is_download_permitted(); $reps["[hasdownloads]"] = $order->has_downloadable_item(); // $reps["[coupons]"] = $order->get_used_coupons(); if ($type != 'ordernumber') { $reps["[ordernumber]"] = $order->get_order_number(); } $user = $order->get_user(); if ($user) { // TODO: Shall we supply a variable for the user's login / display name? // $reps["[user]"] = print_r($user,1); // if (isset($order->username)) $reps["[username]"] = $order->username; } // Allow customization via plugins: filter function($reps, $order, $type, $fmt) $reps = apply_filters( 'opentools_ordernumber_replacements', $reps, $order, $type, $fmt); return str_ireplace (array_keys($reps), array_values($reps), $fmt); } function create_ordernumber($orderid, $order, $type='ordernumber') { if (get_option('customize_'.$type, 'false')) { $fmt = get_option ($type.'_format', "#"); $global = get_option ($type.'_global', 1); $padding = get_option ($type.'_padding', 1); $step = get_option ($type.'_step', 1); $start = get_option ($type.'_start', 1)-$step; // The counter contains the PREVIOUS number! $nr = $this->replace_fields ($fmt, $type, $order); // Split at a | to get the number format and a possibly different counter increment format // If a separate counter format is given after the |, use it, otherwise reuse the number format itself as counter format $parts = explode ("|", $nr); $format = $parts[0]; $counterfmt = ($global==1)?"":$parts[(count($parts)>1)?1:0]; // Look up the current counter $count = $this->_getCounter($type, $counterfmt, $start) + $step; $this->_setCounter($type, $counterfmt, $count); // return the format with the counter inserted $number = str_replace ("#", sprintf('%0' . $padding . 's', $count), $format); update_post_meta( $orderid, $this->ordernumber_meta, $number ); return $number; } else { return $orderid; } } /** * 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) { $stored_number = get_post_meta( $orderid, $this->ordernumber_meta, 'true'); if (!empty($stored_number)) { return $stored_number; } elseif (get_option('customize_ordernumber', 'false')) { // create a new one (if this is a new order, otherwise we don't have/generate a number!) // TODO: Check whether this is a new number! Otherwise, do NOT create an order number $number = $this->create_ordernumber($orderid, $order, 'ordernumber'); return $number; } else { print("