<?php

require_once dirname(__FILE__) . '/XmlImportWooCommerce.php';

class XmlImportWooCommerceShopOrder extends XmlImportWooCommerce{
	
	public $payment_gateways;
	public $shipping_methods;
    public $shipping_zone_methods = array();
    public $prices_include_tax = 0;
	public $tax_rates = array();
	public $order_data = array();

	public function __construct( $options ){		

		global $wpdb;

		$this->wpdb   = $wpdb;
		
		$this->import = $options['import'];
		$this->count  = $options['count'];
		$this->xml    = $options['xml'];
		$this->logger = $options['logger'];
		$this->chunk  = $options['chunk'];
		$this->xpath  = $options['xpath_prefix'];

        $this->prices_include_tax = ('yes' === get_option( 'woocommerce_prices_include_tax', 'no' ));

		$this->payment_gateways = WC_Payment_Gateways::instance()->get_available_payment_gateways();
		$this->shipping_methods = WC()->shipping->get_shipping_methods();

        if (class_exists('WC_Shipping_Zones')){
            $zones = WC_Shipping_Zones::get_zones();
            if (!empty($zones)){
                foreach ($zones as $zone_id => $zone){
                    if (!empty($zone['shipping_methods'])){
                        foreach ($zone['shipping_methods'] as $method){
                            $this->shipping_zone_methods[] = $method;
                        }
                    }
                }
            }
            else
            {
                $zone = new WC_Shipping_Zone( 0 );
                $this->shipping_zone_methods = $zone->get_shipping_methods();
            }
        }

		$tax_classes = array_filter( array_map( 'trim', explode( "\n", get_option( 'woocommerce_tax_classes' ) ) ) );
				
		if ( $tax_classes )
		{
			// Add Standard tax class
			if ( ! in_array( '', $tax_classes ) ) $tax_classes[] = '';
			
			foreach ( $tax_classes as $class )
			{
				foreach ( WC_Tax::get_rates_for_tax_class(sanitize_title( $class )) as $rate_key => $rate)
				{
					$this->tax_rates[$rate->tax_rate_id] = $rate;
				}				
			}
		}

		add_filter('wp_all_import_is_post_to_skip', array( &$this, 'wp_all_import_is_post_to_skip'), 10, 5);
		add_filter('wp_all_import_combine_article_data', array( &$this, 'wp_all_import_combine_article_data'), 10, 4);
		
	}

	protected function parse_item_row( $row, $cxpath, $count )
	{
		$tmp_files  = array();

		$row_data = array();

		$records = array();

		foreach ($row as $opt => $value) 
		{
			switch ($opt) 
			{			
				case 'class_xpath':
				case 'tax_code_xpath':
				case 'visibility_xpath':
					// skipp this field(s)
					break;

				case 'tax_rates':

					foreach ($value as $i => $tax_rate_row) 
					{
						$tax_rate_data = array();

						foreach ($tax_rate_row as $tax_rate_row_opt => $tax_rate_row_value) 
						{
							if ( ! empty($tax_rate_row_value) )
							{
								$tax_rate_data[$tax_rate_row_opt] = XmlImportParser::factory($this->xml, $cxpath, $tax_rate_row_value, $file)->parse($records); $tmp_files[] = $file;
							}	
							else
							{
								$count and $tax_rate_data[$tax_rate_row_opt] = array_fill(0, $count, $tax_rate_row_value);
							}										
						}
						$row_data[$opt][] = $tax_rate_data;
					}									

					break;

				case 'meta_name':
				case 'meta_value':

					foreach ($value as $meta) 
					{
						if ( ! empty($meta) )
						{
							$row_data[$opt][] = XmlImportParser::factory($this->xml, $cxpath, $meta, $file)->parse($records); $tmp_files[] = $file;
						}										
						else
						{
							$row_data[$opt][] = array_fill(0, $count, $meta);
						}
					}																	

					break;

				case 'class':
				case 'tax_code':
				case 'visibility':
					
					if ( $value == 'xpath' and $row[$opt . '_xpath'] != '' )
					{
						$row_data[$opt] = XmlImportParser::factory($this->xml, $cxpath, $row[$opt . '_xpath'], $file)->parse($records); $tmp_files[] = $file;
					}
					else
					{
						$count and $row_data[$opt] = array_fill(0, $count, $value);
					}

					break;

				case 'date':

					if ( ! empty($value))
					{
						$dates = XmlImportParser::factory($this->xml, $cxpath, $value, $file)->parse($records); $tmp_files[] = $file;
					
						$warned = array(); // used to prevent the same notice displaying several times
						foreach ($dates as $i => $d) {
							if ($d == 'now') $d = current_time('mysql'); // Replace 'now' with the WordPress local time to account for timezone offsets (WordPress references its local time during publishing rather than the server’s time so it should use that)
							$time = strtotime($d);
							if (FALSE === $time) {							
								$time = time();
							}
							$row_data[$opt][$i] = date('Y-m-d H:i:s', $time);
						}
					}	
					else
					{
						$count and $row_data[$opt] = array_fill(0, $count, date('Y-m-d H:i:s'));
					}	

					break;
				
				default:
					
					if ( ! empty($value))
					{
						$row_data[$opt] = XmlImportParser::factory($this->xml, $cxpath, $value, $file)->parse($records); $tmp_files[] = $file;	
					}
					else
					{
						$count and $row_data[$opt] = array_fill(0, $count, $value);
					}									

					break;
			}	
		}
		
		foreach ($tmp_files as $file) { // remove all temporary files created
			unlink($file);
		}

		return $row_data;
	}

