nce 1.3.3 * Adding padding number to sequential number * @return $order_number with padding */ public static function add_order_no_padding($order_number) { $padding = ''; $padding_no=get_option('wt_sequence_order_number_padding',0); $padding_count =(int) $padding_no - strlen($order_number); if ($padding_count > 0) { for ($i = 0; $i < $padding_count; $i++) { $padding .= '0'; } } update_option('wt_sequence_last_order_number_padding',$padding_no); return $padding.$order_number; } /** * @since 1.3.6 * Replace date shortcode from sequential number prefix/suffix data * @return string */ public static function get_date_from_shortcode($shortcode_text, $order_id) { preg_match_all("/\[([^\]]*)\]/", $shortcode_text, $matches); if(!empty($matches[1])) { foreach($matches[1] as $date_shortcode) { $match=array(); $date_val=time(); $order_date_format=$date_shortcode; $order_date_format=apply_filters('wt_order_number_date_format',$order_date_format); $order = new WC_Order($order_id); if(!empty($order)) { $order_date = $order->get_date_created(); } $date_val = $order_date->format($order_date_format); $shortcode_text=str_replace("[$date_shortcode]", $date_val, $shortcode_text); } } return $shortcode_text; } /** * @since 1.3.6 * Add Prefix/Suffix to sequential number * @return string */ public static function add_prefix_suffix($padded_order_number,$order_id) { $order_template = get_option('wt_sequence_order_number_format'); $prefix = self::get_sequence_prefix($order_id); $date_prefix = self::get_sequence_prefix_date($order_id); $suffix=''; $date_suffix=''; if($order_template=="") { if($prefix!='' && $date_prefix!='') { $order_template='[prefix][date][number]'; } elseif($prefix!='') { $order_template = '[prefix][number]'; } elseif($date_prefix!= '') { $order_template = '[date][number]'; } elseif($date_prefix == '' && $prefix == '' ) { $order_template = '[number]'; } } if($date_prefix != '') { $date_prefix=self::get_date_from_shortcode($date_prefix, $order_id); } update_option('wt_sequence_last_order_number_format',$order_template); return str_replace(array('[prefix]','[date]','[number]','[suffix]','[date_suffix]'),array($prefix,$date_prefix,$padded_order_number,$suffix,$date_suffix),$order_template); } /** * Set order date prefix to sequential order number. * * @since 1.4.5 * * @param $order_id * @return $date_prefix */ public static function get_sequence_prefix_date($order_id) { $date_prefix = sanitize_text_field(get_option('wt_sequence_order_date_prefix', '')); $date_prefix = apply_filters('wt_order_number_date_prefix', $date_prefix,$order_id); update_option('wt_last_date_prefix',$date_prefix); return $date_prefix; } /** * @since 1.4.5 * @since 1.5.2 Added HPOS Compatibility * Check sequential ordernumber already exists. * @return boolean */ public static function wt_sequential_number_already_exists($order_number) { global $wpdb; $key='_order_number'; if("order_table" === Wt_Advanced_Order_Number_Common::which_table_to_take()){ $table_name = $wpdb->prefix.'wc_orders_meta'; $r = $wpdb->get_col($wpdb->prepare(" SELECT COUNT(om.meta_value) AS num_exists FROM {$table_name} om WHERE om.meta_key = '%s' AND om.meta_value = '%s'", $key,$order_number)); return (isset($r[0]) && $r[0]>0) ? true : false; } else { $post_type = 'shop_order'; $r = $wpdb->get_col($wpdb->prepare(" SELECT COUNT(pm.meta_value) AS num_exists FROM {$wpdb->postmeta} pm LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id WHERE pm.meta_key = '%s' AND p.post_type = '%s' AND pm.meta_value = '%s'", $key, $post_type,$order_number)); return (isset($r[0]) && $r[0]>0) ? true : false; } } /** * Add WooCommerce QuickPay Support * * @since 1.4.6 * @since 1.5.2 Added HPOS Compatibility * * @param string $order_number * @param WC_Order $order * @param bool $recurring * @return $order_number */ public function wt_quickpay_order_number_for_api( $order_number, $order, $recurring ) { $order_id = (WC()->version < '2.7.0') ? $order->id : $order->get_id(); $sequence_number = Wt_Advanced_Order_Number_Common::get_order_meta($order_id, '_order_number'); if( !empty( $sequence_number ) ) { $order_number = $sequence_number; } return $order_number; } /** * Add WooCommerce Amazon Pay Support * * @since 1.4.7 * @since 1.5.2 Added HPOS Compatibility * * Filter the merchant reference ID sent to Amazon * * @hooked woocommerce_amazon_pa_merchant_metadata_reference_id * * @param int $order_id Order ID of the current order * @return string Order number from Sequential Order Number plugin */ function wt_amazon_pay_order_number( $order_id ) { $sequence_number = Wt_Advanced_Order_Number_Common::get_order_meta($order_id, '_order_number'); if( !empty( $sequence_number )) { return $sequence_number; } return $order_id; } /** * @since 1.4.7 * Check whether the order is created before plugin installation. * * @since 1.4.8 [Bug fix]: Uses current_time() instead of time() to avoid timezone conflicts. * * @return boolean */ public static function is_old_order($order_id){ $order = new WC_Order($order_id); $order_date_format='Y-m-d h:i:s'; $date_val = $order->get_date_created(); $order_date = $date_val->format($order_date_format); if(get_option('wt_seq_basic_installation_date') === false) { if(get_option('wt_seq_basic_start_date')) { $install_date = get_option('wt_seq_basic_start_date',current_time( 'timestamp', true )); } else { $install_date = current_time( 'timestamp', true ); } update_option('wt_seq_basic_installation_date',$install_date); } $utc_timestamp = get_option('wt_seq_basic_installation_date'); $utc_timestamp_converted = date( 'Y-m-d h:i:s', $utc_timestamp ); $local_timestamp = get_date_from_gmt( $utc_timestamp_converted, 'Y-m-d h:i:s' ); if($order_date < $local_timestamp) { return true; } return false; } /** * Don't copy over order number meta when creating a parent or child renewal order when WC subscritions version greater than 2.5.0 * * Prevents unnecessary order meta from polluting parent renewal orders, * and set order number for subscription orders * * @since 1.5.0 * @param array $order_meta_query query for pulling the metadata * @return string */ public function wt_seq_remove_renewal_order_meta( $order_meta ) { unset( $order_meta['_order_number'] ); return $order_meta; } private function define_public_hooks() { $plugin_public = new Wt_Advanced_Order_Number_Public($this->get_plugin_name(), $this->get_version()); $this->loader->add_action('wp_enqueue_scripts', $plugin_public, 'enqueue_styles'); $this->loader->add_action('wp_enqueue_scripts', $plugin_public, 'enqueue_scripts'); } public function wt_seq_order_status_changed($order_id, $old_status, $new_status, $order) { $order = wc_get_order( $order_id ); $order_status = $order ? $order->get_status() : ''; $is_draft = self::wt_seq_is_draft_order( $order_status ); $is_draft = apply_filters('wt_sequential_is_draft_order',$is_draft,$order); if( false === $is_draft ) { $this->set_sequential_number( $order_id ); } } public static function wt_seq_is_draft_order($order_status) { $is_draft = false; if( 'checkout-draft' === $order_status) { $is_draft = true; } elseif ( 'draft' === $order_status ) { $is_draft = true; } else { $is_draft = false; } return $is_draft; } /** * Added new filter 'wt_sequential_change_last_order_number'. * @since 1.4.4 * Added new filter 'wt_sequential_alter_order_number'. * @since 1.5.2 * Added additional loop to avoid order number duplication in heavy traffic site * @since 1.5.0 * Added conditionalx check to avoid sequential order number generation for subscription orders. * @since 1.5.0 * @since 1.5.2 Added HPOS Compatibility * @since 1.5.7 Fixed order number duplication issue by using file lock. */ public function set_sequential_number($post_id, $post = array() ) { global $wpdb; if ( is_array( $post ) || is_null( $post ) || ( is_object( $post ) && (( isset( $post->post_type )) || isset( $post->type) ) && (( ( 'shop_order' === $post->post_type ) ) || ( 'shop_order' === $post->type ) ) ) ){ $order = $post instanceof \WC_Order ? $post : ($post_id instanceof \WC_Order ? $post_id :wc_get_order( $post_id )); // checks whether the order is subscription order if ( is_object( $order ) && is_a( $order, 'WC_Subscription' ) ) { $is_subscription = true; } elseif ( is_numeric( $order ) && 'shop_subscription' === get_post_type( $order ) ) { $is_subscription = true; } elseif ( is_object( $order ) && is_a( $order, 'HF_Subscription' ) ) { $is_subscription = true; } elseif ( is_numeric( $order ) && 'hf_shop_subscription' == get_post_type( $order ) ) { $is_subscription = true; } else { $is_subscription = false; } $is_subscription=apply_filters('wt_sequential_is_subscription_order',$is_subscription,$order); // If order is subscription skip sequential order number generation. if( true === $is_subscription) { return ; } $order_id = (WC()->version < '2.7.0') ? $order->id : $order->get_id(); $order_number = Wt_Advanced_Order_Number_Common::get_order_meta($order_id, '_order_number'); $increment_counter = !empty((int) get_option('wt_sequence_increment_counter', 1)) ? (int) get_option('wt_sequence_increment_counter', 1) : 1; $is_old_order = self::is_old_order($order_id); $is_old_order = apply_filters('wt_sequential_is_old_order',$is_old_order,$order_id); $order_status = $order ? $order->get_status() : ''; $is_draft = self::wt_seq_is_draft_order( $order_status ); $is_draft = apply_filters('wt_sequential_is_draft_order',$is_draft,$order); // If order is a draft order skip sequential order number generation. if( true === $is_draft ) { return ; } $lockFolderPath = self::get_temp_dir(); if ( !is_dir( $lockFolderPath ) ) { wp_mkdir_p( $lockFolderPath ); } $lockFilePath = $lockFolderPath.'/wt_sequential_order_number.lock'; $file = fopen($lockFilePath,"w"); if (flock($file,LOCK_EX)) { if (empty($order_number) && false === $is_old_order) { $prefix = self::get_sequence_prefix($order_id); $last_order_num = get_option('wt_last_order_number', 0); $query_array = [ "INSERT INTO {$wpdb->postmeta} (post_id, meta_key, meta_value) VALUES (%d,%s,%s)", ]; if(Wt_Advanced_Order_Number_Common::is_wc_hpos_enabled()) { $table_name = $wpdb->prefix.'wc_orders_meta'; $query_array[] = "INSERT INTO {$table_name} (order_id, meta_key, meta_value) VALUES (%d,%s,%s)"; } $wt_last_order_number = get_option('wt_last_order_number', $last_order_num); $wt_last_order_number = apply_filters('wt_sequential_change_last_order_number',$wt_last_order_number,$order_id); $next_insert_id = $wt_last_order_number + 1; update_option('wt_last_order_number', $next_insert_id,'no'); $next_insert_id_padding=self::add_order_no_padding($next_insert_id); $next_order_number = self::add_prefix_suffix($next_insert_id_padding,$order_id); while(self::wt_sequential_number_already_exists($next_order_number)) { $next_insert_id = $next_insert_id + $increment_counter; update_option('wt_last_order_number', $next_insert_id,'no'); $next_insert_id_padding=self::add_order_no_padding($next_insert_id); $next_order_number = self::add_prefix_suffix($next_insert_id_padding,$order_id); } foreach ($query_array as $sql) { $query = $wpdb->prepare($sql, $post_id, '_order_number', $next_order_number); $res = $wpdb->query($query); } $order->save(); } flock($file,LOCK_UN); } else { sleep(2); self::set_sequential_number( $post_id, $post ); } } } public function get_plugin_name() { return $this->plugin_name; } public function get_plugin_base_name() { return $this->plugin_base_name; } public function get_loader() { return $this->loader; } public function get_version() { return $this->version; } public function display_sequence_number($order_number, $order) { $order_id = (WC()->version < '2.7.0') ? $order->id : $order->get_id(); $sequential_order_number = Wt_Advanced_Order_Number_Common::get_order_meta($order_id, '_order_number'); $sequential_order_number = apply_filters('wt_alter_sequence_number',$sequential_order_number,$order_id); return ($sequential_order_number) ? $sequential_order_number : $order_number; } /** * @since 1.5.8 * Get upload Path * @return string */ public static function get_temp_dir() { $uploads_dir = wp_upload_dir(); $folder_name = WT_SEQUENCIAL_ORDNUMBER_NAME . '-lock'; // Construct the full path for the new folder $folder_path = $uploads_dir['basedir'] . '/' . $folder_name; // Check if the folder already exists if (!file_exists($folder_path)) { // Create the folder and its parent directories wp_mkdir_p($folder_path); } return $folder_path; } public function run() { $this->loader->run(); } }