	public function parse()
	{		
		$cxpath = $this->xpath . $this->import->xpath;

		$this->data = array();
		$records    = array();
		$tmp_files  = array();

		$this->chunk == 1 and $this->logger and call_user_func($this->logger, __('Composing shop order data...', 'wpai_woocommerce_addon_plugin'));

		$default = PMWI_Plugin::get_default_import_options();		

		foreach ($default['pmwi_order'] as $option => $default_value) 
		{
			if ( in_array($option, array('status_xpath', 'payment_method_xpath', 'order_note_visibility_xpath', 'billing_source', 
				'billing_source_match_by', 'shipping_source', 'products_source', 'order_taxes_logic', 'order_refund_issued_source', 'order_refund_issued_match_by', 
				'order_total_logic', 'order_note_separate_logic', 'order_note_separator', 'is_guest_matching', 'copy_from_billing')) or strpos($option, 'is_update_') !== false or strpos($option, '_repeater_mode') !== false) continue;

			switch ($option) 
			{
				case 'date':
				case 'order_refund_date':			
					
					if ( ! empty($this->import->options['pmwi_order'][$option]))
					{
						$dates = XmlImportParser::factory($this->xml, $cxpath, $this->import->options['pmwi_order'][$option], $file)->parse($records); $tmp_files[] = $file;
					
						$warned = array(); // used to prevent the same notice displaying several times
						foreach ($dates as $i => $d) {
							if ($d == 'now') $d = current_time('mysql'); // Replace 'now' with the WordPress local time to account for timezone offsets (WordPress references its local time during publishing rather than the server’s time so it should use that)
							$time = strtotime($d);
							if (FALSE === $time) {							
								$time = time();
							}
							$this->data['pmwi_order'][$option][$i] = date('Y-m-d H:i:s', $time);
						}
					}	
					else
					{
						$this->count and $this->data['pmwi_order'][$option] = array_fill(0, $this->count, date('Y-m-d H:i:s'));
					}									

					break;
				
				case 'status':
				case 'payment_method':
				case 'order_note_visibility':

					if ( $this->import->options['pmwi_order'][$option] == 'xpath' && $this->import->options['pmwi_order'][$option . '_xpath'] != "" )
					{
						$this->data['pmwi_order'][$option] = XmlImportParser::factory($this->xml, $cxpath, $this->import->options['pmwi_order'][$option . '_xpath'], $file)->parse($records); $tmp_files[] = $file;
					}
					else
					{
						$this->count and $this->data['pmwi_order'][$option] = array_fill(0, $this->count, $this->import->options['pmwi_order'][$option]);
					}

					break;

				case 'products':
				case 'manual_products':

					$this->data['pmwi_order'][$option] = array();
					
					switch ($this->import->options['pmwi_order']['products_repeater_mode']) 
					{
						case 'xml':
							
							foreach ($this->import->options['pmwi_order'][$option] as $key => $row) 
							{
								for ($k = 0; $k < $this->count; $k++) 
								{
									$base_xpath = '[' . ( $k + 1 ) . ']/'.  ltrim(trim($this->import->options['pmwi_order']['products_repeater_mode_foreach'],'{}!'), '/');

									$rows = XmlImportParser::factory($this->xml, $cxpath . $base_xpath, "{.}", $file)->parse(); $tmp_files[] = $file;						

									$row_data = $this->parse_item_row( $row, $cxpath . $base_xpath, count($rows) );												

									$products = array();

									if ( ! empty($row_data))
									{
										for ($j = 0; $j < count($rows); $j++) 
										{ 
											$products[] = array(
												'sku' => $row_data['sku'][$j],
												'qty' => $row_data['qty'][$j],
												'price_per_unit' => isset($row_data['price_per_unit'][$j]) ? $row_data['price_per_unit'][$j] : 0,
												'tax_rates' => array()
											);											

											if ( ! empty($row_data['tax_rates']))
											{
												foreach ($row_data['tax_rates'] as $tax_rate) 
												{
													$products[$j]['tax_rates'][] = array(
														'code' => $tax_rate['code'][$j],
														'calculate_logic' => $tax_rate['calculate_logic'][$j], 
														'percentage_value' => $tax_rate['percentage_value'][$j], 
														'amount_per_unit' => $tax_rate['amount_per_unit'][$j] 
													);
												}
											}											

											if ( ! empty($row_data['meta_name']))
											{
												foreach ($row_data['meta_name'] as $meta_name) 
												{
													$products[$j]['meta_name'][] = $meta_name[$j];
												}
											}											

											if ( ! empty($row_data['meta_value']))
											{
												foreach ($row_data['meta_value'] as $meta_value) 
												{
													$products[$j]['meta_value'][] = $meta_value[$j];
												}
											}											
										}	
									}																
									
									$this->data['pmwi_order'][$option][] = $products;			
								}

								break;
							}

							break;

						case 'csv':								

							foreach ($this->import->options['pmwi_order'][$option] as $key => $row) 
							{								
								if (empty($this->import->options['pmwi_order']['products_repeater_mode_separator'])) break;								

								$row_data = $this->parse_item_row( $row, $cxpath, $this->count );																													
								for ($k = 0; $k < $this->count; $k++) 
								{ 
									$products = array();

									$skus = explode($this->import->options['pmwi_order']['products_repeater_mode_separator'], $row_data['sku'][$k]);
									$qtys = explode($this->import->options['pmwi_order']['products_repeater_mode_separator'], $row_data['qty'][$k]);
									$prices = isset($row_data['price_per_unit'][$k]) ? explode($this->import->options['pmwi_order']['products_repeater_mode_separator'], $row_data['price_per_unit'][$k]) : array();

									if ( ! empty($skus))
									{
										for ($j = 0; $j < count($skus); $j++) 
										{ 				
											$products[] = array(
												'sku' => $skus[$j],
												'qty' => $qtys[$j],
												'price_per_unit' => isset($prices[$j]) ? $prices[$j] : 0,
												'tax_rates' => array()
											);																	

											if ( ! empty($row_data['tax_rates']))
											{
												foreach ($row_data['tax_rates'] as $tax_rate) 
												{
													$products[$j]['tax_rates'][] = array(
														'code' => $tax_rate['code'][$k],
														'calculate_logic' => $tax_rate['calculate_logic'][$k], 
														'percentage_value' => $tax_rate['percentage_value'][$k], 
														'amount_per_unit' => $tax_rate['amount_per_unit'][$k] 
													);																								
												}	
											}
											
											if ( ! empty($row_data['meta_name']))
											{
												foreach ($row_data['meta_name'] as $meta_name) 
												{
													$products[$j]['meta_name'][] = $meta_name[$j];
												}
											}
											
											if ( ! empty($row_data['meta_value']))
											{
												foreach ($row_data['meta_value'] as $meta_value) 
												{
													$products[$j]['meta_value'][] = $meta_value[$j];
												}
											}											
										}																					
									}	
									$this->data['pmwi_order'][$option][] = $products;									
								}									
																																							
								break;
							}

							break;
						
						default:
							
							$row_data = array();

							foreach ($this->import->options['pmwi_order'][$option] as $key => $row) 
							{																						
								$row_data[] = $this->parse_item_row( $row, $cxpath, $this->count );	
							}							

							for ($j = 0; $j < $this->count; $j++) 
							{
								$products = array(); 
								
								foreach ($row_data as $k => $product) 
								{
									$products[] = array(
										'sku' => $product['sku'][$j],
										'qty' => $product['qty'][$j],
										'price_per_unit' => isset($product['price_per_unit'][$j]) ? $product['price_per_unit'][$j] : 0,
										'tax_rates' => array()
									);

									if ( ! empty($product['tax_rates']))
									{
										foreach ($product['tax_rates'] as $tax_rate) 
										{
											$products[$k]['tax_rates'][] = array(
												'code' => $tax_rate['code'][$j],
												'calculate_logic' => $tax_rate['calculate_logic'][$j], 
												'percentage_value' => $tax_rate['percentage_value'][$j], 
												'amount_per_unit' => $tax_rate['amount_per_unit'][$j] 
											);
										}	
									}
									
									if ( ! empty($product['meta_name']))
									{
										foreach ($product['meta_name'] as $meta_name) 
										{
											$products[$k]['meta_name'][] = $meta_name[$j];
										}
									}									

									if ( ! empty($product['meta_value']))
									{
										foreach ($product['meta_value'] as $meta_value) 
										{
											$products[$k]['meta_value'][] = $meta_value[$j];
										}
									}									
								}
								$this->data['pmwi_order'][$option][] = $products;
							}														

							break;
					}											

					break;				
					
				case 'fees':
				case 'coupons':
				case 'shipping':
				case 'taxes':
				case 'notes':

					$this->data['pmwi_order'][$option] = array();

					switch ($this->import->options['pmwi_order'][$option . '_repeater_mode']) 
					{
						case 'xml':

							foreach ($this->import->options['pmwi_order'][$option] as $key => $row) 
							{
								for ($k = 0; $k < $this->count; $k++) 
								{
									$base_xpath = '[' . ( $k + 1 ) . ']/'.  ltrim(trim($this->import->options['pmwi_order'][$option . '_repeater_mode_foreach'],'{}!'), '/');

									$rows = XmlImportParser::factory($this->xml, $cxpath . $base_xpath, "{.}", $file)->parse(); $tmp_files[] = $file;

									$row_data = $this->parse_item_row( $row, $cxpath . $base_xpath, count($rows) );				

									$items = array();

									if ( ! empty($row_data))
									{
										for ($j = 0; $j < count($rows); $j++) 
										{
											foreach ($row_data as $itemkey => $values) 
											{
												$items[$j][$itemkey] = $values[$j];
											}										
										}																																															
									}									
																																
									$this->data['pmwi_order'][$option][] = $items;			
								}

								break;
							}

							break;

						case 'csv':

							$separator = $this->import->options['pmwi_order'][ $option . '_repeater_mode_separator'];

							foreach ($this->import->options['pmwi_order'][$option] as $key => $row) 
							{								
								if (empty($separator)) break;								

								$row_data = $this->parse_item_row( $row, $cxpath, $this->count );																															
								
								for ($k = 0; $k < $this->count; $k++) 
								{ 
									$items = array();									

									$maxCountRows = 0;

									foreach ($row_data as $itemkey => $values) 
									{
										$itemIndex = 0;

										$rows = explode($separator, $values[$k]);

										if ( ! empty($rows))
										{
											if (count($rows) > $maxCountRows) $maxCountRows = count($rows);

											if (count($rows) == 1)
											{
												for ($j = 0; $j < $maxCountRows; $j++) 
												{ 
													$items[$itemIndex][$itemkey] = trim($rows[0]);
													$itemIndex++;
												}
											}
											else
											{
												foreach ($rows as $val) 
												{
													$items[$itemIndex][$itemkey] = trim($val);
													$itemIndex++;
												}
											}											
										}
										// else 
										// {
										// 	for ($j = 0; $j < $maxCountRows; $j++) 
										// 	{ 
										// 		$items[$itemIndex][$itemkey] = '';
										// 		$itemIndex++;
										// 	}											
										// }
									}
									
									$this->data['pmwi_order'][$option][] = $items;									
								}									
																																							
								break;
							}

							break;

						default:

							$row_data = array();

							foreach ($this->import->options['pmwi_order'][$option] as $key => $row) 
							{																						
								$row_data[] = $this->parse_item_row( $row, $cxpath, $this->count );	
							}							

							for ($j = 0; $j < $this->count; $j++) 
							{
								$items = array(); 
								
								$itemIndex = 0;

								foreach ($row_data as $k => $item) 
								{									
									foreach ($item as $itemkey => $values) 
									{
										$items[$itemIndex][$itemkey] = $values[$j];
									}									
									$itemIndex++;
								}

								$this->data['pmwi_order'][$option][] = $items;
							}

							break;
					}					

					break;				

				default:

					if ( ! empty($this->import->options['pmwi_order'][$option]) )
					{
						$this->data['pmwi_order'][$option] = XmlImportParser::factory($this->xml, $cxpath, $this->import->options['pmwi_order'][$option], $file)->parse($records); $tmp_files[] = $file;
					}					
					else
					{
						$this->count and $this->data['pmwi_order'][$option] = array_fill(0, $this->count, $default_value);
					}
					
					break;
			}
		}

		foreach ($tmp_files as $file) { // remove all temporary files created
			unlink($file);
		}	
		
		// file_put_contents(PMWI_ROOT_DIR . '/order_parse.txt', json_encode($this->data));

		return $this->data;	
	}		

	public function import( $importData )
	{	
		$order_id = $importData['pid'];
		$index    = $importData['i'];
		$this->articleData = $importData['articleData'];

		/*
		*
		* Import Order details - status, date
		*
		*/

		$order_status = trim($this->data['pmwi_order']['status'][$index]);

		// detect order status by slug or title
		$all_order_statuses = wc_get_order_statuses();		
		if ( empty($all_order_statuses[$order_status])){
			$status_founded = false;
			foreach ($all_order_statuses as $key => $value) {
				if (strtolower($value) == strtolower($order_status)){
					$order_status = $key;
					$status_founded = true;
					break;
				}
			}
			if ( ! $status_founded ){
				$order_status = 'wc-pending';
			}
		}
		
		$this->order_data = array(
			'ID' => $order_id,
			'post_title'    => 'Order &ndash; ' . date_i18n( 'F j, Y @ h:i A', strtotime($this->data['pmwi_order']['date'][$index]) ),
			'post_content'  => '',
			'post_date' 	=> $this->data['pmwi_order']['date'][$index],
			'post_date_gmt' => get_gmt_from_date($this->data['pmwi_order']['date'][$index]),
			'post_status'   => $order_status,
			'ping_status'   => 'closed',
			'post_password' => uniqid( 'order_' ),
			'post_excerpt'  => $this->data['pmwi_order']['customer_provided_note'][$index], 
		);

		$old_status = str_replace("wc-", "", $this->articleData['post_status']);
		$new_status = str_replace("wc-", "", $this->order_data['post_status']);			

		if ( ! empty($this->articleData['ID']))
		{
			if ( $this->import->options['update_all_data'] == 'no' ){
				if ( ! $this->import->options['is_update_dates']) { // preserve date of already existing article when duplicate is found
					$this->order_data['post_title'] = 'Order &ndash; ' . date_i18n( 'F j, Y @ h:i A', strtotime($this->articleData['post_date']) );
					$this->order_data['post_date'] = $this->articleData['post_date'];
					$this->order_data['post_date_gmt'] = $this->articleData['post_date_gmt'];					
				}
				if ( ! $this->import->options['is_update_status']) { // preserve status and trashed flag
					$this->order_data['post_status'] = $this->articleData['post_status'];						
				}
				if ( ! $this->import->options['is_update_excerpt']){ // preserve customer's note
					$this->order_data['post_excerpt'] = $this->articleData['post_excerpt'];
				}
			}						
		}

		$order_id = wp_update_post( $this->order_data );

		if ( is_wp_error( $order_id ) ) {
			return $order_id;
		}

		/** @var WC_Order $order */
		$order = wc_get_order($order_id);

		/*
		*
		* Import Order billing & shipping details
		*
		*/		

		$billing_fields = array('billing_first_name', 'billing_last_name', 'billing_company', 'billing_address_1', 'billing_address_2', 'billing_city', 'billing_postcode', 'billing_country', 'billing_state', 'billing_phone', 'billing_email');

		$billing_data = array();

		if ( empty($this->articleData['ID']) or $this->import->options['update_all_data'] == 'yes' or $this->import->options['is_update_billing_details'] )
		{
			$this->logger and call_user_func($this->logger, sprintf(__('- Importing billing & shipping information for Order ID `%s`.', 'wpai_woocommerce_addon_plugin'), $order_id));			

			// [ Importing billing information ]		
			switch ($this->import->options['pmwi_order']['billing_source']) 
			{
				// Load details from existing customer
				case 'existing':							
					
					$customer = $this->get_existing_customer('billing_source', $index);

					if ( $customer )
					{					
						$this->logger and call_user_func($this->logger, sprintf(__('- %s Existing customer with ID `%s` founded for Order `%s`.', 'wpai_woocommerce_addon_plugin'), $customer->ID, $order_id));
						
						foreach ($billing_fields as $billing_field) 
						{
							$billing_data[$billing_field] = get_user_meta( $customer->ID, $billing_field, true);
							update_post_meta( $order_id, '_' . $billing_field, $billing_data[$billing_field]);
							$this->logger and call_user_func($this->logger, sprintf(__('- Billing field `%s` has been updated with value `%s` for order `%s` ...', 'wp_all_import_plugin'), $billing_field, $billing_data[$billing_field], $order_id));						
						}
						update_post_meta( $order_id, '_customer_user', $customer->ID);
					}
					else
					{
                        if ($this->import->options['pmwi_order']['is_guest_matching']){
                            foreach ($billing_fields as $billing_field) {
                                $billing_data[$billing_field] = $this->data['pmwi_order']['guest_' . $billing_field][$index];
                                update_post_meta( $order_id, '_' . $billing_field, $billing_data[$billing_field]);
                                $this->logger and call_user_func($this->logger, sprintf(__('- Billing field `%s` has been updated with value `%s` for order `%s` ...', 'wp_all_import_plugin'), $billing_field, $this->data['pmwi_order']['guest_' . $billing_field][$index], $order_id));
                            }

                            update_post_meta( $order_id, '_customer_user', '0');
                        }
                        else{
                            $this->logger and call_user_func($this->logger, sprintf(__('<b>WARNING</b>: Existing customer not found for Order `%s`.', 'wpai_woocommerce_addon_plugin'), $this->order_data['post_title']));
                        }
					}

					break;
				
				// Use guest customer
				default:
					
					foreach ($billing_fields as $billing_field) {			
						$billing_data[$billing_field] = $this->data['pmwi_order'][$billing_field][$index];		
						update_post_meta( $order_id, '_' . $billing_field, $billing_data[$billing_field]);
						$this->logger and call_user_func($this->logger, sprintf(__('- Billing field `%s` has been updated with value `%s` for order `%s` ...', 'wp_all_import_plugin'), $billing_field, $this->data['pmwi_order'][$billing_field][$index], $order_id));						
					}
					
					update_post_meta( $order_id, '_customer_user', '0');

					break;
			}
		}		
		// [\Importing billing information ]			

		// [ Importing shipping information ]	
		if ( empty($this->articleData['ID']) or $this->import->options['update_all_data'] == 'yes' or $this->import->options['is_update_shipping_details'] )
		{	
			switch ($this->import->options['pmwi_order']['shipping_source']) 
			{
				// Copy from billing
				case 'copy':

					$this->logger and call_user_func($this->logger, sprintf(__('- Copying shipping from billing information...', 'wpai_woocommerce_addon_plugin')));

					if ( ! empty( $billing_data )){				
						foreach ($billing_data as $key => $value) {
							$shipping_field = str_replace('billing', 'shipping', $key);
							update_post_meta( $order_id, '_' . $shipping_field, $value );
							$this->logger and call_user_func($this->logger, sprintf(__('- Shipping field `%s` has been updated with value `%s` for order `%s` ...', 'wp_all_import_plugin'), $shipping_field, $value, $order_id));
						}
					}

					break;

				// Import shipping address
				default:
					foreach ($billing_fields as $billing_field) {		
						$shipping_field = str_replace('billing', 'shipping', $billing_field);
                        $shipping_value = '';
                        if ( ! empty($this->data['pmwi_order'][$shipping_field][$index]) ){
                            $shipping_value = $this->data['pmwi_order'][$shipping_field][$index];
                        }
                        elseif ($this->import->options['pmwi_order']['copy_from_billing']) {
                            $shipping_value = empty($billing_data[$billing_field]) ? '' : $billing_data[$billing_field];
                        }
                        update_post_meta( $order_id, '_' . $shipping_field, $shipping_value);
                        $this->logger and call_user_func($this->logger, sprintf(__('- Shipping field `%s` has been updated with value `%s` for order `%s` ...', 'wp_all_import_plugin'), $shipping_field, $shipping_value, $order_id));
					}

					break;
			}
		}
		// [\Importing shipping information ]	

		// send notifications on order status changed
		if ( ! empty($this->articleData['ID']) and $new_status !== $old_status && empty($this->import->options['do_not_send_order_notifications'])) 
		{				
			do_action( 'woocommerce_order_status_' . $old_status . '_to_' . $new_status, $order_id );			
			do_action( 'woocommerce_order_status_changed', $order_id, $old_status, $new_status );

			if ( $new_status == 'completed' )
			{
				do_action( 'woocommerce_order_status_completed', $this->articleData['ID']);
			}

		}	

		// send new order notification
		if ( empty($this->articleData['ID']) && empty($this->import->options['do_not_send_order_notifications']) ) 
		{			
			do_action( 'woocommerce_order_status_' . $new_status, $order_id );			
			
			do_action( 'woocommerce_order_status_pending_to_' . $new_status, $order_id );			

			do_action( 'woocommerce_before_resend_order_emails', $order );			

			// Load mailer
			$mailer = WC()->mailer();

			$email_to_send = 'new_order';

			$mails = $mailer->get_emails();

			if ( ! empty( $mails ) ) {
				foreach ( $mails as $mail ) {
					if ( $mail->id == $email_to_send ) {
						$mail->trigger( $order_id );						
						$this->logger and call_user_func($this->logger, sprintf(__('- %s email notification has beed sent. ...', 'wp_all_import_plugin'), $mail->title));
					}
				}
			}

			do_action( 'woocommerce_after_resend_order_email', $order, $email_to_send );
		}


		// [ Importing payment information ]		
		if ( empty($this->articleData['ID']) or $this->import->options['update_all_data'] == 'yes' or $this->import->options['is_update_payment'] )
		{
			$payment_method = $this->data['pmwi_order']['payment_method'][$index];

			if ( ! empty($payment_method) )
			{
				if ( ! empty($this->payment_gateways[$payment_method]) )
				{
					update_post_meta( $order_id, '_payment_method', $payment_method );
					update_post_meta( $order_id, '_payment_method_title', $this->payment_gateways[$payment_method]->title );
				}	
				else
				{
					$method = false;
					if ( ! empty($this->payment_gateways))
					{
						foreach ($this->payment_gateways as $payment_gateway_slug => $payment_gateway) 
						{
							if ( strtolower($payment_gateway->method_title) == strtolower(trim($payment_method)) )
							{
								$method = $payment_method;
								break;
							}
						}						
					}
					
					if ($method)
					{
						update_post_meta( $order_id, '_payment_method', $payment_method );
						update_post_meta( $order_id, '_payment_method_title', $method->method_title );	
					}
				}			
			}
			else
			{
				update_post_meta( $order_id, '_payment_method', 'N/A' );
			}

			update_post_meta( $order_id, '_transaction_id', $this->data['pmwi_order']['transaction_id'][$index] );
		}		
		// [\Importing payment information ]		

		/*
		*
		* Import Order Items
		*
		*/

		// Importing product items
		if ( empty($this->articleData['ID']) or $this->import->options['update_all_data'] == 'yes' or $this->import->options['is_update_products'] )
		{			
			if ( ! empty($this->articleData['ID']) and ( $this->import->options['update_all_data'] == 'yes' or $this->import->options['is_update_products'] and $this->import->options['update_products_logic'] == 'full_update' ) )
			{

				$previously_updated_order = get_option('wp_all_import_previously_updated_order_' . $this->import->id, false);

				if ( empty($previously_updated_order) or $previously_updated_order != $this->articleData['ID'] ){

					$order->remove_order_items( 'line_item' );

					global $wpdb;				

					$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}pmxi_posts WHERE import_id = %d AND post_id = %d AND unique_key LIKE %s;", $this->import->id, $order_id, '%' . $wpdb->esc_like('line-item') . '%' ) );

				}
				
			}

			$this->_import_line_items( $order, $order_id, $index );				
		}

		// Importing fee items
		if ( empty($this->articleData['ID']) or $this->import->options['update_all_data'] == 'yes' or $this->import->options['is_update_fees'] )
		{
			$this->_import_fee_items( $order, $order_id, $index );
		}

		// Importing coupons items
		if ( empty($this->articleData['ID']) or $this->import->options['update_all_data'] == 'yes' or $this->import->options['is_update_coupons'] )
		{
			$this->_import_coupons_items( $order, $order_id, $index );
		}

		// Importing shipping items
		if ( empty($this->articleData['ID']) or $this->import->options['update_all_data'] == 'yes' or $this->import->options['is_update_shipping'] )
		{
			$this->_import_shipping_items( $order, $order_id, $index );
		}

		// Importing taxes items
		if ( empty($this->articleData['ID']) or $this->import->options['update_all_data'] == 'yes' or $this->import->options['is_update_taxes'] )
		{
			$this->_import_taxes_items( $order, $order_id, $index );
		}

        /*
        *
        * Import Order Total
        *
        */

        if ( empty($this->articleData['ID']) or $this->import->options['update_all_data'] == 'yes' or $this->import->options['is_update_total'] )
        {
            if ( $this->import->options['pmwi_order']['order_total_logic'] !== 'auto' )
            {
                if ( version_compare(WOOCOMMERCE_VERSION, '3.0') < 0 ) {
                    $order->set_total($this->data['pmwi_order']['order_total_xpath'][$index], 'total');
                }
                else{
                    update_post_meta( $order_id, '_order_total', wc_format_decimal( $this->data['pmwi_order']['order_total_xpath'][$index], wc_get_price_decimals() ) );
                }
            }
            else
            {
                $order->calculate_totals();
            }
        }

        /*
        *
        * Import Order Refunds
        *
        */
		if ( empty($this->articleData['ID']) or $this->import->options['update_all_data'] == 'yes' or $this->import->options['is_update_refunds'] )
		{
			if ( ! empty($this->data['pmwi_order']['order_refund_amount'][$index]) )
			{
				$refund_item = new PMXI_Post_Record();
				$refund_item->getBy(array(
					'import_id'  => $this->import->id,
					'post_id'    => $order_id,
					'unique_key' => 'refund-item-' . $order_id
				));

				$args = array(
					'amount'     => $this->data['pmwi_order']['order_refund_amount'][$index],
					'reason'     => $this->data['pmwi_order']['order_refund_reason'][$index],
					'order_id'   => $order_id,
					'refund_id'  => 0,
					'line_items' => array(),
					'date'       => $this->data['pmwi_order']['order_refund_date'][$index]
				);
				
				if ( ! $refund_item->isEmpty() ) $args['refund_id'] = str_replace('refund-item-', '', $refund_item->product_key);
				
				$refund = wc_create_refund( $args );

				if ( $refund instanceOf WC_Order_Refund )
				{
					$refund_item->set(array(
						'import_id'   => $this->import->id,
						'post_id'     => $order_id,
						'unique_key'  => 'refund-item-' . $order_id,
						'product_key' => 'refund-item-' . $refund->get_id(),
						'iteration'   => $this->import->iteration
					))->save();

					switch ($this->import->options['pmwi_order']['order_refund_issued_source']) 
					{
						case 'existing':
							
							$customer = $this->get_existing_customer('order_refund_issued', $index);					

							if ($customer)
							{
								wp_update_post(array(
									'ID' => $refund->get_id(),
									'post_author' => $customer->ID
								));
							}

							break;
						
						default:
							
							wp_update_post(array(
								'ID' => $refund->get_id(),
								'post_author' => 0
							));

							break;
					}
				}			
			}
		}

		/*
		*
		* Import Order Notes
		*
		*/

		if ( empty($this->articleData['ID']) or $this->import->options['update_all_data'] == 'yes' or $this->import->options['is_update_notes'] )
		{			
			$this->_import_order_notes( $order, $order_id, $index );			
		}

		update_post_meta( $order_id, '_order_version', WC_VERSION );
        $_order_tax = get_post_meta($order_id, '_order_tax', true);
        if (empty($_order_tax)){
            update_post_meta( $order_id, '_order_tax', 0 );
        }
        update_post_meta( $order_id, '_order_shipping_tax', 0 );
	}

	/**
	*
	* When users are matching to existing customers and/or products and no match it found, 
	* WP All Import doesn't have enough information to import that order, so the whole order will be skipped.
	*
	*/ 
	public function wp_all_import_is_post_to_skip( $is_post_to_skip, $import_id, $current_xml_node, $index, $post_to_update_id ) 
	{		

		$order_title = 'Order &ndash; ' . date_i18n( 'F j, Y @ h:i A', strtotime($this->data['pmwi_order']['date'][$index]) );

		if ( empty($post_to_update_id) or $this->import->options['update_all_data'] == 'yes' or $this->import->options['is_update_billing_details'] )
		{
			if ( $this->import->options['pmwi_order']['billing_source'] == 'existing' ) {
				$customer = $this->get_existing_customer('billing_source', $index);						
				if ( empty($customer) && empty($this->import->options['pmwi_order']['is_guest_matching']))
				{
					$this->logger and call_user_func($this->logger, sprintf(__('<b>SKIPPED</b>: %s Existing customer not found for Order `%s`.', 'wpai_woocommerce_addon_plugin'), $this->get_existing_customer_for_logger('billing_source', $index), $order_title));
					$is_post_to_skip = true;
				}
			}
		}
		
		if ( empty($post_to_update_id) or $this->import->options['update_all_data'] == 'yes' or $this->import->options['is_update_products'] )
		{
			if ( ! $is_post_to_skip and $this->import->options['pmwi_order']['products_source'] == 'existing')
			{
				$is_product_founded = false;

				$searching_for_sku = '';

				foreach ($this->data['pmwi_order']['products'][$index] as $productIndex => $productItem) 
				{				
					if (empty($productItem['sku']))	continue;

					$searching_for_sku = $productItem['sku'];

					$args = array(
						'post_type' => 'product',
						'meta_key' 	=> '_sku',
						'meta_value' => $productItem['sku'],
						'meta_compare' => '=',
					);

					$product = false;				

					$query = new WP_Query( $args );
					while ( $query->have_posts() ) {
						$query->the_post();
						$product = WC()->product_factory->get_product($query->post->ID);
						break;
					}
					wp_reset_postdata();

					if (empty($product))
					{
						$args['post_type'] = 'product_variation';
						$query = new WP_Query( $args );
						while ( $query->have_posts() ) {
							$query->the_post();
							$product = WC()->product_factory->get_product($query->post->ID);
							break;
						}
						wp_reset_postdata();
					}
					if ( $product )
					{
						$is_product_founded = true;
						break;
					}
				}
				if ( ! $is_product_founded ){
					$this->logger and call_user_func($this->logger, sprintf(__('<b>SKIPPED</b>: Existing product `%s` not found for Order `%s`.', 'wpai_woocommerce_addon_plugin'), $searching_for_sku, $order_title));
					$is_post_to_skip = true;
				}
			}
		}		

		return $is_post_to_skip;
	}

	public function wp_all_import_combine_article_data( $articleData, $post_type, $import_id, $index )
	{
		if ( $post_type == 'shop_order' && empty($articleData['post_title'])) 
		{
			$articleData['post_title'] = 'Order &ndash; ' . date_i18n( 'F j, Y @ h:i A', strtotime($this->data['pmwi_order']['date'][$index]) );
		}

		return $articleData;
	}

	public function after_save_post( $importData )
	{
		// Do something when shop order already imported	
		update_option('wp_all_import_previously_updated_order_' . $this->import->id, $importData['pid']);	
	}

	protected function get_order_notes( $order_id ) {
		$notes = array();
		$args  = array(
			'post_id' => $order_id,
			'approve' => 'approve',
			'type'    => ''
		);

		remove_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ) );

		$comments = get_comments( $args );

		foreach ( $comments as $comment ) {										
			if ($comment->comment_approved != 'trash') $notes[] = $comment;
		}

		add_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ) );

		return $notes;
	}

	protected function _import_line_items( & $order, $order_id, $index )
	{
		
		$is_product_founded = false;

		switch ( $this->import->options['pmwi_order']['products_source'] ) 
		{
			// Get data from existing products
			case 'existing':								

				foreach ($this->data['pmwi_order']['products'][$index] as $productIndex => $productItem) 
				{					
					if (empty($productItem['sku']))	continue;

					$args = array(
						'post_type' => 'product',
						'meta_key' 	=> '_sku',
						'meta_value' => $productItem['sku'],
						'meta_compare' => '=',
					);

					$product = false;				

					$query = new WP_Query( $args );
					while ( $query->have_posts() ) {
						$query->the_post();
						$product = WC()->product_factory->get_product($query->post->ID);
						break;
					}
					wp_reset_postdata();

					if ( empty($product))
					{
						$args['post_type'] = 'product_variation';
						$query = new WP_Query( $args );
						while ( $query->have_posts() ) {
							$query->the_post();
							$product = WC()->product_factory->get_product($query->post->ID);
							break;
						}
						wp_reset_postdata();
					}					

					if ( $product )
					{
						$is_product_founded = true;

						$item_price = $product->get_price();

						$item_qty = empty($productItem['qty']) ? 1 : $productItem['qty']; 

						$item_subtotal = $item_price * $item_qty;

						$item_subtotal_tax = 0;

						$line_taxes = array();

						foreach ($productItem['tax_rates'] as $key => $tax_rate) 
						{		
							if (empty($tax_rate['code'])) continue;

							$tax_rate_codes   = explode("|", $tax_rate['code']);
							$percentage_value = explode("|", $tax_rate['percentage_value']);
							$amount_per_unit  = explode("|", $tax_rate['amount_per_unit']);

							foreach ($tax_rate_codes as $rate_key => $tax_rate_code) {

							    if ( $tax_rate_code == 'standard' ) $tax_rate_code = '';

								$line_tax = 0;												

								switch ($tax_rate['calculate_logic']) 
								{
									case 'percentage':										

										if ( ! empty($percentage_value[$rate_key]) and is_numeric($percentage_value[$rate_key]))
										{
											$line_tax = WC_Tax::round( ($item_subtotal/100) * $percentage_value[$rate_key] );
											$item_subtotal_tax += $line_tax;
										}

                                        if ( ! empty($this->tax_rates)){
                                            foreach($this->tax_rates as $rate_id => $rate){
                                                if ($rate->tax_rate_name == $tax_rate_code){
                                                    $line_taxes[$rate->tax_rate_id] = $line_tax;
                                                    break;
                                                }
                                            }
                                        }

										break;

									case 'per_unit';
										
										if ( ! empty($amount_per_unit[$rate_key]) and is_numeric($amount_per_unit[$rate_key]))
										{
											$line_tax = WC_Tax::round( $amount_per_unit[$rate_key] * $item_qty );
											$item_subtotal_tax += $line_tax;
										}

                                        if ( ! empty($this->tax_rates)){
                                            foreach($this->tax_rates as $rate_id => $rate){
                                                if ($rate->tax_rate_name == $tax_rate_code){
                                                    $line_taxes[$rate->tax_rate_id] = $line_tax;
                                                    break;
                                                }
                                            }
                                        }
										break;
								
									// Look up tax rate code
									default:
										
										$found_rates = WC_Tax::get_rates_for_tax_class( $tax_rate_code );

										if ( ! empty($found_rates))
										{
											$found_priority    = array();

											foreach ( $found_rates as $found_rate ) {
                                                $matched_tax_rates = array();

												if ( in_array( $found_rate->tax_rate_priority, $found_priority ) ) {
													continue;
												}

												$matched_tax_rates[ $found_rate->tax_rate_id ] = array(
													'rate'     => $found_rate->tax_rate,
													'label'    => $found_rate->tax_rate_name,
													'shipping' => $found_rate->tax_rate_shipping ? 'yes' : 'no',
													'compound' => $found_rate->tax_rate_compound ? 'yes' : 'no'
												);

                                                $line_tax = array_sum( WC_Tax::calc_tax( $item_subtotal, $matched_tax_rates, $this->prices_include_tax ) );

                                                $item_subtotal_tax += $line_tax;
                                                $line_taxes[$found_rate->tax_rate_id] = $line_tax;

                                                $found_priority[] = $found_rate->tax_rate_priority;
											}
										}

										break;
								}
							}							
						}						

						$variation = array();

						$variation_str = '';

						if ( $product instanceOf WC_Product_Variation )
						{
							$variation = $product->get_variation_attributes();

							if (!empty($variation)){
								foreach ($variation as $key => $value) {
									$variation_str .= $key . '-' . $value;
								}
							}
						}

						$product_item = new PMXI_Post_Record();
						$product_item->getBy(array(
							'import_id'  => $this->import->id,
							'post_id'    => $order_id,
							'unique_key' => 'line-item-' . $product->get_id() . '-' . $variation_str
						));
						
						if ( $product_item->isEmpty() )
						{	
							$item_id = false;
							
							// in case when this is new order just add new line items
							if ( ! $item_id )
							{
								$item_id = $order->add_product(
									$product,
									$item_qty,
									array(
										'variation' => $variation,
										'totals'    => array(
											'subtotal'     => $item_subtotal,
											'subtotal_tax' => $item_subtotal_tax,
											'total'        => $item_subtotal,
											'tax'          => $item_subtotal_tax,
											'tax_data'     => array( 'total' => $line_taxes, 'subtotal' => array()) // Since 2.2
										)
									)
								);								
							}							
								
							if ( ! $item_id ) {
								$this->logger and call_user_func($this->logger, __('- <b>WARNING</b> Unable to create order line product.', 'wp_all_import_plugin'));		
							}
							else
							{
								$product_item->set(array(
									'import_id'   => $this->import->id,
									'post_id'     => $order_id,
									'unique_key'  => 'line-item-' . $product->get_id() . '-' . $variation_str,
									'product_key' => 'line-item-' . $item_id,
									'iteration'   => $this->import->iteration
								))->save();
							}
						}
						else
						{
							$item_id = str_replace('line-item-', '', $product_item->product_key);
							$is_updated = $order->update_product(
								$item_id, 
								$product, 
								array(
									'qty' => $item_qty,
									'tax_class' => $product->get_tax_class(),
									'totals' => array(
										'subtotal'     => $item_subtotal,
										'subtotal_tax' => $item_subtotal_tax,
										'total'        => $item_subtotal,
										'tax'          => $item_subtotal_tax,
										'tax_data'     => array( 'total' => $line_taxes, 'subtotal' => array() ) // Since 2.2
									),
									'variation' =>  $variation
								)
							);
							if ( $is_updated )
							{
								$product_item->set(array(								
									'iteration'   => $this->import->iteration
								))->save();
							}
						}									
					}	
				}				
				
				break;

			// Manually import product order data and do not try to match to existing products
			default:

				$is_product_founded = true;
				
				foreach ($this->data['pmwi_order']['manual_products'][$index] as $productIndex => $productItem) 
				{									

					$item_price = $productItem['price_per_unit'];

					$item_qty = empty($productItem['qty']) ? 1 : $productItem['qty']; 

					$item_subtotal = $item_price * $item_qty;

					$item_subtotal_tax = 0;

					$line_taxes = array();

					foreach ($productItem['tax_rates'] as $key => $tax_rate) 
					{		
						if (empty($tax_rate['code'])) continue;

						$line_tax = 0;												

						switch ($tax_rate['calculate_logic']) 
						{
							case 'percentage':

								if ( ! empty($tax_rate['percentage_value']) and is_numeric($tax_rate['percentage_value']))
								{
									$line_tax = WC_Tax::round( ($item_subtotal/100) * $tax_rate['percentage_value'] );
									$item_subtotal_tax += $line_tax;
								}
								break;

							case 'per_unit';
								
								if ( ! empty($tax_rate['amount_per_unit']) and is_numeric($tax_rate['amount_per_unit']))
								{
									$line_tax = WC_Tax::round( $tax_rate['amount_per_unit'] * $item_qty );
									$item_subtotal_tax += $line_tax;
								}
								break;
						
							// Look up tax rate code
							default:
								
								$found_rates = WC_Tax::get_rates_for_tax_class( $tax_rate['code'] );

								if ( ! empty($found_rates))
								{									
									$matched_tax_rates = array();
									$found_priority    = array();

									foreach ( $found_rates as $found_rate ) {
										if ( in_array( $found_rate->tax_rate_priority, $found_priority ) ) {
											continue;
										}

										$matched_tax_rates[ $found_rate->tax_rate_id ] = array(
											'rate'     => $found_rate->tax_rate,
											'label'    => $found_rate->tax_rate_name,
											'shipping' => $found_rate->tax_rate_shipping ? 'yes' : 'no',
											'compound' => $found_rate->tax_rate_compound ? 'yes' : 'no'
										);

										$found_priority[] = $found_rate->tax_rate_priority;
									}
									$line_tax = array_sum( WC_Tax::calc_tax( $item_subtotal, $matched_tax_rates, true ) );
									$item_subtotal_tax += $line_tax;
								}

								break;
						}

						if ( ! empty($this->tax_rates)){
							foreach($this->tax_rates as $rate_id => $rate){
								$line_taxes[$rate->tax_rate_id] = $line_tax;
								break;
							}							
						}
					}

					$variation = array();

					$product_item = new PMXI_Post_Record();
					$product_item->getBy(array(
						'import_id'  => $this->import->id,
						'post_id'    => $order_id,
						'unique_key' => 'manual-line-item-' . $productIndex . '-' . $productItem['sku']
					));
					
					if ( $product_item->isEmpty() )
					{
						$item_id = wc_add_order_item( $order_id, array(
							'order_item_name' => $productItem['sku'],
							'order_item_type' => 'line_item'
						) );						
						
						if ( ! $item_id ) {
							$this->logger and call_user_func($this->logger, __('- <b>WARNING</b> Unable to create order line product.', 'wp_all_import_plugin'));			
						}
						else
						{
							wc_add_order_item_meta( $item_id, '_qty',          wc_stock_amount( $item_qty ) );
							wc_add_order_item_meta( $item_id, '_tax_class',    '' );

							wc_add_order_item_meta( $item_id, '_line_subtotal',     wc_format_decimal( $item_subtotal ));
							wc_add_order_item_meta( $item_id, '_line_total',        wc_format_decimal( $item_subtotal ));
							wc_add_order_item_meta( $item_id, '_line_subtotal_tax', wc_format_decimal( $item_subtotal_tax ));
							wc_add_order_item_meta( $item_id, '_line_tax',          wc_format_decimal( $item_subtotal_tax ));
							wc_add_order_item_meta( $item_id, '_line_tax_data', 	array( 'total' => $line_taxes, 'subtotal' => array() ) );

							if ( ! empty($productItem['meta_name']))
							{
								foreach ($productItem['meta_name'] as $key => $meta_name) 
								{
									wc_add_order_item_meta( $item_id, $meta_name, isset($productItem['meta_value'][$key]) ? $productItem['meta_value'][$key] : '');		
								}
							}							

							$product_item->set(array(
								'import_id'   => $this->import->id,
								'post_id'     => $order_id,
								'unique_key'  => 'manual-line-item-' . $productIndex . '-' . $productItem['sku'],
								'product_key' => 'manual-line-item-' . $item_id,
								'iteration'   => $this->import->iteration
							))->save();
						}
					}
					else
					{
						$item_id = str_replace('manual-line-item-', '', $product_item->product_key);						
						
						if ( is_numeric($item_id) )
						{
							wc_update_order_item( $item_id, array(
								'order_item_name' => $productItem['sku'],
								'order_item_type' => 'line_item'
							));

							wc_update_order_item_meta( $item_id, '_qty', wc_stock_amount( $item_qty ) );
							wc_update_order_item_meta( $item_id, '_tax_class',    '' );

							wc_update_order_item_meta( $item_id, '_line_subtotal',     wc_format_decimal( $item_subtotal ));
							wc_update_order_item_meta( $item_id, '_line_total',        wc_format_decimal( $item_subtotal ));
							wc_update_order_item_meta( $item_id, '_line_subtotal_tax', wc_format_decimal( $item_subtotal_tax ));
							wc_update_order_item_meta( $item_id, '_line_tax',          wc_format_decimal( $item_subtotal_tax ));
							wc_update_order_item_meta( $item_id, '_line_tax_data', 	array( 'total' => $line_taxes, 'subtotal' => array() ) );

							if ( ! empty($productItem['meta_name']))
							{
								foreach ($productItem['meta_name'] as $key => $meta_name) 
								{
									wc_update_order_item_meta( $item_id, $meta_name, isset($productItem['meta_value'][$key]) ? $productItem['meta_value'][$key] : '');		
								}
							}

							$product_item->set(array(								
								'iteration'   => $this->import->iteration
							))->save();
						}
					}
				}

				break;
		}

		return $is_product_founded;
	}

	protected function _import_fee_items( & $order, $order_id, $index )
	{
		if ( ! empty($this->data['pmwi_order']['fees'][$index]))
		{
			foreach ($this->data['pmwi_order']['fees'][$index] as $feeIndex => $fee) 
			{
				if (empty($fee['name'])) continue;

				$fee_item = new PMXI_Post_Record();
				$fee_item->getBy(array(
					'import_id'  => $this->import->id,
					'post_id'    => $order_id,
					'unique_key' => 'fee-item-' . $feeIndex
				));
				
				if ( $fee_item->isEmpty() )
				{

					$item_id = false;

					if ( ! empty($this->articleData['ID']) )
					{							
						$order_items = $order->get_items('fee');
						
						foreach ($order_items as $order_item_id => $order_item) 
						{
							if ( $order_item['name'] == $fee['name'] )
							{
								$item_id = $order_item_id;
								break(2);
							}									
						}	
					}

					if ( ! $item_id )
					{
						$fee_line = array(
							'name' => $fee['name'],
							'tax_class' => '',
							'amount' => $fee['amount'],
							'tax' => '',
							'tax_data' => array(),
							'taxable' => 0
						);
                        if ( version_compare(WOOCOMMERCE_VERSION, '3.0') < 0 ) {
                            $item_id = $order->add_fee((object) $fee_line);
                        }
                        else{

                            $item = new WC_Order_Item_Fee();
                            $item->set_order_id( $order_id );
                            $item->set_name( wc_clean( $fee_line['name'] ) );
                            $item->set_total( isset( $fee_line['amount'] ) ? floatval( $fee_line['amount'] ) : 0 );

                            // if taxable, tax class and total are required
                            if ( ! empty( $fee_line['taxable'] ) ) {
                                if ( ! isset( $fee_line['tax_class'] ) ) {
                                    $this->logger and call_user_func($this->logger, __('- <b>WARNING</b> Fee tax class is required when fee is taxable.', 'wp_all_import_plugin'));
                                }
                                else{
                                    $item->set_tax_status( 'taxable' );
                                    $item->set_tax_class( $fee_line['tax_class'] );

                                    if ( isset( $fee_line['total_tax'] ) ) {
                                        $item->set_total_tax( isset( $fee_line['total_tax'] ) ? wc_format_refund_total( $fee_line['total_tax'] ) : 0 );
                                    }

                                    if ( isset( $fee_line['tax_data'] ) ) {
                                        $item->set_total_tax( wc_format_refund_total( array_sum( $fee_line['tax_data'] ) ) );
                                        $item->set_taxes( array_map( 'wc_format_refund_total', $fee_line['tax_data'] ) );
                                    }
                                }
                            }
                            $item_id = $item->save();
                        }
					}					

					if ( ! $item_id ) {
						$this->logger and call_user_func($this->logger, __('- <b>WARNING</b> order line fee is not added.', 'wp_all_import_plugin'));
					}
					else
					{
						$fee_item->set(array(
							'import_id'   => $this->import->id,
							'post_id'     => $order_id,
							'unique_key'  => 'fee-item-' . $feeIndex,
							'product_key' => 'fee-item-' . $item_id,
							'iteration'   => $this->import->iteration
						))->save();
					}
				}
				else
				{
					$item_id = str_replace('fee-item-', '', $fee_item->product_key);

                    if ( version_compare(WOOCOMMERCE_VERSION, '3.0') < 0 ) {
                        $is_updated = $order->update_fee($item_id, array(
                            'name' => $fee['name'],
                            'tax_class' => '',
                            'line_total' => $fee['amount'],
                            'line_tax' => 0
                        ));
                    }
                    else{
                        $item = new WC_Order_Item_Fee( $item_id );

                        if ( isset( $fee['title'] ) ) {
                            $item->set_name( wc_clean( $fee['name'] ) );
                        }
                        if ( isset( $fee['tax_class'] ) ) {
                            $item->set_tax_class( $fee['tax_class'] );
                        }
                        if ( isset( $fee['amount'] ) ) {
                            $item->set_total( floatval( $fee['amount'] ) );
                        }
                        if ( isset( $fee['total_tax'] ) ) {
                            $item->set_total_tax( floatval( $fee['total_tax'] ) );
                        }
                        $is_updated = $item->save();
                    }

					if ( $is_updated )
					{
						$fee_item->set(array(								
							'iteration'   => $this->import->iteration
						))->save();
					}
				}
			}
			$this->_calculate_fee_taxes( $order );
		}
	}

	protected function _import_coupons_items( & $order, $order_id, $index )
	{
		$total_discount_amount = 0;
		$total_discount_amount_tax = 0;

		if ( ! empty($this->data['pmwi_order']['coupons'][$index]))
		{			
			foreach ($this->data['pmwi_order']['coupons'][$index] as $couponIndex => $coupon) 
			{
				if (empty($coupon['code'])) continue;

				$coupon += array('code' => '', 'amount' => '', 'amount_tax' => '');

				$order_item = new PMXI_Post_Record();
				$order_item->getBy(array(
					'import_id'  => $this->import->id,
					'post_id'    => $order_id,
					'unique_key' => 'coupon-item-' . $couponIndex
				));

                $absAmount = abs($coupon['amount']);

				if ( ! empty($absAmount) ) $total_discount_amount += $absAmount;
				if ( ! empty($coupon['amount_tax']) ) $total_discount_amount_tax += $coupon['amount_tax'];
				
				if ( $order_item->isEmpty() )
				{	
					$item_id = false;

					if ( ! empty($this->articleData['ID']) )
					{							
						$order_items = $order->get_items('coupon');
						
						foreach ($order_items as $order_item_id => $order_item) 
						{
							if ( $order_item['name'] == $coupon['code'] )
							{
								$item_id = $order_item_id;
								break(2);
							}									
						}	
					}

					if ( ! $item_id )
					{
						if ( version_compare(WOOCOMMERCE_VERSION, '3.0') < 0 ){
                            $item_id = $order->add_coupon( $coupon['code'], $absAmount, $coupon['amount_tax'] );
                        }
                        else{
                            $item = new WC_Order_Item_Coupon();
                            $item->set_props( array(
                                'code'         => $coupon['code'],
                                'discount'     => isset( $coupon['amount'] ) ? floatval( $coupon['amount'] ) : 0,
                                'discount_tax' => 0,
                                'order_id'     => $order_id,
                            ) );
                            $item_id = $item->save();
                        }
					}					

					if ( ! $item_id ) {
						$this->logger and call_user_func($this->logger, __('- <b>WARNING</b> Unable to create order coupon line.', 'wp_all_import_plugin'));
					}
					else
					{
						$order_item->set(array(
							'import_id'   => $this->import->id,
							'post_id'     => $order_id,
							'unique_key'  => 'coupon-item-' . $couponIndex,
							'product_key' => 'coupon-item-' . $item_id,
							'iteration'   => $this->import->iteration
						))->save();
					}					
				}		
				else
				{
					$item_id = str_replace('coupon-item-', '', $order_item->product_key);

                    if ( version_compare(WOOCOMMERCE_VERSION, '3.0') < 0 ) {

                        $is_updated = $order->update_coupon($item_id, array(
                            'code' => $coupon['code'],
                            'discount_amount' => $absAmount,
                            // 'discount_amount_tax' => empty($coupon['amount_tax']) ? NULL : $coupon['amount_tax']
                        ));

                    }
                    else{

                        $item = new WC_Order_Item_Coupon( $item_id );

                        if ( isset( $coupon['code'] ) ) {
                            $item->set_code( $coupon['code'] );
                        }

                        if ( isset( $coupon['amount'] ) ) {
                            $item->set_discount( floatval( $coupon['amount'] ) );
                        }

                        $is_updated = $item->save();
                    }

					if ( $is_updated )
					{
						$order_item->set(array(								
							'iteration'   => $this->import->iteration
						))->save();
					}
				}		
			}			
		}
		update_post_meta($order_id, '_cart_discount', $total_discount_amount);
		update_post_meta($order_id, '_cart_discount_tax', $total_discount_amount_tax);
	}

	protected function _import_shipping_items( & $order, $order_id, $index )
	{
		if ( ! empty($this->data['pmwi_order']['shipping'][$index]))
		{			
			$total_shipping = 0;

			foreach ($this->data['pmwi_order']['shipping'][$index] as $shippingIndex => $shipping)
			{
				if (empty($shipping['name'])) continue;

				$method = false;				

				if ($this->import->options['pmwi_order']['shipping'][0]['class'] == 'xpath')
				{
					if ( empty($this->shipping_methods[$shipping['class']]))
					{
						foreach ($this->shipping_methods as $shipping_method_slug => $shipping_method) 
						{

							if ( $shipping_method_slug == strtolower(trim($shipping['class'])) || $shipping_method->method_title == $shipping['class'])
							{
								$method = $shipping_method;
								break;
							}
						}						
					} 	
					else
					{
						$method = $this->shipping_methods[$shipping['class']];
					}

                    if ( empty($method) && !empty($this->shipping_zone_methods) ){
                        foreach ($this->shipping_zone_methods as $shipping_zone_method) {
                            if ($shipping_zone_method->title == $shipping['class']){
                                $method = $shipping_zone_method;
                                break;
                            }
                        }
                    }
				}	
				else
				{
					foreach ($this->shipping_methods as $shipping_method_slug => $shipping_method) 
					{						
						if ( $shipping_method_slug == strtolower(trim($shipping['class'])) || $shipping_method->method_title == $shipping['class'])
						{
							$method = $shipping_method;
							break;
						}
					}
				}

				if ( $method )
				{					
					$shipping_method = new WC_Shipping_Rate($method->id, $shipping['name'], $shipping['amount']);

					$shipping_item = new PMXI_Post_Record();
					$shipping_item->getBy(array(
						'import_id'  => $this->import->id,
						'post_id'    => $order_id,
						'unique_key' => 'shipping-item-' . $shippingIndex
					));
					
					$total_shipping += $shipping['amount'];

					if ( $shipping_item->isEmpty() )
					{
						$item_id = false;

						if ( ! empty($this->articleData['ID']) )
						{							
							$order_items = $order->get_items('shipping');
							
							foreach ($order_items as $order_item_id => $order_item) 
							{
								if ( $order_item['name'] == $shipping['name'] )
								{
									$item_id = $order_item_id;
									break(2);
								}									
							}	
						}

						if ( ! $item_id )
						{
							$item_id = $order->add_shipping( $shipping_method );
						}

						if ( ! $item_id ) {						
							$this->logger and call_user_func($this->logger, __('- <b>WARNING</b> Unable to create order shipping line.', 'wp_all_import_plugin'));
						}
						else
						{
							$shipping_item->set(array(
								'import_id'   => $this->import->id,
								'post_id'     => $order_id,
								'unique_key'  => 'shipping-item-' . $shippingIndex,
								'product_key' => 'shipping-item-' . $item_id,
								'iteration'   => $this->import->iteration
							))->save();
						}
					}
					else
					{
						$item_id = str_replace('shipping-item-', '', $shipping_item->product_key);
						$is_updated = $order->update_shipping($item_id, array(
							'method_title' => $shipping_method->label,
							'method_id' => $shipping_method->id,
							'cost' => $shipping_method->cost
						));
						if ( $is_updated )
						{
							$shipping_item->set(array(								
								'iteration'   => $this->import->iteration
							))->save();
						}
					}
				}				
			}
			update_post_meta($order_id, '_order_shipping', $total_shipping);

            $this->_calculate_shipping_taxes( $order );
		}
	}

	protected function _import_taxes_items( & $order, $order_id, $index )
	{
		if ( ! empty($this->data['pmwi_order']['taxes'][$index]))
		{
			foreach ($this->data['pmwi_order']['taxes'][$index] as $taxIndex => $tax) 
			{
				if (empty($tax['tax_code'])) continue;

				$founded = true;	

				$tax_rate = null;			

				if ($this->import->options['pmwi_order']['taxes'][0]['tax_code'] == 'xpath')
				{
					if ( empty($this->tax_rates[$tax['tax_code']])) 
					{
						$founded_by_name = false;
						foreach ($this->tax_rates as $rate_id => $rate) {							
							if ($rate->tax_rate_name == $tax['tax_code']){
								$founded_by_name = true;
								$tax_rate = $rate;
								break;
							}
						}
						if ( ! $founded_by_name ) $founded = false;			
					}
					else
					{
						$tax_rate = $this->tax_rates[$tax['tax_code']];
						$tax['tax_amount'] = 0;
						$tax['shipping_tax_amount'] = 0;
					}
				}
                else{
                    if ( ! empty($this->tax_rates[$tax['tax_code']])) {
                        $tax_rate = $this->tax_rates[$tax['tax_code']];
                        $tax['tax_amount'] = 0;
                        $tax['shipping_tax_amount'] = 0;
                    }
                    else{
                        $founded = false;
                    }
                }

                if ( $founded )
				{
					$tax_item = new PMXI_Post_Record();
					$tax_item->getBy(array(
						'import_id'  => $this->import->id,
						'post_id'    => $order_id,
						'unique_key' => 'tax-item-' . $taxIndex
					));

					if ( $tax_item->isEmpty() )
					{
						$item_id = false;

						if ( ! empty($this->articleData['ID']) )
						{							
							$order_items = $order->get_items('tax');
							
							foreach ($order_items as $order_item_id => $order_item) 
							{
								if ( $order_item['name'] == $tax['tax_code'] )
								{
									$item_id = $order_item_id;
									break(2);
								}									
							}	
						}

						if ( ! $item_id )
						{

                            if ( version_compare(WOOCOMMERCE_VERSION, '3.0') < 0 ) {
                                $item_id = $order->add_tax( $tax_rate->tax_rate_id, $tax['tax_amount'], $tax['shipping_tax_amount'] );
                            }
                            else{

                                $item = new WC_Order_Item_Tax();
                                $item->set_props( array(
                                    'name'      => $tax_rate->tax_rate_name,
                                    'tax_class' => empty($tax_rate->tax_rate_class) ? 0 : $tax_rate->tax_rate_class,
                                    'total'     => $tax['tax_amount'],
                                    'total_tax' => $tax['tax_amount'],
                                    'order_id'  => $order_id,
                                ) );
                                $item_id = $item->save();
                            }
						}

						if ( ! $item_id ) {						
							$this->logger and call_user_func($this->logger, __('- <b>WARNING</b> Unable to create order tax line.', 'wp_all_import_plugin'));
						}
						else
						{
							$tax_item->set(array(
								'import_id'   => $this->import->id,
								'post_id'     => $order_id,
								'unique_key'  => 'tax-item-' . $taxIndex,
								'product_key' => 'tax-item-' . $item_id,
								'iteration'   => $this->import->iteration
							))->save();
						}
					}
					else{

                        $item_id = str_replace('tax-item-', '', $tax_item->product_key);

                        if ( version_compare(WOOCOMMERCE_VERSION, '3.0') >= 0 ) {

                            $item = new WC_Order_Item_Tax( $item_id );

                            if ( isset( $tax_rate->tax_rate_name ) ) {
                                $item->set_name( wc_clean( $tax_rate->tax_rate_name ) );
                            }
                            if ( isset( $tax_rate->tax_rate_id ) ) {
                                $item->set_rate( $tax_rate->tax_rate_id );
                            }
                            if ( isset( $tax['tax_amount'] ) ) {
                                $item->set_tax_total( floatval( $tax['tax_amount'] ) );
                            }

                            $is_updated = $item->save();

                            if ( $is_updated )
                            {
                                $tax_item->set(array(
                                    'iteration'   => $this->import->iteration
                                ))->save();
                            }
                        }

                    }
//					$order->update_taxes();
				}				
			}
		}
	}

	protected function _import_order_notes( & $order, $order_id, $index )
	{
		if ( ! empty($this->data['pmwi_order']['notes'][$index]))
		{
			$notes_count = 0;
			foreach ($this->data['pmwi_order']['notes'][$index] as $noteIndex => $note) 
			{
				if (empty($note['content'])) continue;

				$note_item = new PMXI_Post_Record();
				$note_item->getBy(array(
					'import_id'  => $this->import->id,
					'post_id'    => $order_id,
					'unique_key' => 'note-item-' . $order_id . '-' . $noteIndex
				));	

				if ( ! $note_item->isEmpty()){
					$note_id = str_replace('note-item-', '', $note_item->product_key);					
					$is_note_exist = get_comment($note_id);					
					if ( empty($is_note_exist) || $is_note_exist->comment_approved == 'trash' ){
						$note_item->delete();						
						$note_item->clear();
						if ( ! empty($is_note_exist) && $is_note_exist->comment_approved == 'trash' ){
							wp_delete_comment( $note_id, true );
						}
					}
				}

				$comment_author = empty($note['username']) ? 'WP All Import' : $note['username'];
				$comment_author_email = $note['email'];

				if ( empty($comment_author) && empty($comment_author_email) )
				{
					if ( is_user_logged_in() ) {
						$user                 = get_user_by( 'id', get_current_user_id() );							
						$comment_author_email = $user->user_email;
					} else {
						$comment_author_email = strtolower( __( 'WooCommerce', 'wp_all_import_plugin' ) ) . '@';
						$comment_author_email .= isset( $_SERVER['HTTP_HOST'] ) ? str_replace( 'www.', '', $_SERVER['HTTP_HOST'] ) : 'noreply.com';
						$comment_author_email = sanitize_email( $comment_author_email );
					}
				}				

				$comment_post_ID        = $order_id;
				$comment_author_url     = '';
				$comment_content        = $note['content'];
				$comment_agent          = 'WooCommerce';
				$comment_type           = 'order_note';
				$comment_parent         = 0;
				$comment_approved       = 1;
				$is_customer_note       = $note['visibility'] == "private" ? 0 : 1;
				$commentdata            = apply_filters( 'woocommerce_new_order_note_data', compact( 'comment_post_ID', 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_content', 'comment_agent', 'comment_type', 'comment_parent', 'comment_approved' ), array( 'order_id' => $order_id, 'is_customer_note' => $is_customer_note ) );					

				if ( $note_item->isEmpty())
				{						
					$comment_id = false;

					if ( ! empty($this->articleData['ID']))
					{
						$current_notes = $this->get_order_notes( $order_id );

						if ( ! empty($current_notes) )
						{
							foreach ($current_notes as $current_note) 
							{									
								if ($current_note->comment_content == $commentdata['comment_content'] )
								{
									$comment_id = $current_note->comment_ID;
									break;
								}	
							}
						}

					}						

					if ( ! $comment_id )
					{
						$comment_id = wp_insert_comment( $commentdata );

						if ( $note['visibility'] != 'private') 
						{
							add_comment_meta( $comment_id, 'is_customer_note', 1 );
							// send customer note notification
							if ( empty($this->import->options['do_not_send_order_notifications']) ) 
							{												
								do_action( 'woocommerce_new_customer_note', array( 'order_id' => $order_id, 'customer_note' => $commentdata['comment_content'] ) );								
							}
						}
					}						

					$note_item->set(array(
						'import_id'   => $this->import->id,
						'post_id'     => $order_id,
						'unique_key'  => 'note-item-' . $order_id . '-' . $noteIndex,
						'product_key' => 'note-item-' . $comment_id,
						'iteration' => $this->import->iteration
					))->save();
				}				
				else
				{
					$commentdata['comment_ID'] = str_replace('note-item-', '', $note_item->product_key);
					
					wp_update_comment( $commentdata );

					if ( $note['visibility'] != 'private') {
						update_comment_meta( $commentdata['comment_ID'], 'is_customer_note', 1 );
					}
					else
					{
						delete_comment_meta( $commentdata['comment_ID'], 'is_customer_note' );
					}

					$note_item->set(array(						
						'iteration' => $this->import->iteration
					))->save();
				}
				$notes_count++;
			}

			global $wpdb;			

			$wpdb->update( $wpdb->posts, array('comment_count'  => $notes_count), array('ID' => $order_id), array( '%d' ), array( '%d' ) );
		}
	}

	protected function get_existing_customer_for_logger( $option_slug, $index )
	{
		$log = __("Search customer by ", "wpai_woocommerce_addon_plugin");

		switch ($this->import->options['pmwi_order'][$option_slug . '_match_by']) 
		{
			case 'username':

				$log .= __("username", "wpai_woocommerce_addon_plugin") . " `" . $this->data['pmwi_order'][$option_slug . '_username'][$index] . "`";
				
				break;

			case 'email':
				
				$log .= __("email", "wpai_woocommerce_addon_plugin") . " `" . $this->data['pmwi_order'][$option_slug . '_email'][$index] . "`";

				break;

			case 'cf':

				$log .= __("custom field", "wpai_woocommerce_addon_plugin") . ": `" . $this->data['pmwi_order'][$option_slug . '_cf_name'][$index] . "` equals to `" . $this->data['pmwi_order'][$option_slug . '_cf_value'][$index] . "`";				

				break;

			case 'id':

				$log .= __("ID", "wpai_woocommerce_addon_plugin") . " `" . $this->data['pmwi_order'][$option_slug . '_id'][$index] . "`";

				break;
		}

		return $log . ".";
	}

	protected function get_existing_customer( $option_slug, $index )
	{
		$customer = false;
				
		switch ($this->import->options['pmwi_order'][$option_slug . '_match_by']) 
		{
			case 'username':
				$search_by = $this->data['pmwi_order'][$option_slug . '_username'][$index];
				$customer  = get_user_by('login', $search_by) or $customer = get_user_by('slug', $search_by);
				break;

			case 'email':
				$search_by = $this->data['pmwi_order'][$option_slug . '_email'][$index];
				$customer  = get_user_by('email', $search_by);
				break;

			case 'cf':
				$cf_name   = $this->data['pmwi_order'][$option_slug . '_cf_name'][$index];
				$cf_value  = $this->data['pmwi_order'][$option_slug . '_cf_value'][$index];

				$user_query = new WP_User_Query( array( 'meta_key' => $cf_name, 'meta_value' => $cf_value ) );

				if ( ! empty( $user_query->results ) ) {
					$customer = array_shift($user_query->results);							
				}

				break;

			case 'id':
				$search_by = $this->data['pmwi_order'][$option_slug . '_id'][$index];
				$customer = get_user_by('id', $search_by);
				break;
		}

		return $customer;
	}

	protected function _calculate_shipping_taxes( & $order ){

        $tax_total          = 0;
        $shipping_tax_total = 0;
        $taxes              = array();
        $shipping_taxes     = array();
        $tax_based_on       = get_option( 'woocommerce_tax_based_on' );

        // If is_vat_exempt is 'yes', or wc_tax_enabled is false, return and do nothing.
        if ( ! wc_tax_enabled() ) {
            return false;
        }

        if ( 'billing' === $tax_based_on ) {
            $country  = $order->billing_country;
            $state    = $order->billing_state;
            $postcode = $order->billing_postcode;
            $city     = $order->billing_city;
        } elseif ( 'shipping' === $tax_based_on ) {
            $country  = $order->shipping_country;
            $state    = $order->shipping_state;
            $postcode = $order->shipping_postcode;
            $city     = $order->shipping_city;
        }

	    // Calc taxes for shipping
        foreach ( $order->get_shipping_methods() as $item_id => $item ) {
            $shipping_tax_class = get_option( 'woocommerce_shipping_tax_class' );

            // Inherit tax class from items
            if ( '' === $shipping_tax_class ) {
                $tax_classes       = WC_Tax::get_tax_classes();
                $found_tax_classes = $order->get_items_tax_classes();

                foreach ( $tax_classes as $tax_class ) {
                    $tax_class = sanitize_title( $tax_class );
                    if ( in_array( $tax_class, $found_tax_classes ) ) {
                        $tax_rates = WC_Tax::find_shipping_rates( array(
                          'country'   => $country,
                          'state'     => $state,
                          'postcode'  => $postcode,
                          'city'      => $city,
                          'tax_class' => $tax_class,
                        ) );
                        break;
                    }
                }
            } else {
                $tax_rates = WC_Tax::find_shipping_rates( array(
                  'country'   => $country,
                  'state'     => $state,
                  'postcode'  => $postcode,
                  'city'      => $city,
                  'tax_class' => 'standard' === $shipping_tax_class ? '' : $shipping_tax_class,
                ) );
            }

            $line_taxes          = WC_Tax::calc_tax( $item['cost'], $tax_rates, false );
            $line_tax            = max( 0, array_sum( $line_taxes ) );
            $shipping_tax_total += $line_tax;

            wc_update_order_item_meta( $item_id, '_line_tax', wc_format_decimal( $line_tax ) );
            wc_update_order_item_meta( $item_id, '_line_tax_data', array( 'total' => $line_taxes ) );

            // Sum the item taxes
            foreach ( array_keys( $shipping_taxes + $line_taxes ) as $key ) {
                $shipping_taxes[ $key ] = ( isset( $line_taxes[ $key ] ) ? $line_taxes[ $key ] : 0 ) + ( isset( $shipping_taxes[ $key ] ) ? $shipping_taxes[ $key ] : 0 );
            }
            wc_update_order_item_meta( $item_id, 'taxes', $shipping_taxes );
        }

        // Save tax totals
        $order->set_total( $shipping_tax_total, 'shipping_tax' );
//        $order->set_total( $tax_total, 'tax' );
    }

    protected function _calculate_fee_taxes( &$order ){

        $tax_total          = 0;
        $shipping_tax_total = 0;
        $taxes              = array();
        $shipping_taxes     = array();
        $tax_based_on       = get_option( 'woocommerce_tax_based_on' );

        // If is_vat_exempt is 'yes', or wc_tax_enabled is false, return and do nothing.
        if ( ! wc_tax_enabled() ) {
            return false;
        }

        if ( 'billing' === $tax_based_on ) {
            $country  = $order->billing_country;
            $state    = $order->billing_state;
            $postcode = $order->billing_postcode;
            $city     = $order->billing_city;
        } elseif ( 'shipping' === $tax_based_on ) {
            $country  = $order->shipping_country;
            $state    = $order->shipping_state;
            $postcode = $order->shipping_postcode;
            $city     = $order->shipping_city;
        }

        // Default to base
        if ( 'base' === $tax_based_on || empty( $country ) ) {
            $default  = wc_get_base_location();
            $country  = $default['country'];
            $state    = $default['state'];
            $postcode = '';
            $city     = '';
        }

        // Get items
        foreach ( $order->get_items( array( 'fee' ) ) as $item_id => $item ) {

            $product           = $order->get_product_from_item( $item );
            $line_total        = isset( $item['line_total'] ) ? $item['line_total'] : 0;
            $line_subtotal     = isset( $item['line_subtotal'] ) ? $item['line_subtotal'] : 0;
            $tax_class         = $item['tax_class'];
            $item_tax_status   = $product ? $product->get_tax_status() : 'taxable';

            if ( '0' !== $tax_class && 'taxable' === $item_tax_status ) {

                $tax_rates = WC_Tax::find_rates( array(
                  'country'   => $country,
                  'state'     => $state,
                  'postcode'  => $postcode,
                  'city'      => $city,
                  'tax_class' => $tax_class
                ) );

                $line_subtotal_taxes = WC_Tax::calc_tax( $line_subtotal, $tax_rates, false );
                $line_taxes          = WC_Tax::calc_tax( $line_total, $tax_rates, false );
                $line_subtotal_tax   = max( 0, array_sum( $line_subtotal_taxes ) );
                $line_tax            = max( 0, array_sum( $line_taxes ) );
                $tax_total           += $line_tax;

                wc_update_order_item_meta( $item_id, '_line_subtotal_tax', wc_format_decimal( $line_subtotal_tax ) );
                wc_update_order_item_meta( $item_id, '_line_tax', wc_format_decimal( $line_tax ) );
                wc_update_order_item_meta( $item_id, '_line_tax_data', array( 'total' => $line_taxes, 'subtotal' => $line_subtotal_taxes ) );

                // Sum the item taxes
                foreach ( array_keys( $taxes + $line_taxes ) as $key ) {
                    $taxes[ $key ] = ( isset( $line_taxes[ $key ] ) ? $line_taxes[ $key ] : 0 ) + ( isset( $taxes[ $key ] ) ? $taxes[ $key ] : 0 );
                }
            }
        }
    }
}