8889841cPK     K[AðMÉ  É    class-wc-cli.phpnu „[µü¤        includes();
		$this->hooks();
	}
	/**
	 * Load command files.
	 */
	private function includes() {
		require_once dirname( __FILE__ ) . '/cli/class-wc-cli-runner.php';
		require_once dirname( __FILE__ ) . '/cli/class-wc-cli-rest-command.php';
		require_once dirname( __FILE__ ) . '/cli/class-wc-cli-tool-command.php';
		require_once dirname( __FILE__ ) . '/cli/class-wc-cli-update-command.php';
		require_once dirname( __FILE__ ) . '/cli/class-wc-cli-tracker-command.php';
		require_once dirname( __FILE__ ) . '/cli/class-wc-cli-com-command.php';
		require_once dirname( __FILE__ ) . '/cli/class-wc-cli-com-extension-command.php';
	}
	/**
	 * Sets up and hooks WP CLI to our CLI code.
	 */
	private function hooks() {
		WP_CLI::add_hook( 'after_wp_load', 'WC_CLI_Runner::after_wp_load' );
		WP_CLI::add_hook( 'after_wp_load', 'WC_CLI_Tool_Command::register_commands' );
		WP_CLI::add_hook( 'after_wp_load', 'WC_CLI_Update_Command::register_commands' );
		WP_CLI::add_hook( 'after_wp_load', 'WC_CLI_Tracker_Command::register_commands' );
		WP_CLI::add_hook( 'after_wp_load', 'WC_CLI_COM_Command::register_commands' );
		WP_CLI::add_hook( 'after_wp_load', 'WC_CLI_COM_Extension_Command::register_commands' );
		$cli_runner = wc_get_container()->get( CLIRunner::class );
		WP_CLI::add_hook( 'after_wp_load', array( $cli_runner, 'register_commands' ) );
	}
}
new WC_CLI();
PK     K[ÈÛ4´x
  x
    class-wc-cart-fees.phpnu „[µü¤        cart->fees_api() which will reference this class.
 *
 * We suggest using the action woocommerce_cart_calculate_fees hook for adding fees.
 *
 * @package WooCommerce\Classes
 * @version 3.2.0
 */
defined( 'ABSPATH' ) || exit;
/**
 * WC_Cart_Fees class.
 *
 * @since 3.2.0
 */
final class WC_Cart_Fees {
	/**
	 * An array of fee objects.
	 *
	 * @var object[]
	 */
	private $fees = array();
	/**
	 * New fees are made out of these props.
	 *
	 * @var array
	 */
	private $default_fee_props = array(
		'id'        => '',
		'name'      => '',
		'tax_class' => '',
		'taxable'   => false,
		'amount'    => 0,
		'total'     => 0,
	);
	/**
	 * Constructor. Reference to the cart.
	 *
	 * @param null $deprecated Deprecated since WooCommerce 8.2.0.
	 *
	 * @since 3.2.0
	 */
	public function __construct( $deprecated = null ) {
		if ( isset( $deprecated ) ) {
			wc_doing_it_wrong(
				'new WC_Cart_Fees',
				'You don\'t need to pass a cart parameter to the WC_Cart_Fees constructor anymore',
				'8.2.0'
			);
		}
	}
	/**
	 * Register methods for this object on the appropriate WordPress hooks.
	 */
	public function init() {}
	/**
	 * Add a fee. Fee IDs must be unique.
	 *
	 * @since 3.2.0
	 * @param array $args Array of fee properties.
	 * @return object Either a fee object if added, or a WP_Error if it failed.
	 */
	public function add_fee( $args = array() ) {
		$fee_props            = (object) wp_parse_args( $args, $this->default_fee_props );
		$fee_props->name      = $fee_props->name ? $fee_props->name : __( 'Fee', 'woocommerce' );
		$fee_props->tax_class = in_array( $fee_props->tax_class, array_merge( WC_Tax::get_tax_classes(), WC_Tax::get_tax_class_slugs() ), true ) ? $fee_props->tax_class : '';
		$fee_props->taxable   = wc_string_to_bool( $fee_props->taxable );
		$fee_props->amount    = wc_format_decimal( $fee_props->amount );
		if ( empty( $fee_props->id ) ) {
			$fee_props->id = $this->generate_id( $fee_props );
		}
		if ( array_key_exists( $fee_props->id, $this->fees ) ) {
			return new WP_Error( 'fee_exists', __( 'Fee has already been added.', 'woocommerce' ) );
		}
		$this->fees[ $fee_props->id ] = $fee_props;
		return $this->fees[ $fee_props->id ];
	}
	/**
	 * Get fees.
	 *
	 * @return array
	 */
	public function get_fees() {
		uasort( $this->fees, array( $this, 'sort_fees_callback' ) );
		return $this->fees;
	}
	/**
	 * Set fees.
	 *
	 * @param object[] $raw_fees Array of fees.
	 */
	public function set_fees( $raw_fees = array() ) {
		$this->fees = array();
		foreach ( $raw_fees as $raw_fee ) {
			$this->add_fee( $raw_fee );
		}
	}
	/**
	 * Remove all fees.
	 *
	 * @since 3.2.0
	 */
	public function remove_all_fees() {
		$this->set_fees();
	}
	/**
	 * Sort fees by amount.
	 *
	 * @param stdClass $a Fee object.
	 * @param stdClass $b Fee object.
	 * @return int
	 */
	protected function sort_fees_callback( $a, $b ) {
		/**
		 * Filter sort fees callback.
		 *
		 * @since 3.8.0
		 * @param int Sort order, -1 or 1.
		 * @param stdClass $a Fee object.
		 * @param stdClass $b Fee object.
		 */
		return apply_filters( 'woocommerce_sort_fees_callback', $a->amount > $b->amount ? -1 : 1, $a, $b );
	}
	/**
	 * Generate a unique ID for the fee being added.
	 *
	 * @param string $fee Fee object.
	 * @return string fee key.
	 */
	private function generate_id( $fee ) {
		return sanitize_title( $fee->name );
	}
}
PK     K[•P-zšÂ  šÂ    class-wc-countries.phpnu „[µü¤        get_countries();
		} elseif ( 'states' === $key ) {
			return $this->get_states();
		} elseif ( 'continents' === $key ) {
			return $this->get_continents();
		}
	}
	/**
	 * Get all countries.
	 *
	 * @return array
	 */
	public function get_countries() {
		if ( empty( $this->geo_cache['countries'] ) ) {
			/**
			 * Allows filtering of the list of countries in WC.
			 *
			 * @since 1.5.3
			 *
			 * @param array $countries
			 */
			$this->geo_cache['countries'] = apply_filters( 'woocommerce_countries', include WC()->plugin_path() . '/i18n/countries.php' );
			if ( apply_filters( 'woocommerce_sort_countries', true ) ) {
				wc_asort_by_locale( $this->geo_cache['countries'] );
			}
		}
		return $this->geo_cache['countries'];
	}
	/**
	 * Check if a given code represents a valid ISO 3166-1 alpha-2 code for a country known to us.
	 *
	 * @since 5.1.0
	 * @param string $country_code The country code to check as a ISO 3166-1 alpha-2 code.
	 * @return bool True if the country is known to us, false otherwise.
	 */
	public function country_exists( $country_code ) {
		return isset( $this->get_countries()[ $country_code ] );
	}
	/**
	 * Get all continents.
	 *
	 * @return array
	 */
	public function get_continents() {
		if ( empty( $this->geo_cache['continents'] ) ) {
			/**
			 * Allows filtering of continents in WC.
			 *
			 * @since 2.6.0
			 *
			 * @param array[array] $continents
			 */
			$this->geo_cache['continents'] = apply_filters( 'woocommerce_continents', include WC()->plugin_path() . '/i18n/continents.php' );
		}
		return $this->geo_cache['continents'];
	}
	/**
	 * Get continent code for a country code.
	 *
	 * @since 2.6.0
	 * @param string $cc Country code.
	 * @return string
	 */
	public function get_continent_code_for_country( $cc ) {
		$cc                 = trim( strtoupper( $cc ) );
		$continents         = $this->get_continents();
		$continents_and_ccs = wp_list_pluck( $continents, 'countries' );
		foreach ( $continents_and_ccs as $continent_code => $countries ) {
			if ( false !== array_search( $cc, $countries, true ) ) {
				return $continent_code;
			}
		}
		return '';
	}
	/**
	 * Get calling code for a country code.
	 *
	 * @since 3.6.0
	 * @param string $cc Country code.
	 * @return string|array Some countries have multiple. The code will be stripped of - and spaces and always be prefixed with +.
	 */
	public function get_country_calling_code( $cc ) {
		$codes = wp_cache_get( 'calling-codes', 'countries' );
		if ( ! $codes ) {
			$codes = include WC()->plugin_path() . '/i18n/phone.php';
			wp_cache_set( 'calling-codes', $codes, 'countries' );
		}
		$calling_code = isset( $codes[ $cc ] ) ? $codes[ $cc ] : '';
		if ( is_array( $calling_code ) ) {
			$calling_code = $calling_code[0];
		}
		return $calling_code;
	}
	/**
	 * Get continents that the store ships to.
	 *
	 * @since 3.6.0
	 * @return array
	 */
	public function get_shipping_continents() {
		$continents             = $this->get_continents();
		$shipping_countries     = $this->get_shipping_countries();
		$shipping_country_codes = array_keys( $shipping_countries );
		$shipping_continents    = array();
		foreach ( $continents as $continent_code => $continent ) {
			if ( count( array_intersect( $continent['countries'], $shipping_country_codes ) ) ) {
				$shipping_continents[ $continent_code ] = $continent;
			}
		}
		return $shipping_continents;
	}
	/**
	 * Load the states.
	 *
	 * @deprecated 3.6.0 This method was used to load state files, but is no longer needed. @see get_states().
	 */
	public function load_country_states() {
		global $states;
		$states = include WC()->plugin_path() . '/i18n/states.php';
		/**
		 * Allows filtering of country states in WC.
		 *
		 * @since 1.5.3
		 *
		 * @param array $states
		 */
		$this->geo_cache['states'] = apply_filters( 'woocommerce_states', $states );
	}
	/**
	 * Get the states for a country.
	 *
	 * @param  string $cc Country code.
	 * @return false|array of states
	 */
	public function get_states( $cc = null ) {
		if ( ! isset( $this->geo_cache['states'] ) ) {
			/**
			 * Allows filtering of country states in WC.
			 *
			 * @since 1.5.3
			 *
			 * @param array $states
			 */
			$this->geo_cache['states'] = apply_filters( 'woocommerce_states', include WC()->plugin_path() . '/i18n/states.php' );
		}
		if ( ! is_null( $cc ) ) {
			return isset( $this->geo_cache['states'][ $cc ] ) ? $this->geo_cache['states'][ $cc ] : false;
		} else {
			return $this->geo_cache['states'];
		}
	}
	/**
	 * Get the base address (first line) for the store.
	 *
	 * @since 3.1.1
	 * @return string
	 */
	public function get_base_address() {
		$base_address = get_option( 'woocommerce_store_address', '' );
		return apply_filters( 'woocommerce_countries_base_address', $base_address );
	}
	/**
	 * Get the base address (second line) for the store.
	 *
	 * @since 3.1.1
	 * @return string
	 */
	public function get_base_address_2() {
		$base_address_2 = get_option( 'woocommerce_store_address_2', '' );
		return apply_filters( 'woocommerce_countries_base_address_2', $base_address_2 );
	}
	/**
	 * Get the base country for the store.
	 *
	 * @return string
	 */
	public function get_base_country() {
		$default = wc_get_base_location();
		return apply_filters( 'woocommerce_countries_base_country', $default['country'] );
	}
	/**
	 * Get the base state for the store.
	 *
	 * @return string
	 */
	public function get_base_state() {
		$default = wc_get_base_location();
		return apply_filters( 'woocommerce_countries_base_state', $default['state'] );
	}
	/**
	 * Get the base city for the store.
	 *
	 * @version 3.1.1
	 * @return string
	 */
	public function get_base_city() {
		$base_city = get_option( 'woocommerce_store_city', '' );
		return apply_filters( 'woocommerce_countries_base_city', $base_city );
	}
	/**
	 * Get the base postcode for the store.
	 *
	 * @since 3.1.1
	 * @return string
	 */
	public function get_base_postcode() {
		$base_postcode = get_option( 'woocommerce_store_postcode', '' );
		return apply_filters( 'woocommerce_countries_base_postcode', $base_postcode );
	}
	/**
	 * Get countries that the store sells to.
	 *
	 * @return array
	 */
	public function get_allowed_countries() {
		if ( 'all' === get_option( 'woocommerce_allowed_countries' ) ) {
			return apply_filters( 'woocommerce_countries_allowed_countries', $this->countries );
		}
		if ( 'all_except' === get_option( 'woocommerce_allowed_countries' ) ) {
			$except_countries = get_option( 'woocommerce_all_except_countries', array() );
			if ( ! $except_countries ) {
				return $this->countries;
			} else {
				$all_except_countries = $this->countries;
				foreach ( $except_countries as $country ) {
					unset( $all_except_countries[ $country ] );
				}
				return apply_filters( 'woocommerce_countries_allowed_countries', $all_except_countries );
			}
		}
		$countries = array();
		$raw_countries = get_option( 'woocommerce_specific_allowed_countries', array() );
		if ( $raw_countries ) {
			foreach ( $raw_countries as $country ) {
				$countries[ $country ] = $this->countries[ $country ];
			}
		}
		return apply_filters( 'woocommerce_countries_allowed_countries', $countries );
	}
	/**
	 * Get countries that the store ships to.
	 *
	 * @return array
	 */
	public function get_shipping_countries() {
		if ( '' === get_option( 'woocommerce_ship_to_countries' ) ) {
			return $this->get_allowed_countries();
		}
		if ( 'all' === get_option( 'woocommerce_ship_to_countries' ) ) {
			return $this->countries;
		}
		$countries = array();
		$raw_countries = get_option( 'woocommerce_specific_ship_to_countries' );
		if ( $raw_countries ) {
			foreach ( $raw_countries as $country ) {
				$countries[ $country ] = $this->countries[ $country ];
			}
		}
		return apply_filters( 'woocommerce_countries_shipping_countries', $countries );
	}
	/**
	 * Get allowed country states.
	 *
	 * @return array
	 */
	public function get_allowed_country_states() {
		if ( get_option( 'woocommerce_allowed_countries' ) !== 'specific' ) {
			return $this->states;
		}
		$states = array();
		$raw_countries = get_option( 'woocommerce_specific_allowed_countries' );
		if ( $raw_countries ) {
			foreach ( $raw_countries as $country ) {
				if ( isset( $this->states[ $country ] ) ) {
					$states[ $country ] = $this->states[ $country ];
				}
			}
		}
		return apply_filters( 'woocommerce_countries_allowed_country_states', $states );
	}
	/**
	 * Get shipping country states.
	 *
	 * @return array
	 */
	public function get_shipping_country_states() {
		if ( get_option( 'woocommerce_ship_to_countries' ) === '' ) {
			return $this->get_allowed_country_states();
		}
		if ( get_option( 'woocommerce_ship_to_countries' ) !== 'specific' ) {
			return $this->states;
		}
		$states = array();
		$raw_countries = get_option( 'woocommerce_specific_ship_to_countries' );
		if ( $raw_countries ) {
			foreach ( $raw_countries as $country ) {
				if ( ! empty( $this->states[ $country ] ) ) {
					$states[ $country ] = $this->states[ $country ];
				}
			}
		}
		return apply_filters( 'woocommerce_countries_shipping_country_states', $states );
	}
	/**
	 * Gets an array of countries in the EU.
	 *
	 * @param  string $type Type of countries to retrieve. Blank for EU member countries. eu_vat for EU VAT countries.
	 * @return string[]
	 */
	public function get_european_union_countries( $type = '' ) {
		$countries = array( 'AT', 'BE', 'BG', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GR', 'HR', 'HU', 'IE', 'IT', 'LT', 'LU', 'LV', 'MT', 'NL', 'PL', 'PT', 'RO', 'SE', 'SI', 'SK' );
		if ( 'eu_vat' === $type ) {
			$countries[] = 'MC';
		}
		return apply_filters( 'woocommerce_european_union_countries', $countries, $type );
	}
	/**
	 * Gets an array of Non-EU countries that use VAT as the Local name for their taxes based on this list - https://en.wikipedia.org/wiki/Value-added_tax#Non-European_Union_countries
	 *
	 * @deprecated 4.0.0
	 * @since 3.9.0
	 * @return string[]
	 */
	public function countries_using_vat() {
		wc_deprecated_function( 'countries_using_vat', '4.0', 'WC_Countries::get_vat_countries' );
		$countries = array( 'AE', 'AL', 'AR', 'AZ', 'BB', 'BH', 'BO', 'BS', 'BY', 'CL', 'CO', 'EC', 'EG', 'ET', 'FJ', 'GH', 'GM', 'GT', 'IL', 'IN', 'IR', 'KN', 'KR', 'KZ', 'LK', 'MD', 'ME', 'MK', 'MN', 'MU', 'MX', 'NA', 'NG', 'NP', 'PS', 'PY', 'RS', 'RU', 'RW', 'SA', 'SV', 'TH', 'TR', 'UA', 'UY', 'UZ', 'VE', 'VN', 'ZA' );
		return apply_filters( 'woocommerce_countries_using_vat', $countries );
	}
	/**
	 * Gets an array of countries using VAT.
	 *
	 * @since 4.0.0
	 * @return string[] of country codes.
	 */
	public function get_vat_countries() {
		$eu_countries  = $this->get_european_union_countries();
		$vat_countries = array( 'AE', 'AL', 'AR', 'AZ', 'BB', 'BH', 'BO', 'BS', 'BY', 'CL', 'CO', 'EC', 'EG', 'ET', 'FJ', 'GB', 'GH', 'GM', 'GT', 'IL', 'IM', 'IN', 'IR', 'KN', 'KR', 'KZ', 'LK', 'MC', 'MD', 'ME', 'MK', 'MN', 'MU', 'MX', 'NA', 'NG', 'NO', 'NP', 'PS', 'PY', 'RS', 'RU', 'RW', 'SA', 'SV', 'TH', 'TR', 'UA', 'UY', 'UZ', 'VE', 'VN', 'ZA' );
		return apply_filters( 'woocommerce_vat_countries', array_merge( $eu_countries, $vat_countries ) );
	}
	/**
	 * Gets the correct string for shipping - either 'to the' or 'to'.
	 *
	 * @param string $country_code Country code.
	 * @return string
	 */
	public function shipping_to_prefix( $country_code = '' ) {
		$country_code = $country_code ? $country_code : WC()->customer->get_shipping_country();
		$countries    = array( 'AE', 'CZ', 'DO', 'GB', 'NL', 'PH', 'US', 'USAF' );
		$return       = in_array( $country_code, $countries, true ) ? _x( 'to the', 'shipping country prefix', 'woocommerce' ) : _x( 'to', 'shipping country prefix', 'woocommerce' );
		return apply_filters( 'woocommerce_countries_shipping_to_prefix', $return, $country_code );
	}
	/**
	 * Prefix certain countries with 'the'.
	 *
	 * @param string $country_code Country code.
	 * @return string
	 */
	public function estimated_for_prefix( $country_code = '' ) {
		$country_code = $country_code ? $country_code : $this->get_base_country();
		$countries    = array( 'AE', 'CZ', 'DO', 'GB', 'NL', 'PH', 'US', 'USAF' );
		$return       = in_array( $country_code, $countries, true ) ? __( 'the', 'woocommerce' ) . ' ' : '';
		return apply_filters( 'woocommerce_countries_estimated_for_prefix', $return, $country_code );
	}
	/**
	 * Correctly name tax in some countries VAT on the frontend.
	 *
	 * @return string
	 */
	public function tax_or_vat() {
		$return = in_array( $this->get_base_country(), $this->get_vat_countries(), true ) ? __( 'VAT', 'woocommerce' ) : __( 'Tax', 'woocommerce' );
		return apply_filters( 'woocommerce_countries_tax_or_vat', $return );
	}
	/**
	 * Include the Inc Tax label.
	 *
	 * @return string
	 */
	public function inc_tax_or_vat() {
		$return = in_array( $this->get_base_country(), $this->get_vat_countries(), true ) ? __( '(incl. VAT)', 'woocommerce' ) : __( '(incl. tax)', 'woocommerce' );
		return apply_filters( 'woocommerce_countries_inc_tax_or_vat', $return );
	}
	/**
	 * Include the Ex Tax label.
	 *
	 * @return string
	 */
	public function ex_tax_or_vat() {
		$return = in_array( $this->get_base_country(), $this->get_vat_countries(), true ) ? __( '(ex. VAT)', 'woocommerce' ) : __( '(ex. tax)', 'woocommerce' );
		return apply_filters( 'woocommerce_countries_ex_tax_or_vat', $return );
	}
	/**
	 * Outputs the list of countries and states for use in dropdown boxes.
	 *
	 * @param string $selected_country Selected country.
	 * @param string $selected_state   Selected state.
	 * @param bool   $escape           If we should escape HTML.
	 */
	public function country_dropdown_options( $selected_country = '', $selected_state = '', $escape = false ) {
		if ( $this->countries ) {
			foreach ( $this->countries as $key => $value ) {
				$states = $this->get_states( $key );
				if ( $states ) {
					// Maybe default the selected state as the first one.
					if ( $selected_country === $key && '*' === $selected_state ) {
						$selected_state = key( $states ) ?? '*';
					}
					echo '';
				} else {
					echo ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
				}
			}
		}
	}
	/**
	 * Get country address formats.
	 *
	 * These define how addresses are formatted for display in various countries.
	 *
	 * @return array
	 */
	public function get_address_formats() {
		if ( empty( $this->address_formats ) ) {
			$this->address_formats = apply_filters(
				'woocommerce_localisation_address_formats',
				array(
					'default' => "{name}\n{company}\n{address_1}\n{address_2}\n{city}\n{state}\n{postcode}\n{country}",
					'AT'      => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
					'AU'      => "{name}\n{company}\n{address_1}\n{address_2}\n{city} {state} {postcode}\n{country}",
					'BE'      => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
					'CA'      => "{company}\n{name}\n{address_1}\n{address_2}\n{city} {state_code} {postcode}\n{country}",
					'CH'      => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
					'CL'      => "{company}\n{name}\n{address_1}\n{address_2}\n{state}\n{postcode} {city}\n{country}",
					'CN'      => "{country} {postcode}\n{state}, {city}, {address_2}, {address_1}\n{company}\n{name}",
					'CZ'      => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
					'DE'      => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
					'DK'      => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
					'EE'      => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
					'ES'      => "{name}\n{company}\n{address_1}\n{address_2}\n{postcode} {city}\n{state}\n{country}",
					'FI'      => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
					'FR'      => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city_upper}\n{country}",
					'HK'      => "{company}\n{first_name} {last_name_upper}\n{address_1}\n{address_2}\n{city_upper}\n{state_upper}\n{country}",
					'HU'      => "{last_name} {first_name}\n{company}\n{city}\n{address_1}\n{address_2}\n{postcode}\n{country}",
					'IN'      => "{company}\n{name}\n{address_1}\n{address_2}\n{city} {postcode}\n{state}, {country}",
					'IS'      => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
					'IT'      => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode}\n{city}\n{state_upper}\n{country}",
					'JM'      => "{name}\n{company}\n{address_1}\n{address_2}\n{city}\n{state}\n{postcode_upper}\n{country}",
					'JP'      => "{postcode}\n{state} {city} {address_1}\n{address_2}\n{company}\n{last_name} {first_name}\n{country}",
					'LI'      => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
					'NL'      => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
					'NO'      => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
					'NZ'      => "{name}\n{company}\n{address_1}\n{address_2}\n{city} {postcode}\n{country}",
					'PL'      => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
					'PR'      => "{company}\n{name}\n{address_1} {address_2}\n{city} \n{country} {postcode}",
					'PT'      => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
					'RS'      => "{name}\n{company}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
					'SE'      => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
					'SI'      => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
					'SK'      => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
					'TR'      => "{name}\n{company}\n{address_1}\n{address_2}\n{postcode} {city} {state}\n{country}",
					'TW'      => "{company}\n{last_name} {first_name}\n{address_1}\n{address_2}\n{state}, {city} {postcode}\n{country}",
					'UG'      => "{name}\n{company}\n{address_1}\n{address_2}\n{city}\n{state}, {country}",
					'US'      => "{name}\n{company}\n{address_1}\n{address_2}\n{city}, {state_code} {postcode}\n{country}",
					'VN'      => "{name}\n{company}\n{address_1}\n{address_2}\n{city} {postcode}\n{country}",
				)
			);
		}
		return $this->address_formats;
	}
	/**
	 * Get country address format.
	 *
	 * @param  array  $args Arguments.
	 * @param  string $separator How to separate address lines. @since 3.5.0.
	 * @return string
	 */
	public function get_formatted_address( $args = array(), $separator = '
' ) {
		$default_args = array(
			'first_name' => '',
			'last_name'  => '',
			'company'    => '',
			'address_1'  => '',
			'address_2'  => '',
			'city'       => '',
			'state'      => '',
			'postcode'   => '',
			'country'    => '',
		);
		$args    = array_map( 'trim', wp_parse_args( $args, $default_args ) );
		$state   = $args['state'];
		$country = $args['country'];
		// Get all formats.
		$formats = $this->get_address_formats();
		// Get format for the address' country.
		$format = ( $country && isset( $formats[ $country ] ) ) ? $formats[ $country ] : $formats['default'];
		// Handle full country name.
		$full_country = ( isset( $this->countries[ $country ] ) ) ? $this->countries[ $country ] : $country;
		// Country is not needed if the same as base.
		if ( $country === $this->get_base_country() && ! apply_filters( 'woocommerce_formatted_address_force_country_display', false ) ) {
			$format = str_replace( '{country}', '', $format );
		}
		// Handle full state name.
		$full_state = ( $country && $state && isset( $this->states[ $country ][ $state ] ) ) ? $this->states[ $country ][ $state ] : $state;
		// Substitute address parts into the string.
		$replace = array_map(
			'esc_html',
			apply_filters(
				'woocommerce_formatted_address_replacements',
				array(
					'{first_name}'       => $args['first_name'],
					'{last_name}'        => $args['last_name'],
					'{name}'             => sprintf(
						/* translators: 1: first name 2: last name */
						_x( '%1$s %2$s', 'full name', 'woocommerce' ),
						$args['first_name'],
						$args['last_name']
					),
					'{company}'          => $args['company'],
					'{address_1}'        => $args['address_1'],
					'{address_2}'        => $args['address_2'],
					'{city}'             => $args['city'],
					'{state}'            => $full_state,
					'{postcode}'         => $args['postcode'],
					'{country}'          => $full_country,
					'{first_name_upper}' => wc_strtoupper( $args['first_name'] ),
					'{last_name_upper}'  => wc_strtoupper( $args['last_name'] ),
					'{name_upper}'       => wc_strtoupper(
						sprintf(
							/* translators: 1: first name 2: last name */
							_x( '%1$s %2$s', 'full name', 'woocommerce' ),
							$args['first_name'],
							$args['last_name']
						)
					),
					'{company_upper}'    => wc_strtoupper( $args['company'] ),
					'{address_1_upper}'  => wc_strtoupper( $args['address_1'] ),
					'{address_2_upper}'  => wc_strtoupper( $args['address_2'] ),
					'{city_upper}'       => wc_strtoupper( $args['city'] ),
					'{state_upper}'      => wc_strtoupper( $full_state ),
					'{state_code}'       => wc_strtoupper( $state ),
					'{postcode_upper}'   => wc_strtoupper( $args['postcode'] ),
					'{country_upper}'    => wc_strtoupper( $full_country ),
				),
				$args
			)
		);
		$formatted_address = str_replace( array_keys( $replace ), $replace, $format );
		// Clean up white space.
		$formatted_address = preg_replace( '/  +/', ' ', trim( $formatted_address ) );
		$formatted_address = preg_replace( '/\n\n+/', "\n", $formatted_address );
		// Break newlines apart and remove empty lines/trim commas and white space.
		$formatted_address = array_filter( array_map( array( $this, 'trim_formatted_address_line' ), explode( "\n", $formatted_address ) ) );
		// Add html breaks.
		$formatted_address = implode( $separator, $formatted_address );
		// We're done!
		return $formatted_address;
	}
	/**
	 * Trim white space and commas off a line.
	 *
	 * @param  string $line Line.
	 * @return string
	 */
	private function trim_formatted_address_line( $line ) {
		return trim( $line, ', ' );
	}
	/**
	 * Returns the fields we show by default. This can be filtered later on.
	 *
	 * @return array
	 */
	public function get_default_address_fields() {
		$address_2_label = __( 'Apartment, suite, unit, etc.', 'woocommerce' );
		// If necessary, append '(optional)' to the placeholder: we don't need to worry about the
		// label, though, as woocommerce_form_field() takes care of that.
		if ( 'optional' === get_option( 'woocommerce_checkout_address_2_field', 'optional' ) ) {
			$address_2_placeholder = __( 'Apartment, suite, unit, etc. (optional)', 'woocommerce' );
		} else {
			$address_2_placeholder = $address_2_label;
		}
		$fields = array(
			'first_name' => array(
				'label'        => __( 'First name', 'woocommerce' ),
				'required'     => true,
				'class'        => array( 'form-row-first' ),
				'autocomplete' => 'given-name',
				'priority'     => 10,
			),
			'last_name'  => array(
				'label'        => __( 'Last name', 'woocommerce' ),
				'required'     => true,
				'class'        => array( 'form-row-last' ),
				'autocomplete' => 'family-name',
				'priority'     => 20,
			),
			'company'    => array(
				'label'        => __( 'Company name', 'woocommerce' ),
				'class'        => array( 'form-row-wide' ),
				'autocomplete' => 'organization',
				'priority'     => 30,
				'required'     => 'required' === get_option( 'woocommerce_checkout_company_field', 'optional' ),
			),
			'country'    => array(
				'type'         => 'country',
				'label'        => __( 'Country / Region', 'woocommerce' ),
				'required'     => true,
				'class'        => array( 'form-row-wide', 'address-field', 'update_totals_on_change' ),
				'autocomplete' => 'country',
				'priority'     => 40,
			),
			'address_1'  => array(
				'label'        => __( 'Street address', 'woocommerce' ),
				/* translators: use local order of street name and house number. */
				'placeholder'  => esc_attr__( 'House number and street name', 'woocommerce' ),
				'required'     => true,
				'class'        => array( 'form-row-wide', 'address-field' ),
				'autocomplete' => 'address-line1',
				'priority'     => 50,
			),
			'address_2'  => array(
				'label'        => $address_2_label,
				'label_class'  => array( 'screen-reader-text' ),
				'placeholder'  => esc_attr( $address_2_placeholder ),
				'class'        => array( 'form-row-wide', 'address-field' ),
				'autocomplete' => 'address-line2',
				'priority'     => 60,
				'required'     => 'required' === get_option( 'woocommerce_checkout_address_2_field', 'optional' ),
			),
			'city'       => array(
				'label'        => __( 'Town / City', 'woocommerce' ),
				'required'     => true,
				'class'        => array( 'form-row-wide', 'address-field' ),
				'autocomplete' => 'address-level2',
				'priority'     => 70,
			),
			'state'      => array(
				'type'         => 'state',
				'label'        => __( 'State / County', 'woocommerce' ),
				'required'     => true,
				'class'        => array( 'form-row-wide', 'address-field' ),
				'validate'     => array( 'state' ),
				'autocomplete' => 'address-level1',
				'priority'     => 80,
			),
			'postcode'   => array(
				'label'        => __( 'Postcode / ZIP', 'woocommerce' ),
				'required'     => true,
				'class'        => array( 'form-row-wide', 'address-field' ),
				'validate'     => array( 'postcode' ),
				'autocomplete' => 'postal-code',
				'priority'     => 90,
			),
		);
		if ( 'hidden' === get_option( 'woocommerce_checkout_company_field', 'optional' ) ) {
			unset( $fields['company'] );
		}
		if ( 'hidden' === get_option( 'woocommerce_checkout_address_2_field', 'optional' ) ) {
			unset( $fields['address_2'] );
		}
		$default_address_fields = apply_filters( 'woocommerce_default_address_fields', $fields );
		// Sort each of the fields based on priority.
		uasort( $default_address_fields, 'wc_checkout_fields_uasort_comparison' );
		return $default_address_fields;
	}
	/**
	 * Get JS selectors for fields which are shown/hidden depending on the locale.
	 *
	 * @return array
	 */
	public function get_country_locale_field_selectors() {
		$locale_fields = array(
			'address_1' => '#billing_address_1_field, #shipping_address_1_field',
			'address_2' => '#billing_address_2_field, #shipping_address_2_field',
			'state'     => '#billing_state_field, #shipping_state_field, #calc_shipping_state_field',
			'postcode'  => '#billing_postcode_field, #shipping_postcode_field, #calc_shipping_postcode_field',
			'city'      => '#billing_city_field, #shipping_city_field, #calc_shipping_city_field',
		);
		return apply_filters( 'woocommerce_country_locale_field_selectors', $locale_fields );
	}
	/**
	 * Get country locale settings.
	 *
	 * These locales override the default country selections after a country is chosen.
	 *
	 * @return array
	 */
	public function get_country_locale() {
		if ( empty( $this->locale ) ) {
			$this->locale = apply_filters(
				'woocommerce_get_country_locale',
				array(
					'AE' => array(
						'postcode' => array(
							'required' => false,
							'hidden'   => true,
						),
						'state'    => array(
							'required' => false,
						),
					),
					'AF' => array(
						'state' => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'AL' => array(
						'state'    => array(
							'label' => __( 'County', 'woocommerce' ),
						),
					),
					'AO' => array(
						'postcode' => array(
							'required' => false,
							'hidden'   => true,
						),
						'state'    => array(
							'label' => __( 'Province', 'woocommerce' ),
						),
					),
					'AT' => array(
						'postcode' => array(
							'priority' => 65,
						),
						'state'    => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'AU' => array(
						'city'     => array(
							'label' => __( 'Suburb', 'woocommerce' ),
						),
						'postcode' => array(
							'label' => __( 'Postcode', 'woocommerce' ),
						),
						'state'    => array(
							'label' => __( 'State', 'woocommerce' ),
						),
					),
					'AX' => array(
						'postcode' => array(
							'priority' => 65,
						),
						'state'    => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'BA' => array(
						'postcode' => array(
							'priority' => 65,
						),
						'state'    => array(
							'label'    => __( 'Canton', 'woocommerce' ),
							'required' => false,
							'hidden'   => true,
						),
					),
					'BD' => array(
						'postcode' => array(
							'required' => false,
						),
						'state'    => array(
							'label' => __( 'District', 'woocommerce' ),
						),
					),
					'BE' => array(
						'postcode' => array(
							'priority' => 65,
						),
						'state'    => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'BG' => array(
						'state' => array(
							'required' => false,
						),
					),
					'BH' => array(
						'postcode' => array(
							'required' => false,
						),
						'state'    => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'BI' => array(
						'state' => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'BO' => array(
						'postcode' => array(
							'required' => false,
							'hidden'   => true,
						),
						'state'    => array(
							'label' => __( 'Department', 'woocommerce' ),
						),
					),
					'BS' => array(
						'postcode' => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'BZ' => array(
						'postcode' => array(
							'required' => false,
							'hidden'   => true,
						),
						'state' 	=> array(
							'required' => false,
						),
					),
					'CA' => array(
						'postcode' => array(
							'label' => __( 'Postal code', 'woocommerce' ),
						),
						'state'    => array(
							'label' => __( 'Province', 'woocommerce' ),
						),
					),
					'CH' => array(
						'postcode' => array(
							'priority' => 65,
						),
						'state'    => array(
							'label'    => __( 'Canton', 'woocommerce' ),
							'required' => false,
						),
					),
					'CL' => array(
						'city'     => array(
							'required' => true,
						),
						'postcode' => array(
							'required' => false,
							// Hidden for stores within Chile. @see https://github.com/woocommerce/woocommerce/issues/36546.
							'hidden'   => 'CL' === $this->get_base_country(),
						),
						'state'    => array(
							'label' => __( 'Region', 'woocommerce' ),
						),
					),
					'CN' => array(
						'state' => array(
							'label' => __( 'Province', 'woocommerce' ),
						),
					),
					'CO' => array(
						'postcode' => array(
							'required' => false,
						),
						'state' => array(
							'label' => __( 'Department', 'woocommerce' ),
						),
					),
					'CR' => array(
						'state' => array(
							'label' => __( 'Province', 'woocommerce' ),
						),
					),
					'CW' => array(
						'postcode' => array(
							'required' => false,
							'hidden'   => true,
						),
						'state'    => array(
							'required' => false,
						),
					),
					'CZ' => array(
						'state' => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'DE' => array(
						'postcode' => array(
							'priority' => 65,
						),
						'state'    => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'DK' => array(
						'postcode' => array(
							'priority' => 65,
						),
						'state'    => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'DO' => array(
						'state'    => array(
							'label' => __( 'Province', 'woocommerce' ),
						),
					),
					'EC' => array(
						'state'    => array(
							'label' => __( 'Province', 'woocommerce' ),
						),
					),
					'EE' => array(
						'postcode' => array(
							'priority' => 65,
						),
						'state'    => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'ET' => array(
						'state' => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'FI' => array(
						'postcode' => array(
							'priority' => 65,
						),
						'state'    => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'FR' => array(
						'postcode' => array(
							'priority' => 65,
						),
						'state'    => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'GG' => array(
 						'state' => array(
 							'required' => false,
 							'label' => __( 'Parish', 'woocommerce' ),
 						),
 					),
					'GH' => array(
						'postcode' => array(
							'required' => false,
						),
						'state'    => array(
							'label' => __( 'Region', 'woocommerce' ),
						),
					),
					'GP' => array(
						'state' => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'GF' => array(
						'state' => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'GR' => array(
						'state' => array(
							'required' => false,
						),
					),
					'GT' => array(
						'postcode' => array(
							'required' => false,
						),
						'state'    => array(
							'label' => __( 'Department', 'woocommerce' ),
						),
					),
					'HK' => array(
						'postcode' => array(
							'required' => false,
						),
						'city'     => array(
							'label' => __( 'Town / District', 'woocommerce' ),
						),
						'state'    => array(
							'label' => __( 'Region', 'woocommerce' ),
						),
					),
					'HN' => array(
						'state'    => array(
							'label' => __( 'Department', 'woocommerce' ),
						),
					),
					'HU' => array(
						'last_name'  => array(
							'class'    => array( 'form-row-first' ),
							'priority' => 10,
						),
						'first_name' => array(
							'class'    => array( 'form-row-last' ),
							'priority' => 20,
						),
						'postcode'   => array(
							'class'    => array( 'form-row-first', 'address-field' ),
							'priority' => 65,
						),
						'city'       => array(
							'class' => array( 'form-row-last', 'address-field' ),
						),
						'address_1'  => array(
							'priority' => 71,
						),
						'address_2'  => array(
							'priority' => 72,
						),
						'state'      => array(
							'label'    => __( 'County', 'woocommerce' ),
							'required' => false,
						),
					),
					'ID' => array(
						'state' => array(
							'label' => __( 'Province', 'woocommerce' ),
						),
					),
					'IE' => array(
						'postcode' => array(
							'required' => false,
							'label'    => __( 'Eircode', 'woocommerce' ),
						),
						'state'    => array(
							'label' => __( 'County', 'woocommerce' ),
						),
					),
					'IS' => array(
						'postcode' => array(
							'priority' => 65,
						),
						'state'    => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'IL' => array(
						'postcode' => array(
							'priority' => 65,
						),
						'state'    => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'IM' => array(
						'state' => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'IN' => array(
						'postcode' => array(
							'label' => __( 'PIN Code', 'woocommerce' ),
						),
						'state'    => array(
							'label' => __( 'State', 'woocommerce' ),
						),
					),
					'IR' => array(
						'state'     => array(
							'priority' => 50,
						),
						'city'      => array(
							'priority' => 60,
						),
						'address_1' => array(
							'priority' => 70,
						),
						'address_2' => array(
							'priority' => 80,
						),
					),
					'IT' => array(
						'postcode' => array(
							'priority' => 65,
						),
						'state'    => array(
							'required' => true,
							'label'    => __( 'Province', 'woocommerce' ),
						),
					),
					'JM' => array(
						'city'     => array(
							'label' => __( 'Town / City / Post Office', 'woocommerce' ),
						),
						'postcode' => array(
							'required' => false,
							'label'    => __( 'Postal Code', 'woocommerce' ),
						),
						'state'    => array(
							'required' => true,
							'label'    => __( 'Parish', 'woocommerce' ),
						),
					),
					'JP' => array(
						'last_name'  => array(
							'class'    => array( 'form-row-first' ),
							'priority' => 10,
						),
						'first_name' => array(
							'class'    => array( 'form-row-last' ),
							'priority' => 20,
						),
						'postcode'   => array(
							'class'    => array( 'form-row-first', 'address-field' ),
							'priority' => 65,
						),
						'state'      => array(
							'label'    => __( 'Prefecture', 'woocommerce' ),
							'class'    => array( 'form-row-last', 'address-field' ),
							'priority' => 66,
						),
						'city'       => array(
							'priority' => 67,
						),
						'address_1'  => array(
							'priority' => 68,
						),
						'address_2'  => array(
							'priority' => 69,
						),
					),
					'KN' => array(
						'postcode' => array(
							'required' => false,
							'label'    => __( 'Postal code', 'woocommerce' ),
						),
						'state'    => array(
							'required' => true,
							'label'    => __( 'Parish', 'woocommerce' ),
						),
					),
					'KR' => array(
						'state' => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'KW' => array(
						'state' => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'LV' => array(
						'state' => array(
							'label'    => __( 'Municipality', 'woocommerce' ),
							'required' => false,
						),
					),
					'LB' => array(
						'state' => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'MF' => array(
						'state' => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'MQ' => array(
						'state' => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'MT' => array(
						'state' => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'MZ' => array(
						'postcode' => array(
							'required' => false,
							'hidden'   => true,
						),
						'state'    => array(
							'label' => __( 'Province', 'woocommerce' ),
						),
					),
					'NI' => array(
						'state'    => array(
							'label' => __( 'Department', 'woocommerce' ),
						),
					),
					'NL' => array(
						'postcode' => array(
							'priority' => 65,
						),
						'state'    => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'NG' => array(
						'postcode' => array(
							'label'    => __( 'Postcode', 'woocommerce' ),
							'required' => false,
							'hidden'   => true,
						),
						'state'    => array(
							'label' => __( 'State', 'woocommerce' ),
						),
					),
					'NZ' => array(
						'postcode' => array(
							'label' => __( 'Postcode', 'woocommerce' ),
						),
						'state'    => array(
							'required' => false,
							'label'    => __( 'Region', 'woocommerce' ),
						),
					),
					'NO' => array(
						'postcode' => array(
							'priority' => 65,
						),
						'state'    => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'NP' => array(
						'state'    => array(
							'label' => __( 'State / Zone', 'woocommerce' ),
						),
						'postcode' => array(
							'required' => false,
						),
					),
					'PA' => array(
						'state'    => array(
							'label' => __( 'Province', 'woocommerce' ),
						),
					),
					'PL' => array(
						'postcode' => array(
							'priority' => 65,
						),
						'state'    => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'PR' => array(
						'city'  => array(
							'label' => __( 'Municipality', 'woocommerce' ),
						),
						'state' => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'PT' => array(
						'state' => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'PY' => array(
						'state' => array(
							'label'    => __( 'Department', 'woocommerce' ),
						),
					),
					'RE' => array(
						'state' => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'RO' => array(
						'state' => array(
							'label'    => __( 'County', 'woocommerce' ),
							'required' => true,
						),
					),
					'RS' => array(
						'city'     => array(
							'required' => true,
						),
						'postcode' => array(
							'required' => true,
						),
						'state'    => array(
							'label'    => __( 'District', 'woocommerce' ),
							'required' => false,
						),
					),
					'RW' => array(
						'state' => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'SG' => array(
						'state' => array(
							'required' => false,
							'hidden'   => true,
						),
						'city'  => array(
							'required' => false,
						),
					),
					'SK' => array(
						'postcode' => array(
							'priority' => 65,
						),
						'state'    => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'SI' => array(
						'postcode' => array(
							'priority' => 65,
						),
						'state'    => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'SR' => array(
						'postcode' => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'SV' => array(
						'state'    => array(
							'label' => __( 'Department', 'woocommerce' ),
						),
					),
					'ES' => array(
						'postcode' => array(
							'priority' => 65,
						),
						'state'    => array(
							'label' => __( 'Province', 'woocommerce' ),
						),
					),
					'LI' => array(
						'postcode' => array(
							'priority' => 65,
						),
						'state'    => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'LK' => array(
						'state' => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'LU' => array(
						'state' => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'MD' => array(
						'state' => array(
							'label' => __( 'Municipality / District', 'woocommerce' ),
						),
					),
					'SE' => array(
						'postcode' => array(
							'priority' => 65,
						),
						'state'    => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'TR' => array(
						'postcode' => array(
							'priority' => 65,
						),
						'state'    => array(
							'label' => __( 'Province', 'woocommerce' ),
						),
					),
					'UG' => array(
						'postcode' => array(
							'required' => false,
							'hidden'   => true,
						),
						'city'     => array(
							'label'    => __( 'Town / Village', 'woocommerce' ),
							'required' => true,
						),
						'state'    => array(
							'label'    => __( 'District', 'woocommerce' ),
							'required' => true,
						),
					),
					'US' => array(
						'postcode' => array(
							'label' => __( 'ZIP Code', 'woocommerce' ),
						),
						'state'    => array(
							'label' => __( 'State', 'woocommerce' ),
						),
					),
					'UY' => array(
						'state'    => array(
							'label' => __( 'Department', 'woocommerce' ),
						),
					),
					'GB' => array(
						'postcode' => array(
							'label' => __( 'Postcode', 'woocommerce' ),
						),
						'state'    => array(
							'label'    => __( 'County', 'woocommerce' ),
							'required' => false,
						),
					),
					'ST' => array(
						'postcode' => array(
							'required' => false,
							'hidden'   => true,
						),
						'state'    => array(
							'label' => __( 'District', 'woocommerce' ),
						),
					),
					'VN' => array(
						'state'     => array(
							'required' => false,
							'hidden'   => true,
						),
						'postcode'  => array(
							'priority' => 65,
							'required' => false,
							'hidden'   => false,
						),
						'address_2' => array(
							'required' => false,
							'hidden'   => false,
						),
					),
					'WS' => array(
						'postcode' => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'YT' => array(
						'state' => array(
							'required' => false,
							'hidden'   => true,
						),
					),
					'ZA' => array(
						'state' => array(
							'label' => __( 'Province', 'woocommerce' ),
						),
					),
					'ZW' => array(
						'postcode' => array(
							'required' => false,
							'hidden'   => true,
						),
					),
				)
			);
			$this->locale = array_intersect_key( $this->locale, array_merge( $this->get_allowed_countries(), $this->get_shipping_countries() ) );
			// Default Locale Can be filtered to override fields in get_address_fields(). Countries with no specific locale will use default.
			$this->locale['default'] = apply_filters( 'woocommerce_get_country_locale_default', $this->get_default_address_fields() );
			// Filter default AND shop base locales to allow overrides via a single function. These will be used when changing countries on the checkout.
			if ( ! isset( $this->locale[ $this->get_base_country() ] ) ) {
				$this->locale[ $this->get_base_country() ] = $this->locale['default'];
			}
			$this->locale['default']                   = apply_filters( 'woocommerce_get_country_locale_base', $this->locale['default'] );
			$this->locale[ $this->get_base_country() ] = apply_filters( 'woocommerce_get_country_locale_base', $this->locale[ $this->get_base_country() ] );
		}
		return $this->locale;
	}
	/**
	 * Apply locale and get address fields.
	 *
	 * @param  mixed  $country Country.
	 * @param  string $type    Address type, defaults to 'billing_'.
	 * @return array
	 */
	public function get_address_fields( $country = '', $type = 'billing_' ) {
		if ( ! $country ) {
			$country = $this->get_base_country();
		}
		$fields = $this->get_default_address_fields();
		$locale = $this->get_country_locale();
		if ( isset( $locale[ $country ] ) ) {
			$fields = wc_array_overlay( $fields, $locale[ $country ] );
		}
		// Prepend field keys.
		$address_fields = array();
		foreach ( $fields as $key => $value ) {
			if ( 'state' === $key ) {
				$value['country_field'] = $type . 'country';
				$value['country']       = $country;
			}
			$address_fields[ $type . $key ] = $value;
		}
		// Add email and phone fields.
		if ( 'billing_' === $type ) {
			if ( 'hidden' !== get_option( 'woocommerce_checkout_phone_field', 'required' ) ) {
				$address_fields['billing_phone'] = array(
					'label'        => __( 'Phone', 'woocommerce' ),
					'required'     => 'required' === get_option( 'woocommerce_checkout_phone_field', 'required' ),
					'type'         => 'tel',
					'class'        => array( 'form-row-wide' ),
					'validate'     => array( 'phone' ),
					'autocomplete' => 'tel',
					'priority'     => 100,
				);
			}
			$address_fields['billing_email'] = array(
				'label'        => __( 'Email address', 'woocommerce' ),
				'required'     => true,
				'type'         => 'email',
				'class'        => array( 'form-row-wide' ),
				'validate'     => array( 'email' ),
				'autocomplete' => 'no' === get_option( 'woocommerce_registration_generate_username' ) ? 'email' : 'email username',
				'priority'     => 110,
			);
		}
		/**
		 * Important note on this filter: Changes to address fields can and will be overridden by
		 * the woocommerce_default_address_fields. The locales/default locales apply on top based
		 * on country selection. If you want to change things like the required status of an
		 * address field, filter woocommerce_default_address_fields instead.
		 */
		$address_fields = apply_filters( 'woocommerce_' . $type . 'fields', $address_fields, $country );
		// Sort each of the fields based on priority.
		uasort( $address_fields, 'wc_checkout_fields_uasort_comparison' );
		return $address_fields;
	}
}
PK     K[-kô  ô     class-wc-geolite-integration.phpnu „[µü¤        database = $database;
	}
	/**
	 * Get country 2-letters ISO by IP address.
	 * Returns empty string when not able to find any ISO code.
	 *
	 * @param string $ip_address User IP address.
	 * @return string
	 * @deprecated 3.9.0
	 */
	public function get_country_iso( $ip_address ) {
		wc_deprecated_function( 'get_country_iso', '3.9.0' );
		$iso_code = '';
		try {
			$reader = new MaxMind\Db\Reader( $this->database ); // phpcs:ignore PHPCompatibility.LanguageConstructs.NewLanguageConstructs.t_ns_separatorFound
			$data   = $reader->get( $ip_address );
			if ( isset( $data['country']['iso_code'] ) ) {
				$iso_code = $data['country']['iso_code'];
			}
			$reader->close();
		} catch ( Exception $e ) {
			$this->log( $e->getMessage(), 'warning' );
		}
		return sanitize_text_field( strtoupper( $iso_code ) );
	}
	/**
	 * Logging method.
	 *
	 * @param string $message Log message.
	 * @param string $level   Log level.
	 *                        Available options: 'emergency', 'alert',
	 *                        'critical', 'error', 'warning', 'notice',
	 *                        'info' and 'debug'.
	 *                        Defaults to 'info'.
	 */
	private function log( $message, $level = 'info' ) {
		if ( is_null( $this->log ) ) {
			$this->log = wc_get_logger();
		}
		$this->log->log( $level, $message, array( 'source' => 'geoip' ) );
	}
}
PK     K[ !A[ÿp  ÿp    class-wc-cart-totals.phpnu „[µü¤         0,
		'fees_total_tax'     => 0,
		'items_subtotal'     => 0,
		'items_subtotal_tax' => 0,
		'items_total'        => 0,
		'items_total_tax'    => 0,
		'total'              => 0,
		'shipping_total'     => 0,
		'shipping_tax_total' => 0,
		'discounts_total'    => 0,
	);
	/**
	 * Cache of tax rates for a given tax class.
	 *
	 * @var array
	 */
	protected $item_tax_rates;
	/**
	 * Sets up the items provided, and calculate totals.
	 *
	 * @since 3.2.0
	 * @throws Exception If missing WC_Cart object.
	 * @param WC_Cart $cart Cart object to calculate totals for.
	 */
	public function __construct( &$cart = null ) {
		if ( ! is_a( $cart, 'WC_Cart' ) ) {
			throw new Exception( 'A valid WC_Cart object is required' );
		}
		$this->cart          = $cart;
		$this->calculate_tax = wc_tax_enabled() && ! $cart->get_customer()->get_is_vat_exempt();
		$this->calculate();
	}
	/**
	 * Run all calculation methods on the given items in sequence.
	 *
	 * @since 3.2.0
	 */
	protected function calculate() {
		$this->calculate_item_totals();
		$this->calculate_shipping_totals();
		$this->calculate_fee_totals();
		$this->calculate_totals();
	}
	/**
	 * Get default blank set of props used per item.
	 *
	 * @since  3.2.0
	 * @return array
	 */
	protected function get_default_item_props() {
		return (object) array(
			'object'             => null,
			'tax_class'          => '',
			'taxable'            => false,
			'quantity'           => 0,
			'product'            => false,
			'price_includes_tax' => false,
			'subtotal'           => 0,
			'subtotal_tax'       => 0,
			'subtotal_taxes'     => array(),
			'total'              => 0,
			'total_tax'          => 0,
			'taxes'              => array(),
		);
	}
	/**
	 * Get default blank set of props used per fee.
	 *
	 * @since  3.2.0
	 * @return array
	 */
	protected function get_default_fee_props() {
		return (object) array(
			'object'    => null,
			'tax_class' => '',
			'taxable'   => false,
			'total_tax' => 0,
			'taxes'     => array(),
		);
	}
	/**
	 * Get default blank set of props used per shipping row.
	 *
	 * @since  3.2.0
	 * @return array
	 */
	protected function get_default_shipping_props() {
		return (object) array(
			'object'    => null,
			'tax_class' => '',
			'taxable'   => false,
			'total'     => 0,
			'total_tax' => 0,
			'taxes'     => array(),
		);
	}
	/**
	 * Handles a cart or order object passed in for calculation. Normalises data
	 * into the same format for use by this class.
	 *
	 * Each item is made up of the following props, in addition to those returned by get_default_item_props() for totals.
	 *  - key: An identifier for the item (cart item key or line item ID).
	 *  - cart_item: For carts, the cart item from the cart which may include custom data.
	 *  - quantity: The qty for this line.
	 *  - price: The line price in cents.
	 *  - product: The product object this cart item is for.
	 *
	 * @since 3.2.0
	 */
	protected function get_items_from_cart() {
		$this->items = array();
		foreach ( $this->cart->get_cart() as $cart_item_key => $cart_item ) {
			$item                          = $this->get_default_item_props();
			$item->key                     = $cart_item_key;
			$item->object                  = $cart_item;
			$item->tax_class               = $cart_item['data']->get_tax_class();
			$item->taxable                 = 'taxable' === $cart_item['data']->get_tax_status();
			$item->price_includes_tax      = wc_prices_include_tax();
			$item->quantity                = $cart_item['quantity'];
			$item->price                   = wc_add_number_precision_deep( (float) $cart_item['data']->get_price() * (float) $cart_item['quantity'] );
			$item->product                 = $cart_item['data'];
			$item->tax_rates               = $this->get_item_tax_rates( $item );
			$this->items[ $cart_item_key ] = $item;
		}
	}
	/**
	 * Get item costs grouped by tax class.
	 *
	 * @since  3.2.0
	 * @return array
	 */
	protected function get_tax_class_costs() {
		$item_tax_classes     = wp_list_pluck( $this->items, 'tax_class' );
		$shipping_tax_classes = wp_list_pluck( $this->shipping, 'tax_class' );
		$fee_tax_classes      = wp_list_pluck( $this->fees, 'tax_class' );
		$costs                = array_fill_keys( $item_tax_classes + $shipping_tax_classes + $fee_tax_classes, 0 );
		$costs['non-taxable'] = 0;
		foreach ( $this->items + $this->fees + $this->shipping as $item ) {
			if ( 0 > $item->total ) {
				continue;
			}
			if ( ! $item->taxable ) {
				$costs['non-taxable'] += $item->total;
			} elseif ( 'inherit' === $item->tax_class ) {
				$costs[ reset( $item_tax_classes ) ] += $item->total;
			} else {
				$costs[ $item->tax_class ] += $item->total;
			}
		}
		return array_filter( $costs );
	}
	/**
	 * Get fee objects from the cart. Normalises data
	 * into the same format for use by this class.
	 *
	 * @since 3.2.0
	 */
	protected function get_fees_from_cart() {
		$this->fees = array();
		$this->cart->calculate_fees();
		$fee_running_total = 0;
		foreach ( $this->cart->get_fees() as $fee_key => $fee_object ) {
			$fee            = $this->get_default_fee_props();
			$fee->object    = $fee_object;
			$fee->tax_class = $fee->object->tax_class;
			$fee->taxable   = $fee->object->taxable;
			$fee->total     = wc_add_number_precision_deep( $fee->object->amount );
			// Negative fees should not make the order total go negative.
			if ( 0 > $fee->total ) {
				$max_discount = NumberUtil::round( $this->get_total( 'items_total', true ) + $fee_running_total + $this->get_total( 'shipping_total', true ) ) * -1;
				if ( $fee->total < $max_discount ) {
					$fee->total = $max_discount;
				}
			}
			$fee_running_total += $fee->total;
			if ( $this->calculate_tax ) {
				if ( 0 > $fee->total ) {
					// Negative fees should have the taxes split between all items so it works as a true discount.
					$tax_class_costs = $this->get_tax_class_costs();
					$total_cost      = array_sum( $tax_class_costs );
					if ( $total_cost ) {
						foreach ( $tax_class_costs as $tax_class => $tax_class_cost ) {
							if ( 'non-taxable' === $tax_class ) {
								continue;
							}
							$proportion               = $tax_class_cost / $total_cost;
							$cart_discount_proportion = $fee->total * $proportion;
							$fee->taxes               = wc_array_merge_recursive_numeric( $fee->taxes, WC_Tax::calc_tax( $fee->total * $proportion, WC_Tax::get_rates( $tax_class ) ) );
						}
					}
				} elseif ( $fee->object->taxable ) {
					$fee->taxes = WC_Tax::calc_tax( $fee->total, WC_Tax::get_rates( $fee->tax_class, $this->cart->get_customer() ), false );
				}
			}
			$fee->taxes     = apply_filters( 'woocommerce_cart_totals_get_fees_from_cart_taxes', $fee->taxes, $fee, $this );
			$fee->total_tax = array_sum( array_map( array( $this, 'round_line_tax' ), $fee->taxes ) );
			// Set totals within object.
			$fee->object->total    = wc_remove_number_precision_deep( $fee->total );
			$fee->object->tax_data = wc_remove_number_precision_deep( $fee->taxes );
			$fee->object->tax      = wc_remove_number_precision_deep( $fee->total_tax );
			$this->fees[ $fee_key ] = $fee;
		}
	}
	/**
	 * Get shipping methods from the cart and normalise.
	 *
	 * @since 3.2.0
	 */
	protected function get_shipping_from_cart() {
		$this->shipping = array();
		if ( ! $this->cart->show_shipping() ) {
			return;
		}
		foreach ( $this->cart->calculate_shipping() as $key => $shipping_object ) {
			$shipping_line            = $this->get_default_shipping_props();
			$shipping_line->object    = $shipping_object;
			$shipping_line->tax_class = get_option( 'woocommerce_shipping_tax_class' );
			$shipping_line->taxable   = true;
			$shipping_line->total     = wc_add_number_precision_deep( $shipping_object->cost );
			$shipping_line->taxes     = wc_add_number_precision_deep( $shipping_object->taxes, false );
			$shipping_line->taxes     = array_map( array( $this, 'round_item_subtotal' ), $shipping_line->taxes );
			$shipping_line->total_tax = array_sum( $shipping_line->taxes );
			$this->shipping[ $key ] = $shipping_line;
		}
	}
	/**
	 * Return array of coupon objects from the cart. Normalises data
	 * into the same format for use by this class.
	 *
	 * @since  3.2.0
	 */
	protected function get_coupons_from_cart() {
		$this->coupons = $this->cart->get_coupons();
		foreach ( $this->coupons as $coupon ) {
			switch ( $coupon->get_discount_type() ) {
				case 'fixed_product':
					$coupon->sort = 1;
					break;
				case 'percent':
					$coupon->sort = 2;
					break;
				case 'fixed_cart':
					$coupon->sort = 3;
					break;
				default:
					$coupon->sort = 0;
					break;
			}
			// Allow plugins to override the default order.
			$coupon->sort = apply_filters( 'woocommerce_coupon_sort', $coupon->sort, $coupon );
		}
		uasort( $this->coupons, array( $this, 'sort_coupons_callback' ) );
	}
	/**
	 * Sort coupons so discounts apply consistently across installs.
	 *
	 * In order of priority;
	 *  - sort param
	 *  - usage restriction
	 *  - coupon value
	 *  - ID
	 *
	 * @param WC_Coupon $a Coupon object.
	 * @param WC_Coupon $b Coupon object.
	 * @return int
	 */
	protected function sort_coupons_callback( $a, $b ) {
		if ( $a->sort === $b->sort ) {
			if ( $a->get_limit_usage_to_x_items() === $b->get_limit_usage_to_x_items() ) {
				if ( $a->get_amount() === $b->get_amount() ) {
					return $b->get_id() - $a->get_id();
				}
				return ( $a->get_amount() < $b->get_amount() ) ? -1 : 1;
			}
			return ( $a->get_limit_usage_to_x_items() < $b->get_limit_usage_to_x_items() ) ? -1 : 1;
		}
		return ( $a->sort < $b->sort ) ? -1 : 1;
	}
	/**
	 * Ran to remove all base taxes from an item. Used when prices include tax, and the customer is tax exempt.
	 *
	 * @since 3.2.2
	 * @param object $item Item to adjust the prices of.
	 * @return object
	 */
	protected function remove_item_base_taxes( $item ) {
		if ( $item->price_includes_tax && $item->taxable ) {
			if ( apply_filters( 'woocommerce_adjust_non_base_location_prices', true ) ) {
				$base_tax_rates = WC_Tax::get_base_tax_rates( $item->product->get_tax_class( 'unfiltered' ) );
			} else {
				/**
				 * If we want all customers to pay the same price on this store, we should not remove base taxes from a VAT exempt user's price,
				 * but just the relevant tax rate. See issue #20911.
				 */
				$base_tax_rates = $item->tax_rates;
			}
			// Work out a new base price without the shop's base tax.
			$taxes = WC_Tax::calc_tax( $item->price, $base_tax_rates, true );
			// Now we have a new item price (excluding TAX).
			$item->price              = NumberUtil::round( $item->price - array_sum( $taxes ) );
			$item->price_includes_tax = false;
		}
		return $item;
	}
	/**
	 * Only ran if woocommerce_adjust_non_base_location_prices is true.
	 *
	 * If the customer is outside of the base location, this removes the base
	 * taxes. This is off by default unless the filter is used.
	 *
	 * Uses edit context so unfiltered tax class is returned.
	 *
	 * @since 3.2.0
	 * @param object $item Item to adjust the prices of.
	 * @return object
	 */
	protected function adjust_non_base_location_price( $item ) {
		if ( $item->price_includes_tax && $item->taxable ) {
			$base_tax_rates = WC_Tax::get_base_tax_rates( $item->product->get_tax_class( 'unfiltered' ) );
			if ( $item->tax_rates !== $base_tax_rates ) {
				// Work out a new base price without the shop's base tax.
				$taxes     = WC_Tax::calc_tax( $item->price, $base_tax_rates, true );
				$new_taxes = WC_Tax::calc_tax( $item->price - array_sum( $taxes ), $item->tax_rates, false );
				// Now we have a new item price.
				$item->price = $item->price - array_sum( $taxes ) + array_sum( $new_taxes );
			}
		}
		return $item;
	}
	/**
	 * Get discounted price of an item with precision (in cents).
	 *
	 * @since  3.2.0
	 * @param  object $item_key Item to get the price of.
	 * @return int
	 */
	protected function get_discounted_price_in_cents( $item_key ) {
		$item  = $this->items[ $item_key ];
		$price = isset( $this->coupon_discount_totals[ $item_key ] ) ? $item->price - $this->coupon_discount_totals[ $item_key ] : $item->price;
		return $price;
	}
	/**
	 * Get tax rates for an item. Caches rates in class to avoid multiple look ups.
	 *
	 * @param  object $item Item to get tax rates for.
	 * @return array of taxes
	 */
	protected function get_item_tax_rates( $item ) {
		if ( ! wc_tax_enabled() ) {
			return array();
		}
		$tax_class      = $item->product->get_tax_class();
		$item_tax_rates = isset( $this->item_tax_rates[ $tax_class ] ) ? $this->item_tax_rates[ $tax_class ] : $this->item_tax_rates[ $tax_class ] = WC_Tax::get_rates( $item->product->get_tax_class(), $this->cart->get_customer() );
		// Allow plugins to filter item tax rates.
		return apply_filters( 'woocommerce_cart_totals_get_item_tax_rates', $item_tax_rates, $item, $this->cart );
	}
	/**
	 * Get item costs grouped by tax class.
	 *
	 * @since  3.2.0
	 * @return array
	 */
	protected function get_item_costs_by_tax_class() {
		$tax_classes = array(
			'non-taxable' => 0,
		);
		foreach ( $this->items + $this->fees + $this->shipping as $item ) {
			if ( ! isset( $tax_classes[ $item->tax_class ] ) ) {
				$tax_classes[ $item->tax_class ] = 0;
			}
			if ( $item->taxable ) {
				$tax_classes[ $item->tax_class ] += $item->total;
			} else {
				$tax_classes['non-taxable'] += $item->total;
			}
		}
		return $tax_classes;
	}
	/**
	 * Get a single total with or without precision (in cents).
	 *
	 * @since  3.2.0
	 * @param  string $key Total to get.
	 * @param  bool   $in_cents Should the totals be returned in cents, or without precision.
	 * @return int|float
	 */
	public function get_total( $key = 'total', $in_cents = false ) {
		$totals = $this->get_totals( $in_cents );
		return isset( $totals[ $key ] ) ? $totals[ $key ] : 0;
	}
	/**
	 * Set a single total.
	 *
	 * @since  3.2.0
	 * @param string $key Total name you want to set.
	 * @param int    $total Total to set.
	 */
	protected function set_total( $key, $total ) {
		$this->totals[ $key ] = $total;
	}
	/**
	 * Get all totals with or without precision (in cents).
	 *
	 * @since  3.2.0
	 * @param  bool $in_cents Should the totals be returned in cents, or without precision.
	 * @return array.
	 */
	public function get_totals( $in_cents = false ) {
		return $in_cents ? $this->totals : wc_remove_number_precision_deep( $this->totals );
	}
	/**
	 * Returns array of values for totals calculation.
	 *
	 * @param string $field Field name. Will probably be `total` or `subtotal`.
	 * @return array Items object
	 */
	protected function get_values_for_total( $field ) {
		return array_values( wp_list_pluck( $this->items, $field ) );
	}
	/**
	 * Get taxes merged by type.
	 *
	 * @since 3.2.0
	 * @param  bool         $in_cents If returned value should be in cents.
	 * @param  array|string $types    Types to merge and return. Defaults to all.
	 * @return array
	 */
	protected function get_merged_taxes( $in_cents = false, $types = array( 'items', 'fees', 'shipping' ) ) {
		$items = array();
		$taxes = array();
		if ( is_string( $types ) ) {
			$types = array( $types );
		}
		foreach ( $types as $type ) {
			if ( isset( $this->$type ) ) {
				$items = array_merge( $items, $this->$type );
			}
		}
		foreach ( $items as $item ) {
			foreach ( $item->taxes as $rate_id => $rate ) {
				if ( ! isset( $taxes[ $rate_id ] ) ) {
					$taxes[ $rate_id ] = 0;
				}
				$taxes[ $rate_id ] += $this->round_line_tax( $rate );
			}
		}
		return $in_cents ? $taxes : wc_remove_number_precision_deep( $taxes );
	}
	/**
	 * Round merged taxes.
	 *
	 * @deprecated 3.9.0 `calculate_item_subtotals` should already appropriately round the tax values.
	 * @since 3.5.4
	 * @param array $taxes Taxes to round.
	 * @return array
	 */
	protected function round_merged_taxes( $taxes ) {
		foreach ( $taxes as $rate_id => $tax ) {
			$taxes[ $rate_id ] = $this->round_line_tax( $tax );
		}
		return $taxes;
	}
	/**
	 * Combine item taxes into a single array, preserving keys.
	 *
	 * @since 3.2.0
	 * @param array $item_taxes Taxes to combine.
	 * @return array
	 */
	protected function combine_item_taxes( $item_taxes ) {
		$merged_taxes = array();
		foreach ( $item_taxes as $taxes ) {
			foreach ( $taxes as $tax_id => $tax_amount ) {
				if ( ! isset( $merged_taxes[ $tax_id ] ) ) {
					$merged_taxes[ $tax_id ] = 0;
				}
				$merged_taxes[ $tax_id ] += $tax_amount;
			}
		}
		return $merged_taxes;
	}
	/*
	|--------------------------------------------------------------------------
	| Calculation methods.
	|--------------------------------------------------------------------------
	*/
	/**
	 * Calculate item totals.
	 *
	 * @since 3.2.0
	 */
	protected function calculate_item_totals() {
		$this->get_items_from_cart();
		$this->calculate_item_subtotals();
		$this->calculate_discounts();
		foreach ( $this->items as $item_key => $item ) {
			$item->total     = $this->get_discounted_price_in_cents( $item_key );
			$item->total_tax = 0;
			if ( has_filter( 'woocommerce_get_discounted_price' ) ) {
				/**
				 * Allow plugins to filter this price like in the legacy cart class.
				 *
				 * This is legacy and should probably be deprecated in the future.
				 * $item->object is the cart item object.
				 * $this->cart is the cart object.
				 */
				$item->total = wc_add_number_precision(
					apply_filters( 'woocommerce_get_discounted_price', wc_remove_number_precision( $item->total ), $item->object, $this->cart )
				);
			}
			if ( $this->calculate_tax && $item->product->is_taxable() ) {
				$total_taxes     = apply_filters( 'woocommerce_calculate_item_totals_taxes', WC_Tax::calc_tax( $item->total, $item->tax_rates, $item->price_includes_tax ), $item, $this );
				$item->taxes     = $total_taxes;
				$item->total_tax = array_sum( array_map( array( $this, 'round_line_tax' ), $item->taxes ) );
				if ( $item->price_includes_tax ) {
					// Use unrounded taxes so we can re-calculate from the orders screen accurately later.
					$item->total = $item->total - array_sum( $item->taxes );
				}
			}
			$this->cart->cart_contents[ $item_key ]['line_tax_data']['total'] = wc_remove_number_precision_deep( $item->taxes );
			$this->cart->cart_contents[ $item_key ]['line_total']             = wc_remove_number_precision( $item->total );
			$this->cart->cart_contents[ $item_key ]['line_tax']               = wc_remove_number_precision( $item->total_tax );
		}
		$items_total = $this->get_rounded_items_total( $this->get_values_for_total( 'total' ) );
		$this->set_total( 'items_total', $items_total );
		$this->set_total( 'items_total_tax', array_sum( array_values( wp_list_pluck( $this->items, 'total_tax' ) ) ) );
		$this->cart->set_cart_contents_total( $this->get_total( 'items_total' ) );
		$this->cart->set_cart_contents_tax( array_sum( $this->get_merged_taxes( false, 'items' ) ) );
		$this->cart->set_cart_contents_taxes( $this->get_merged_taxes( false, 'items' ) );
	}
	/**
	 * Subtotals are costs before discounts.
	 *
	 * To prevent rounding issues we need to work with the inclusive price where possible
	 * otherwise we'll see errors such as when working with a 9.99 inc price, 20% VAT which would
	 * be 8.325 leading to totals being 1p off.
	 *
	 * Pre tax coupons come off the price the customer thinks they are paying - tax is calculated
	 * afterwards.
	 *
	 * e.g. $100 bike with $10 coupon = customer pays $90 and tax worked backwards from that.
	 *
	 * @since 3.2.0
	 */
	protected function calculate_item_subtotals() {
		$merged_subtotal_taxes = array(); // Taxes indexed by tax rate ID for storage later.
		$adjust_non_base_location_prices = apply_filters( 'woocommerce_adjust_non_base_location_prices', true );
		$is_customer_vat_exempt          = $this->cart->get_customer()->get_is_vat_exempt();
		foreach ( $this->items as $item_key => $item ) {
			if ( $item->price_includes_tax ) {
				if ( $is_customer_vat_exempt ) {
					$item = $this->remove_item_base_taxes( $item );
				} elseif ( $adjust_non_base_location_prices ) {
					$item = $this->adjust_non_base_location_price( $item );
				}
			}
			$item->subtotal = $item->price;
			if ( $this->calculate_tax && $item->product->is_taxable() ) {
				$item->subtotal_taxes = WC_Tax::calc_tax( $item->subtotal, $item->tax_rates, $item->price_includes_tax );
				$item->subtotal_tax   = array_sum( array_map( array( $this, 'round_line_tax' ), $item->subtotal_taxes ) );
				if ( $item->price_includes_tax ) {
					// Use unrounded taxes so we can re-calculate from the orders screen accurately later.
					$item->subtotal = $item->subtotal - array_sum( $item->subtotal_taxes );
				}
				foreach ( $item->subtotal_taxes as $rate_id => $rate ) {
					if ( ! isset( $merged_subtotal_taxes[ $rate_id ] ) ) {
						$merged_subtotal_taxes[ $rate_id ] = 0;
					}
					$merged_subtotal_taxes[ $rate_id ] += $this->round_line_tax( $rate );
				}
			}
			$this->cart->cart_contents[ $item_key ]['line_tax_data']     = array( 'subtotal' => wc_remove_number_precision_deep( $item->subtotal_taxes ) );
			$this->cart->cart_contents[ $item_key ]['line_subtotal']     = wc_remove_number_precision( $item->subtotal );
			$this->cart->cart_contents[ $item_key ]['line_subtotal_tax'] = wc_remove_number_precision( $item->subtotal_tax );
		}
		$items_subtotal = $this->get_rounded_items_total( $this->get_values_for_total( 'subtotal' ) );
		// Prices are not rounded here because they should already be rounded based on settings in `get_rounded_items_total` and in `round_line_tax` method calls.
		$this->set_total( 'items_subtotal', $items_subtotal );
		$this->set_total( 'items_subtotal_tax', array_sum( $merged_subtotal_taxes ), 0 );
		$this->cart->set_subtotal( $this->get_total( 'items_subtotal' ) );
		$this->cart->set_subtotal_tax( $this->get_total( 'items_subtotal_tax' ) );
	}
	/**
	 * Calculate COUPON based discounts which change item prices.
	 *
	 * @since 3.2.0
	 * @uses  WC_Discounts class.
	 */
	protected function calculate_discounts() {
		$this->get_coupons_from_cart();
		$discounts = new WC_Discounts( $this->cart );
		// Set items directly so the discounts class can see any tax adjustments made thus far using subtotals.
		$discounts->set_items( $this->items );
		foreach ( $this->coupons as $coupon ) {
			$discounts->apply_coupon( $coupon );
		}
		$coupon_discount_amounts     = $discounts->get_discounts_by_coupon( true );
		$coupon_discount_tax_amounts = array();
		// See how much tax was 'discounted' per item and per coupon.
		if ( $this->calculate_tax ) {
			foreach ( $discounts->get_discounts( true ) as $coupon_code => $coupon_discounts ) {
				$coupon_discount_tax_amounts[ $coupon_code ] = 0;
				foreach ( $coupon_discounts as $item_key => $coupon_discount ) {
					$item = $this->items[ $item_key ];
					if ( $item->product->is_taxable() ) {
						// Item subtotals were sent, so set 3rd param.
						$item_tax = array_sum( WC_Tax::calc_tax( $coupon_discount, $item->tax_rates, $item->price_includes_tax ) );
						// Sum total tax.
						$coupon_discount_tax_amounts[ $coupon_code ] += $item_tax;
						// Remove tax from discount total.
						if ( $item->price_includes_tax ) {
							$coupon_discount_amounts[ $coupon_code ] -= $item_tax;
						}
					}
				}
			}
		}
		$this->coupon_discount_totals     = (array) $discounts->get_discounts_by_item( true );
		$this->coupon_discount_tax_totals = $coupon_discount_tax_amounts;
		if ( wc_prices_include_tax() ) {
			$this->set_total( 'discounts_total', array_sum( $this->coupon_discount_totals ) - array_sum( $this->coupon_discount_tax_totals ) );
			$this->set_total( 'discounts_tax_total', array_sum( $this->coupon_discount_tax_totals ) );
		} else {
			$this->set_total( 'discounts_total', array_sum( $this->coupon_discount_totals ) );
			$this->set_total( 'discounts_tax_total', array_sum( $this->coupon_discount_tax_totals ) );
		}
		$this->cart->set_coupon_discount_totals( wc_remove_number_precision_deep( $coupon_discount_amounts ) );
		$this->cart->set_coupon_discount_tax_totals( wc_remove_number_precision_deep( $coupon_discount_tax_amounts ) );
		// Add totals to cart object. Note: Discount total for cart is excl tax.
		$this->cart->set_discount_total( $this->get_total( 'discounts_total' ) );
		$this->cart->set_discount_tax( $this->get_total( 'discounts_tax_total' ) );
	}
	/**
	 * Triggers the cart fees API, grabs the list of fees, and calculates taxes.
	 *
	 * Note: This class sets the totals for the 'object' as they are calculated. This is so that APIs like the fees API can see these totals if needed.
	 *
	 * @since 3.2.0
	 */
	protected function calculate_fee_totals() {
		$this->get_fees_from_cart();
		$this->set_total( 'fees_total', array_sum( wp_list_pluck( $this->fees, 'total' ) ) );
		$this->set_total( 'fees_total_tax', array_sum( wp_list_pluck( $this->fees, 'total_tax' ) ) );
		$this->cart->fees_api()->set_fees( wp_list_pluck( $this->fees, 'object' ) );
		$this->cart->set_fee_total( wc_remove_number_precision_deep( array_sum( wp_list_pluck( $this->fees, 'total' ) ) ) );
		$this->cart->set_fee_tax( wc_remove_number_precision_deep( array_sum( wp_list_pluck( $this->fees, 'total_tax' ) ) ) );
		$this->cart->set_fee_taxes( wc_remove_number_precision_deep( $this->combine_item_taxes( wp_list_pluck( $this->fees, 'taxes' ) ) ) );
	}
	/**
	 * Calculate any shipping taxes.
	 *
	 * @since 3.2.0
	 */
	protected function calculate_shipping_totals() {
		$this->get_shipping_from_cart();
		$this->set_total( 'shipping_total', array_sum( wp_list_pluck( $this->shipping, 'total' ) ) );
		$this->set_total( 'shipping_tax_total', array_sum( wp_list_pluck( $this->shipping, 'total_tax' ) ) );
		$this->cart->set_shipping_total( $this->get_total( 'shipping_total' ) );
		$this->cart->set_shipping_tax( $this->get_total( 'shipping_tax_total' ) );
		$this->cart->set_shipping_taxes( wc_remove_number_precision_deep( $this->combine_item_taxes( wp_list_pluck( $this->shipping, 'taxes' ) ) ) );
	}
	/**
	 * Main cart totals.
	 *
	 * @since 3.2.0
	 */
	protected function calculate_totals() {
		$this->set_total( 'total', NumberUtil::round( $this->get_total( 'items_total', true ) + $this->get_total( 'fees_total', true ) + $this->get_total( 'shipping_total', true ) + array_sum( $this->get_merged_taxes( true ) ), 0 ) );
		$items_tax = array_sum( $this->get_merged_taxes( false, array( 'items' ) ) );
		// Shipping and fee taxes are rounded separately because they were entered excluding taxes (as opposed to item prices, which may or may not be including taxes depending upon settings).
		$shipping_and_fee_taxes = NumberUtil::round( array_sum( $this->get_merged_taxes( false, array( 'fees', 'shipping' ) ) ), wc_get_price_decimals() );
		$this->cart->set_total_tax( $items_tax + $shipping_and_fee_taxes );
		// Allow plugins to hook and alter totals before final total is calculated.
		if ( has_action( 'woocommerce_calculate_totals' ) ) {
			do_action( 'woocommerce_calculate_totals', $this->cart );
		}
		// Allow plugins to filter the grand total, and sum the cart totals in case of modifications.
		$this->cart->set_total( max( 0, apply_filters( 'woocommerce_calculated_total', $this->get_total( 'total' ), $this->cart ) ) );
	}
}
PK     K[Žâ´ßE”  E”    wc-order-functions.phpnu „[µü¤         'limit',
		'post_type'      => 'type',
		'post_status'    => 'status',
		'post_parent'    => 'parent',
		'author'         => 'customer',
		'email'          => 'billing_email',
		'posts_per_page' => 'limit',
		'paged'          => 'page',
	);
	foreach ( $map_legacy as $from => $to ) {
		if ( isset( $args[ $from ] ) ) {
			$args[ $to ] = $args[ $from ];
		}
	}
	// Map legacy date args to modern date args.
	$date_before = false;
	$date_after  = false;
	if ( ! empty( $args['date_before'] ) ) {
		$datetime    = wc_string_to_datetime( $args['date_before'] );
		$date_before = strpos( $args['date_before'], ':' ) ? $datetime->getOffsetTimestamp() : $datetime->date( 'Y-m-d' );
	}
	if ( ! empty( $args['date_after'] ) ) {
		$datetime   = wc_string_to_datetime( $args['date_after'] );
		$date_after = strpos( $args['date_after'], ':' ) ? $datetime->getOffsetTimestamp() : $datetime->date( 'Y-m-d' );
	}
	if ( $date_before && $date_after ) {
		$args['date_created'] = $date_after . '...' . $date_before;
	} elseif ( $date_before ) {
		$args['date_created'] = '<' . $date_before;
	} elseif ( $date_after ) {
		$args['date_created'] = '>' . $date_after;
	}
	$query = new WC_Order_Query( $args );
	return $query->get_orders();
}
/**
 * Main function for returning orders, uses the WC_Order_Factory class.
 *
 * @since  2.2
 *
 * @param mixed $the_order       Post object or post ID of the order.
 *
 * @return bool|WC_Order|WC_Order_Refund
 */
function wc_get_order( $the_order = false ) {
	if ( ! did_action( 'woocommerce_after_register_post_type' ) ) {
		wc_doing_it_wrong( __FUNCTION__, 'wc_get_order should not be called before post types are registered (woocommerce_after_register_post_type action)', '2.5' );
		return false;
	}
	return WC()->order_factory->get_order( $the_order );
}
/**
 * Get all order statuses.
 *
 * @since 2.2
 * @used-by WC_Order::set_status
 * @return array
 */
function wc_get_order_statuses() {
	$order_statuses = array(
		'wc-pending'    => _x( 'Pending payment', 'Order status', 'woocommerce' ),
		'wc-processing' => _x( 'Processing', 'Order status', 'woocommerce' ),
		'wc-on-hold'    => _x( 'On hold', 'Order status', 'woocommerce' ),
		'wc-completed'  => _x( 'Completed', 'Order status', 'woocommerce' ),
		'wc-cancelled'  => _x( 'Cancelled', 'Order status', 'woocommerce' ),
		'wc-refunded'   => _x( 'Refunded', 'Order status', 'woocommerce' ),
		'wc-failed'     => _x( 'Failed', 'Order status', 'woocommerce' ),
	);
	return apply_filters( 'wc_order_statuses', $order_statuses );
}
/**
 * See if a string is an order status.
 *
 * @param  string $maybe_status Status, including any wc- prefix.
 * @return bool
 */
function wc_is_order_status( $maybe_status ) {
	$order_statuses = wc_get_order_statuses();
	return isset( $order_statuses[ $maybe_status ] );
}
/**
 * Get list of statuses which are consider 'paid'.
 *
 * @since  3.0.0
 * @return array
 */
function wc_get_is_paid_statuses() {
	return apply_filters( 'woocommerce_order_is_paid_statuses', array( 'processing', 'completed' ) );
}
/**
 * Get list of statuses which are consider 'pending payment'.
 *
 * @since  3.6.0
 * @return array
 */
function wc_get_is_pending_statuses() {
	return apply_filters( 'woocommerce_order_is_pending_statuses', array( 'pending' ) );
}
/**
 * Get the nice name for an order status.
 *
 * @since  2.2
 * @param  string $status Status.
 * @return string
 */
function wc_get_order_status_name( $status ) {
	$statuses = wc_get_order_statuses();
	$status   = 'wc-' === substr( $status, 0, 3 ) ? substr( $status, 3 ) : $status;
	$status   = isset( $statuses[ 'wc-' . $status ] ) ? $statuses[ 'wc-' . $status ] : $status;
	return $status;
}
/**
 * Generate an order key with prefix.
 *
 * @since 3.5.4
 * @param string $key Order key without a prefix. By default generates a 13 digit secret.
 * @return string The order key.
 */
function wc_generate_order_key( $key = '' ) {
	if ( '' === $key ) {
		$key = wp_generate_password( 13, false );
	}
	return 'wc_' . apply_filters( 'woocommerce_generate_order_key', 'order_' . $key );
}
/**
 * Finds an Order ID based on an order key.
 *
 * @param string $order_key An order key has generated by.
 * @return int The ID of an order, or 0 if the order could not be found.
 */
function wc_get_order_id_by_order_key( $order_key ) {
	$data_store = WC_Data_Store::load( 'order' );
	return $data_store->get_order_id_by_order_key( $order_key );
}
/**
 * Get all registered order types.
 *
 * @since  2.2
 * @param  string $for Optionally define what you are getting order types for so
 *                     only relevant types are returned.
 *                     e.g. for 'order-meta-boxes', 'order-count'.
 * @return array
 */
function wc_get_order_types( $for = '' ) {
	global $wc_order_types;
	if ( ! is_array( $wc_order_types ) ) {
		$wc_order_types = array();
	}
	$order_types = array();
	switch ( $for ) {
		case 'order-count':
			foreach ( $wc_order_types as $type => $args ) {
				if ( ! $args['exclude_from_order_count'] ) {
					$order_types[] = $type;
				}
			}
			break;
		case 'order-meta-boxes':
			foreach ( $wc_order_types as $type => $args ) {
				if ( $args['add_order_meta_boxes'] ) {
					$order_types[] = $type;
				}
			}
			break;
		case 'view-orders':
			foreach ( $wc_order_types as $type => $args ) {
				if ( ! $args['exclude_from_order_views'] ) {
					$order_types[] = $type;
				}
			}
			break;
		case 'reports':
			foreach ( $wc_order_types as $type => $args ) {
				if ( ! $args['exclude_from_order_reports'] ) {
					$order_types[] = $type;
				}
			}
			break;
		case 'sales-reports':
			foreach ( $wc_order_types as $type => $args ) {
				if ( ! $args['exclude_from_order_sales_reports'] ) {
					$order_types[] = $type;
				}
			}
			break;
		case 'order-webhooks':
			foreach ( $wc_order_types as $type => $args ) {
				if ( ! $args['exclude_from_order_webhooks'] ) {
					$order_types[] = $type;
				}
			}
			break;
		case 'cot-migration':
			foreach ( $wc_order_types as $type => $args ) {
				if ( DataSynchronizer::PLACEHOLDER_ORDER_POST_TYPE !== $type ) {
					$order_types[] = $type;
				}
			}
			break;
		case 'admin-menu':
			$order_types = array_intersect(
				array_keys( $wc_order_types ),
				get_post_types(
					array(
						'show_ui'      => true,
						'show_in_menu' => 'woocommerce',
					)
				)
			);
			break;
		default:
			$order_types = array_keys( $wc_order_types );
			break;
	}
	return apply_filters( 'wc_order_types', $order_types, $for );
}
/**
 * Get an order type by post type name.
 *
 * @param  string $type Post type name.
 * @return bool|array Details about the order type.
 */
function wc_get_order_type( $type ) {
	global $wc_order_types;
	if ( isset( $wc_order_types[ $type ] ) ) {
		return $wc_order_types[ $type ];
	}
	return false;
}
/**
 * Register order type. Do not use before init.
 *
 * Wrapper for register post type, as well as a method of telling WC which.
 * post types are types of orders, and having them treated as such.
 *
 * $args are passed to register_post_type, but there are a few specific to this function:
 *      - exclude_from_orders_screen (bool) Whether or not this order type also get shown in the main.
 *      orders screen.
 *      - add_order_meta_boxes (bool) Whether or not the order type gets shop_order meta boxes.
 *      - exclude_from_order_count (bool) Whether or not this order type is excluded from counts.
 *      - exclude_from_order_views (bool) Whether or not this order type is visible by customers when.
 *      viewing orders e.g. on the my account page.
 *      - exclude_from_order_reports (bool) Whether or not to exclude this type from core reports.
 *      - exclude_from_order_sales_reports (bool) Whether or not to exclude this type from core sales reports.
 *
 * @since  2.2
 * @see    register_post_type for $args used in that function
 * @param  string $type Post type. (max. 20 characters, can not contain capital letters or spaces).
 * @param  array  $args An array of arguments.
 * @return bool Success or failure
 */
function wc_register_order_type( $type, $args = array() ) {
	if ( post_type_exists( $type ) ) {
		return false;
	}
	global $wc_order_types;
	if ( ! is_array( $wc_order_types ) ) {
		$wc_order_types = array();
	}
	// Register as a post type.
	if ( is_wp_error( register_post_type( $type, $args ) ) ) {
		return false;
	}
	// Register for WC usage.
	$order_type_args = array(
		'exclude_from_orders_screen'       => false,
		'add_order_meta_boxes'             => true,
		'exclude_from_order_count'         => false,
		'exclude_from_order_views'         => false,
		'exclude_from_order_webhooks'      => false,
		'exclude_from_order_reports'       => false,
		'exclude_from_order_sales_reports' => false,
		'class_name'                       => 'WC_Order',
	);
	$args                    = array_intersect_key( $args, $order_type_args );
	$args                    = wp_parse_args( $args, $order_type_args );
	$wc_order_types[ $type ] = $args;
	return true;
}
/**
 * Return the count of processing orders.
 *
 * @return int
 */
function wc_processing_order_count() {
	return wc_orders_count( 'processing' );
}
/**
 * Return the orders count of a specific order status.
 *
 * @param string $status Status.
 * @param string $type   (Optional) Order type. Leave empty to include all 'for order-count' order types. @{see wc_get_order_types()}.
 * @return int
 */
function wc_orders_count( $status, string $type = '' ) {
	$count           = 0;
	$legacy_statuses = array( 'draft', 'trash' );
	$valid_statuses  = array_merge( array_keys( wc_get_order_statuses() ), $legacy_statuses );
	$status          = ( ! in_array( $status, $legacy_statuses, true ) && 0 !== strpos( $status, 'wc-' ) ) ? 'wc-' . $status : $status;
	$valid_types     = wc_get_order_types( 'order-count' );
	$type            = trim( $type );
	if ( ! in_array( $status, $valid_statuses, true ) || ( $type && ! in_array( $type, $valid_types, true ) ) ) {
		return 0;
	}
	$cache_key    = WC_Cache_Helper::get_cache_prefix( 'orders' ) . $status . $type;
	$cached_count = wp_cache_get( $cache_key, 'counts' );
	if ( false !== $cached_count ) {
		return $cached_count;
	}
	$types_for_count = $type ? array( $type ) : $valid_types;
	foreach ( $types_for_count as $type ) {
		$data_store = WC_Data_Store::load( 'shop_order' === $type ? 'order' : $type );
		if ( $data_store ) {
			$count += $data_store->get_order_count( $status );
		}
	}
	wp_cache_set( $cache_key, $count, 'counts' );
	return $count;
}
/**
 * Grant downloadable product access to the file identified by $download_id.
 *
 * @param  string         $download_id File identifier.
 * @param  int|WC_Product $product     Product instance or ID.
 * @param  WC_Order       $order       Order data.
 * @param  int            $qty         Quantity purchased.
 * @param  WC_Order_Item  $item        Item of the order.
 * @return int|bool insert id or false on failure.
 */
function wc_downloadable_file_permission( $download_id, $product, $order, $qty = 1, $item = null ) {
	if ( is_numeric( $product ) ) {
		$product = wc_get_product( $product );
	}
	$download = new WC_Customer_Download();
	$download->set_download_id( $download_id );
	$download->set_product_id( $product->get_id() );
	$download->set_user_id( $order->get_customer_id() );
	$download->set_order_id( $order->get_id() );
	$download->set_user_email( $order->get_billing_email() );
	$download->set_order_key( $order->get_order_key() );
	$download->set_downloads_remaining( 0 > $product->get_download_limit() ? '' : $product->get_download_limit() * $qty );
	$download->set_access_granted( time() );
	$download->set_download_count( 0 );
	$expiry = $product->get_download_expiry();
	if ( $expiry > 0 ) {
		$from_date = $order->get_date_completed() ? $order->get_date_completed()->format( 'Y-m-d' ) : current_time( 'mysql', true );
		$download->set_access_expires( strtotime( $from_date . ' + ' . $expiry . ' DAY' ) );
	}
	$download = apply_filters( 'woocommerce_downloadable_file_permission', $download, $product, $order, $qty, $item );
	return $download->save();
}
/**
 * Order Status completed - give downloadable product access to customer.
 *
 * @param int  $order_id Order ID.
 * @param bool $force    Force downloadable permissions.
 */
function wc_downloadable_product_permissions( $order_id, $force = false ) {
	$order = wc_get_order( $order_id );
	if ( ! $order || ( $order->get_data_store()->get_download_permissions_granted( $order ) && ! $force ) ) {
		return;
	}
	if ( $order->has_status( 'processing' ) && 'no' === get_option( 'woocommerce_downloads_grant_access_after_payment' ) ) {
		return;
	}
	if ( count( $order->get_items() ) > 0 ) {
		foreach ( $order->get_items() as $item ) {
			$product = $item->get_product();
			if ( $product && $product->exists() && $product->is_downloadable() ) {
				$downloads = $product->get_downloads();
				foreach ( array_keys( $downloads ) as $download_id ) {
					wc_downloadable_file_permission( $download_id, $product, $order, $item->get_quantity(), $item );
				}
			}
		}
	}
	$order->get_data_store()->set_download_permissions_granted( $order, true );
	do_action( 'woocommerce_grant_product_download_permissions', $order_id );
}
add_action( 'woocommerce_order_status_completed', 'wc_downloadable_product_permissions' );
add_action( 'woocommerce_order_status_processing', 'wc_downloadable_product_permissions' );
/**
 * Clear all transients cache for order data.
 *
 * @param int|WC_Order $order Order instance or ID.
 */
function wc_delete_shop_order_transients( $order = 0 ) {
	if ( is_numeric( $order ) ) {
		$order = wc_get_order( $order );
	}
	$reports             = WC_Admin_Reports::get_reports();
	$transients_to_clear = array(
		'wc_admin_report',
	);
	foreach ( $reports as $report_group ) {
		foreach ( $report_group['reports'] as $report_key => $report ) {
			$transients_to_clear[] = 'wc_report_' . $report_key;
		}
	}
	foreach ( $transients_to_clear as $transient ) {
		delete_transient( $transient );
	}
	// Clear customer's order related caches.
	if ( is_a( $order, 'WC_Order' ) ) {
		$order_id = $order->get_id();
		delete_user_meta( $order->get_customer_id(), '_money_spent' );
		delete_user_meta( $order->get_customer_id(), '_order_count' );
		delete_user_meta( $order->get_customer_id(), '_last_order' );
	} else {
		$order_id = 0;
	}
	// Increments the transient version to invalidate cache.
	WC_Cache_Helper::get_transient_version( 'orders', true );
	// Do the same for regular cache.
	WC_Cache_Helper::invalidate_cache_group( 'orders' );
	do_action( 'woocommerce_delete_shop_order_transients', $order_id );
}
/**
 * See if we only ship to billing addresses.
 *
 * @return bool
 */
function wc_ship_to_billing_address_only() {
	return 'billing_only' === get_option( 'woocommerce_ship_to_destination' );
}
/**
 * Create a new order refund programmatically.
 *
 * Returns a new refund object on success which can then be used to add additional data.
 *
 * @since 2.2
 * @throws Exception Throws exceptions when fail to create, but returns WP_Error instead.
 * @param array $args New refund arguments.
 * @return WC_Order_Refund|WP_Error
 */
function wc_create_refund( $args = array() ) {
	$default_args = array(
		'amount'         => 0,
		'reason'         => null,
		'order_id'       => 0,
		'refund_id'      => 0,
		'line_items'     => array(),
		'refund_payment' => false,
		'restock_items'  => false,
	);
	try {
		$args  = wp_parse_args( $args, $default_args );
		$order = wc_get_order( $args['order_id'] );
		if ( ! $order ) {
			throw new Exception( __( 'Invalid order ID.', 'woocommerce' ) );
		}
		$remaining_refund_amount = $order->get_remaining_refund_amount();
		$remaining_refund_items  = $order->get_remaining_refund_items();
		$refund_item_count       = 0;
		$refund                  = new WC_Order_Refund( $args['refund_id'] );
		if ( 0 > $args['amount'] || $args['amount'] > $remaining_refund_amount ) {
			throw new Exception( __( 'Invalid refund amount.', 'woocommerce' ) );
		}
		$refund->set_currency( $order->get_currency() );
		$refund->set_amount( $args['amount'] );
		$refund->set_parent_id( absint( $args['order_id'] ) );
		$refund->set_refunded_by( get_current_user_id() ? get_current_user_id() : 1 );
		$refund->set_prices_include_tax( $order->get_prices_include_tax() );
		if ( ! is_null( $args['reason'] ) ) {
			$refund->set_reason( $args['reason'] );
		}
		// Negative line items.
		if ( count( $args['line_items'] ) > 0 ) {
			$items = $order->get_items( array( 'line_item', 'fee', 'shipping' ) );
			foreach ( $items as $item_id => $item ) {
				if ( ! isset( $args['line_items'][ $item_id ] ) ) {
					continue;
				}
				$qty          = isset( $args['line_items'][ $item_id ]['qty'] ) ? $args['line_items'][ $item_id ]['qty'] : 0;
				$refund_total = $args['line_items'][ $item_id ]['refund_total'];
				$refund_tax   = isset( $args['line_items'][ $item_id ]['refund_tax'] ) ? array_filter( (array) $args['line_items'][ $item_id ]['refund_tax'] ) : array();
				if ( empty( $qty ) && empty( $refund_total ) && empty( $args['line_items'][ $item_id ]['refund_tax'] ) ) {
					continue;
				}
				$class         = get_class( $item );
				$refunded_item = new $class( $item );
				$refunded_item->set_id( 0 );
				$refunded_item->add_meta_data( '_refunded_item_id', $item_id, true );
				$refunded_item->set_total( wc_format_refund_total( $refund_total ) );
				$refunded_item->set_taxes(
					array(
						'total'    => array_map( 'wc_format_refund_total', $refund_tax ),
						'subtotal' => array_map( 'wc_format_refund_total', $refund_tax ),
					)
				);
				if ( is_callable( array( $refunded_item, 'set_subtotal' ) ) ) {
					$refunded_item->set_subtotal( wc_format_refund_total( $refund_total ) );
				}
				if ( is_callable( array( $refunded_item, 'set_quantity' ) ) ) {
					$refunded_item->set_quantity( $qty * -1 );
				}
				$refund->add_item( $refunded_item );
				$refund_item_count += $qty;
			}
		}
		$refund->update_taxes();
		$refund->calculate_totals( false );
		$refund->set_total( $args['amount'] * -1 );
		// this should remain after update_taxes(), as this will save the order, and write the current date to the db
		// so we must wait until the order is persisted to set the date.
		if ( isset( $args['date_created'] ) ) {
			$refund->set_date_created( $args['date_created'] );
		}
		/**
		 * Action hook to adjust refund before save.
		 *
		 * @since 3.0.0
		 */
		do_action( 'woocommerce_create_refund', $refund, $args );
		if ( $refund->save() ) {
			if ( $args['refund_payment'] ) {
				$result = wc_refund_payment( $order, $refund->get_amount(), $refund->get_reason() );
				if ( is_wp_error( $result ) ) {
					$refund->delete();
					return $result;
				}
				$refund->set_refunded_payment( true );
				$refund->save();
			}
			if ( $args['restock_items'] ) {
				wc_restock_refunded_items( $order, $args['line_items'] );
			}
			/**
			 * Trigger notification emails.
			 *
			 * Filter hook to modify the partially-refunded status conditions.
			 *
			 * @since 6.7.0
			 *
			 * @param bool $is_partially_refunded Whether the order is partially refunded.
			 * @param int  $order_id The order id.
			 * @param int  $refund_id The refund id.
			 */
			if ( (bool) apply_filters( 'woocommerce_order_is_partially_refunded', ( $remaining_refund_amount - $args['amount'] ) > 0 || ( $order->has_free_item() && ( $remaining_refund_items - $refund_item_count ) > 0 ), $order->get_id(), $refund->get_id() ) ) {
				do_action( 'woocommerce_order_partially_refunded', $order->get_id(), $refund->get_id() );
			} else {
				do_action( 'woocommerce_order_fully_refunded', $order->get_id(), $refund->get_id() );
				$parent_status = apply_filters( 'woocommerce_order_fully_refunded_status', 'refunded', $order->get_id(), $refund->get_id() );
				if ( $parent_status ) {
					$order->update_status( $parent_status );
				}
			}
		}
		$order->set_date_modified( time() );
		$order->save();
		do_action( 'woocommerce_refund_created', $refund->get_id(), $args );
		do_action( 'woocommerce_order_refunded', $order->get_id(), $refund->get_id() );
	} catch ( Exception $e ) {
		if ( isset( $refund ) && is_a( $refund, 'WC_Order_Refund' ) ) {
			$refund->delete( true );
		}
		return new WP_Error( 'error', $e->getMessage() );
	}
	return $refund;
}
/**
 * Try to refund the payment for an order via the gateway.
 *
 * @since 3.0.0
 * @throws Exception Throws exceptions when fail to refund, but returns WP_Error instead.
 * @param WC_Order $order  Order instance.
 * @param string   $amount Amount to refund.
 * @param string   $reason Refund reason.
 * @return bool|WP_Error
 */
function wc_refund_payment( $order, $amount, $reason = '' ) {
	try {
		if ( ! is_a( $order, 'WC_Order' ) ) {
			throw new Exception( __( 'Invalid order.', 'woocommerce' ) );
		}
		$gateway_controller = WC_Payment_Gateways::instance();
		$all_gateways       = $gateway_controller->payment_gateways();
		$payment_method     = $order->get_payment_method();
		$gateway            = isset( $all_gateways[ $payment_method ] ) ? $all_gateways[ $payment_method ] : false;
		if ( ! $gateway ) {
			throw new Exception( __( 'The payment gateway for this order does not exist.', 'woocommerce' ) );
		}
		if ( ! $gateway->supports( 'refunds' ) ) {
			throw new Exception( __( 'The payment gateway for this order does not support automatic refunds.', 'woocommerce' ) );
		}
		$result = $gateway->process_refund( $order->get_id(), $amount, $reason );
		if ( ! $result ) {
			throw new Exception( __( 'An error occurred while attempting to create the refund using the payment gateway API.', 'woocommerce' ) );
		}
		if ( is_wp_error( $result ) ) {
			throw new Exception( $result->get_error_message() );
		}
		return true;
	} catch ( Exception $e ) {
		return new WP_Error( 'error', $e->getMessage() );
	}
}
/**
 * Restock items during refund.
 *
 * @since 3.0.0
 * @param WC_Order $order               Order instance.
 * @param array    $refunded_line_items Refunded items list.
 */
function wc_restock_refunded_items( $order, $refunded_line_items ) {
	if ( ! apply_filters( 'woocommerce_can_restock_refunded_items', true, $order, $refunded_line_items ) ) {
		return;
	}
	$line_items = $order->get_items();
	foreach ( $line_items as $item_id => $item ) {
		if ( ! isset( $refunded_line_items[ $item_id ], $refunded_line_items[ $item_id ]['qty'] ) ) {
			continue;
		}
		$product                = $item->get_product();
		$item_stock_reduced     = $item->get_meta( '_reduced_stock', true );
		$restock_refunded_items = (int) $item->get_meta( '_restock_refunded_items', true );
		$qty_to_refund          = $refunded_line_items[ $item_id ]['qty'];
		if ( ! $item_stock_reduced || ! $qty_to_refund || ! $product || ! $product->managing_stock() ) {
			continue;
		}
		$old_stock = $product->get_stock_quantity();
		$new_stock = wc_update_product_stock( $product, $qty_to_refund, 'increase' );
		// Update _reduced_stock meta to track changes.
		$item_stock_reduced = $item_stock_reduced - $qty_to_refund;
		// Keeps track of total running tally of reduced stock.
		$item->update_meta_data( '_reduced_stock', $item_stock_reduced );
		// Keeps track of only refunded items that needs restock.
		$item->update_meta_data( '_restock_refunded_items', $qty_to_refund + $restock_refunded_items );
		/* translators: 1: product ID 2: old stock level 3: new stock level */
		$restock_note = sprintf( __( 'Item #%1$s stock increased from %2$s to %3$s.', 'woocommerce' ), $product->get_id(), $old_stock, $new_stock );
		/**
		 * Allow the restock note to be modified.
		 *
		 * @since 6.4.0
		 *
		 * @param string $restock_note The original note.
		 * @param int $old_stock The old stock.
		 * @param bool|int|null $new_stock The new stock.
		 * @param WC_Order $order The order the refund was done for.
		 * @param bool|WC_Product $product The product the refund was done for.
		 */
		$restock_note = apply_filters( 'woocommerce_refund_restock_note', $restock_note, $old_stock, $new_stock, $order, $product );
		$order->add_order_note( $restock_note );
		$item->save();
		do_action( 'woocommerce_restock_refunded_item', $product->get_id(), $old_stock, $new_stock, $order, $product );
	}
}
/**
 * Get tax class by tax id.
 *
 * @since 2.2
 * @param int $tax_id Tax ID.
 * @return string
 */
function wc_get_tax_class_by_tax_id( $tax_id ) {
	global $wpdb;
	return $wpdb->get_var( $wpdb->prepare( "SELECT tax_rate_class FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %d", $tax_id ) );
}
/**
 * Get payment gateway class by order data.
 *
 * @since 2.2
 * @param int|WC_Order $order Order instance.
 * @return WC_Payment_Gateway|bool
 */
function wc_get_payment_gateway_by_order( $order ) {
	if ( WC()->payment_gateways() ) {
		$payment_gateways = WC()->payment_gateways()->payment_gateways();
	} else {
		$payment_gateways = array();
	}
	if ( ! is_object( $order ) ) {
		$order_id = absint( $order );
		$order    = wc_get_order( $order_id );
	}
	return is_a( $order, 'WC_Order' ) && isset( $payment_gateways[ $order->get_payment_method() ] ) ? $payment_gateways[ $order->get_payment_method() ] : false;
}
/**
 * When refunding an order, create a refund line item if the partial refunds do not match order total.
 *
 * This is manual; no gateway refund will be performed.
 *
 * @since 2.4
 * @param int $order_id Order ID.
 */
function wc_order_fully_refunded( $order_id ) {
	$order      = wc_get_order( $order_id );
	$max_refund = wc_format_decimal( $order->get_total() - $order->get_total_refunded() );
	if ( ! $max_refund ) {
		return;
	}
	// Create the refund object.
	wc_switch_to_site_locale();
	wc_create_refund(
		array(
			'amount'     => $max_refund,
			'reason'     => __( 'Order fully refunded.', 'woocommerce' ),
			'order_id'   => $order_id,
			'line_items' => array(),
		)
	);
	wc_restore_locale();
	$order->add_order_note( __( 'Order status set to refunded. To return funds to the customer you will need to issue a refund through your payment gateway.', 'woocommerce' ) );
}
add_action( 'woocommerce_order_status_refunded', 'wc_order_fully_refunded' );
/**
 * Search orders.
 *
 * @since  2.6.0
 * @param  string $term Term to search.
 * @return array List of orders ID.
 */
function wc_order_search( $term ) {
	$data_store = WC_Data_Store::load( 'order' );
	return $data_store->search_orders( str_replace( 'Order #', '', wc_clean( $term ) ) );
}
/**
 * Update total sales amount for each product within a paid order.
 *
 * @since 3.0.0
 * @param int $order_id Order ID.
 */
function wc_update_total_sales_counts( $order_id ) {
	$order = wc_get_order( $order_id );
	if ( ! $order ) {
		return;
	}
	$recorded_sales  = $order->get_data_store()->get_recorded_sales( $order );
	$reflected_order = in_array( $order->get_status(), array( 'cancelled', 'trash' ), true );
	if ( ! $reflected_order && 'woocommerce_before_delete_order' === current_action() ) {
		$reflected_order = true;
	}
	if ( $recorded_sales xor $reflected_order ) {
		return;
	}
	$operation = $recorded_sales && $reflected_order ? 'decrease' : 'increase';
	if ( count( $order->get_items() ) > 0 ) {
		foreach ( $order->get_items() as $item ) {
			$product_id = $item->get_product_id();
			if ( $product_id ) {
				$data_store = WC_Data_Store::load( 'product' );
				$data_store->update_product_sales( $product_id, absint( $item->get_quantity() ), $operation );
			}
		}
	}
	if ( 'decrease' === $operation ) {
		$order->get_data_store()->set_recorded_sales( $order, false );
	} else {
		$order->get_data_store()->set_recorded_sales( $order, true );
	}
	/**
	 * Called when sales for an order are recorded
	 *
	 * @param int $order_id order id
	 */
	do_action( 'woocommerce_recorded_sales', $order_id );
}
add_action( 'woocommerce_order_status_completed', 'wc_update_total_sales_counts' );
add_action( 'woocommerce_order_status_processing', 'wc_update_total_sales_counts' );
add_action( 'woocommerce_order_status_on-hold', 'wc_update_total_sales_counts' );
add_action( 'woocommerce_order_status_completed_to_cancelled', 'wc_update_total_sales_counts' );
add_action( 'woocommerce_order_status_processing_to_cancelled', 'wc_update_total_sales_counts' );
add_action( 'woocommerce_order_status_on-hold_to_cancelled', 'wc_update_total_sales_counts' );
add_action( 'woocommerce_trash_order', 'wc_update_total_sales_counts' );
add_action( 'woocommerce_untrash_order', 'wc_update_total_sales_counts' );
add_action( 'woocommerce_before_delete_order', 'wc_update_total_sales_counts' );
/**
 * Update used coupon amount for each coupon within an order.
 *
 * @since 3.0.0
 * @param int $order_id Order ID.
 */
function wc_update_coupon_usage_counts( $order_id ) {
	$order = wc_get_order( $order_id );
	if ( ! $order ) {
		return;
	}
	$has_recorded = $order->get_data_store()->get_recorded_coupon_usage_counts( $order );
	if ( $order->has_status( 'cancelled' ) && $has_recorded ) {
		$action = 'reduce';
		$order->get_data_store()->set_recorded_coupon_usage_counts( $order, false );
	} elseif ( ! $order->has_status( 'cancelled' ) && ! $has_recorded ) {
		$action = 'increase';
		$order->get_data_store()->set_recorded_coupon_usage_counts( $order, true );
	} elseif ( $order->has_status( 'cancelled' ) ) {
		$order->get_data_store()->release_held_coupons( $order, true );
		return;
	} else {
		return;
	}
	if ( count( $order->get_coupon_codes() ) > 0 ) {
		foreach ( $order->get_coupon_codes() as $code ) {
			if ( StringUtil::is_null_or_whitespace( $code ) ) {
				continue;
			}
			$coupon  = new WC_Coupon( $code );
			$used_by = $order->get_user_id();
			if ( ! $used_by ) {
				$used_by = $order->get_billing_email();
			}
			switch ( $action ) {
				case 'reduce':
					$coupon->decrease_usage_count( $used_by );
					break;
				case 'increase':
					$coupon->increase_usage_count( $used_by, $order );
					break;
			}
		}
		$order->get_data_store()->release_held_coupons( $order, true );
	}
}
add_action( 'woocommerce_order_status_pending', 'wc_update_coupon_usage_counts' );
add_action( 'woocommerce_order_status_completed', 'wc_update_coupon_usage_counts' );
add_action( 'woocommerce_order_status_processing', 'wc_update_coupon_usage_counts' );
add_action( 'woocommerce_order_status_on-hold', 'wc_update_coupon_usage_counts' );
add_action( 'woocommerce_order_status_cancelled', 'wc_update_coupon_usage_counts' );
/**
 * Cancel all unpaid orders after held duration to prevent stock lock for those products.
 */
function wc_cancel_unpaid_orders() {
	$held_duration = get_option( 'woocommerce_hold_stock_minutes' );
	// Re-schedule the event before cancelling orders
	// this way in case of a DB timeout or (plugin) crash the event is always scheduled for retry.
	wp_clear_scheduled_hook( 'woocommerce_cancel_unpaid_orders' );
	$cancel_unpaid_interval = apply_filters( 'woocommerce_cancel_unpaid_orders_interval_minutes', absint( $held_duration ) );
	wp_schedule_single_event( time() + ( absint( $cancel_unpaid_interval ) * 60 ), 'woocommerce_cancel_unpaid_orders' );
	if ( $held_duration < 1 || 'yes' !== get_option( 'woocommerce_manage_stock' ) ) {
		return;
	}
	$data_store    = WC_Data_Store::load( 'order' );
	$unpaid_orders = $data_store->get_unpaid_orders( strtotime( '-' . absint( $held_duration ) . ' MINUTES', current_time( 'timestamp' ) ) );
	if ( $unpaid_orders ) {
		foreach ( $unpaid_orders as $unpaid_order ) {
			$order = wc_get_order( $unpaid_order );
			if ( apply_filters( 'woocommerce_cancel_unpaid_order', 'checkout' === $order->get_created_via(), $order ) ) {
				$order->update_status( 'cancelled', __( 'Unpaid order cancelled - time limit reached.', 'woocommerce' ) );
			}
		}
	}
}
add_action( 'woocommerce_cancel_unpaid_orders', 'wc_cancel_unpaid_orders' );
/**
 * Sanitize order id removing unwanted characters.
 *
 * E.g Users can sometimes try to track an order id using # with no success.
 * This function will fix this.
 *
 * @since 3.1.0
 * @param int $order_id Order ID.
 */
function wc_sanitize_order_id( $order_id ) {
	return (int) filter_var( $order_id, FILTER_SANITIZE_NUMBER_INT );
}
add_filter( 'woocommerce_shortcode_order_tracking_order_id', 'wc_sanitize_order_id' );
/**
 * Get an order note.
 *
 * @since  3.2.0
 * @param  int|WP_Comment $data Note ID (or WP_Comment instance for internal use only).
 * @return stdClass|null        Object with order note details or null when does not exists.
 */
function wc_get_order_note( $data ) {
	if ( is_numeric( $data ) ) {
		$data = get_comment( $data );
	}
	if ( ! is_a( $data, 'WP_Comment' ) ) {
		return null;
	}
	return (object) apply_filters(
		'woocommerce_get_order_note',
		array(
			'id'            => (int) $data->comment_ID,
			'date_created'  => wc_string_to_datetime( $data->comment_date ),
			'content'       => $data->comment_content,
			'customer_note' => (bool) get_comment_meta( $data->comment_ID, 'is_customer_note', true ),
			'added_by'      => __( 'WooCommerce', 'woocommerce' ) === $data->comment_author ? 'system' : $data->comment_author,
		),
		$data
	);
}
/**
 * Get order notes.
 *
 * @since  3.2.0
 * @param  array $args Query arguments {
 *     Array of query parameters.
 *
 *     @type string $limit         Maximum number of notes to retrieve.
 *                                 Default empty (no limit).
 *     @type int    $order_id      Limit results to those affiliated with a given order ID.
 *                                 Default 0.
 *     @type array  $order__in     Array of order IDs to include affiliated notes for.
 *                                 Default empty.
 *     @type array  $order__not_in Array of order IDs to exclude affiliated notes for.
 *                                 Default empty.
 *     @type string $orderby       Define how should sort notes.
 *                                 Accepts 'date_created', 'date_created_gmt' or 'id'.
 *                                 Default: 'id'.
 *     @type string $order         How to order retrieved notes.
 *                                 Accepts 'ASC' or 'DESC'.
 *                                 Default: 'DESC'.
 *     @type string $type          Define what type of note should retrieve.
 *                                 Accepts 'customer', 'internal' or empty for both.
 *                                 Default empty.
 * }
 * @return stdClass[]              Array of stdClass objects with order notes details.
 */
function wc_get_order_notes( $args ) {
	$key_mapping = array(
		'limit'         => 'number',
		'order_id'      => 'post_id',
		'order__in'     => 'post__in',
		'order__not_in' => 'post__not_in',
	);
	foreach ( $key_mapping as $query_key => $db_key ) {
		if ( isset( $args[ $query_key ] ) ) {
			$args[ $db_key ] = $args[ $query_key ];
			unset( $args[ $query_key ] );
		}
	}
	// Define orderby.
	$orderby_mapping = array(
		'date_created'     => 'comment_date',
		'date_created_gmt' => 'comment_date_gmt',
		'id'               => 'comment_ID',
	);
	$args['orderby'] = ! empty( $args['orderby'] ) && in_array( $args['orderby'], array( 'date_created', 'date_created_gmt', 'id' ), true ) ? $orderby_mapping[ $args['orderby'] ] : 'comment_ID';
	// Set WooCommerce order type.
	if ( isset( $args['type'] ) && 'customer' === $args['type'] ) {
		$args['meta_query'] = array( // WPCS: slow query ok.
			array(
				'key'     => 'is_customer_note',
				'value'   => 1,
				'compare' => '=',
			),
		);
	} elseif ( isset( $args['type'] ) && 'internal' === $args['type'] ) {
		$args['meta_query'] = array( // WPCS: slow query ok.
			array(
				'key'     => 'is_customer_note',
				'compare' => 'NOT EXISTS',
			),
		);
	}
	// Set correct comment type.
	$args['type'] = 'order_note';
	// Always approved.
	$args['status'] = 'approve';
	// Does not support 'count' or 'fields'.
	unset( $args['count'], $args['fields'] );
	remove_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 );
	$notes = get_comments( $args );
	add_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 );
	return array_filter( array_map( 'wc_get_order_note', $notes ) );
}
/**
 * Create an order note.
 *
 * @since  3.2.0
 * @param  int    $order_id         Order ID.
 * @param  string $note             Note to add.
 * @param  bool   $is_customer_note If is a costumer note.
 * @param  bool   $added_by_user    If note is create by an user.
 * @return int|WP_Error             Integer when created or WP_Error when found an error.
 */
function wc_create_order_note( $order_id, $note, $is_customer_note = false, $added_by_user = false ) {
	$order = wc_get_order( $order_id );
	if ( ! $order ) {
		return new WP_Error( 'invalid_order_id', __( 'Invalid order ID.', 'woocommerce' ), array( 'status' => 400 ) );
	}
	return $order->add_order_note( $note, (int) $is_customer_note, $added_by_user );
}
/**
 * Delete an order note.
 *
 * @since  3.2.0
 * @param  int $note_id Order note.
 * @return bool         True on success, false on failure.
 */
function wc_delete_order_note( $note_id ) {
	return wp_delete_comment( $note_id, true );
}
PK     K[\,iT  T    class-wc-product-factory.phpnu „[µü¤        get_product_id( $product_id );
		if ( ! $product_id ) {
			return false;
		}
		$product_type = $this->get_product_type( $product_id );
		// Backwards compatibility.
		if ( ! empty( $deprecated ) ) {
			wc_deprecated_argument( 'args', '3.0', 'Passing args to the product factory is deprecated. If you need to force a type, construct the product class directly.' );
			if ( isset( $deprecated['product_type'] ) ) {
				$product_type = $this->get_classname_from_product_type( $deprecated['product_type'] );
			}
		}
		$classname = $this->get_product_classname( $product_id, $product_type );
		try {
			return new $classname( $product_id, $deprecated );
		} catch ( Exception $e ) {
			return false;
		}
	}
	/**
	 * Gets a product classname and allows filtering. Returns WC_Product_Simple if the class does not exist.
	 *
	 * @since  3.0.0
	 * @param  int    $product_id   Product ID.
	 * @param  string $product_type Product type.
	 * @return string
	 */
	public static function get_product_classname( $product_id, $product_type ) {
		$classname = apply_filters( 'woocommerce_product_class', self::get_classname_from_product_type( $product_type ), $product_type, 'variation' === $product_type ? 'product_variation' : 'product', $product_id );
		if ( ! $classname || ! class_exists( $classname ) ) {
			$classname = 'WC_Product_Simple';
		}
		return $classname;
	}
	/**
	 * Get the product type for a product.
	 *
	 * @since 3.0.0
	 * @param  int $product_id Product ID.
	 * @return string|false
	 */
	public static function get_product_type( $product_id ) {
		// Allow the overriding of the lookup in this function. Return the product type here.
		$override = apply_filters( 'woocommerce_product_type_query', false, $product_id );
		if ( ! $override ) {
			return WC_Data_Store::load( 'product' )->get_product_type( $product_id );
		} else {
			return $override;
		}
	}
	/**
	 * Create a WC coding standards compliant class name e.g. WC_Product_Type_Class instead of WC_Product_type-class.
	 *
	 * @param  string $product_type Product type.
	 * @return string|false
	 */
	public static function get_classname_from_product_type( $product_type ) {
		return $product_type ? 'WC_Product_' . implode( '_', array_map( 'ucfirst', explode( '-', $product_type ) ) ) : false;
	}
	/**
	 * Get the product ID depending on what was passed.
	 *
	 * @since  3.0.0
	 * @param  WC_Product|WP_Post|int|bool $product Product instance, post instance, numeric or false to use global $post.
	 * @return int|bool false on failure
	 */
	private function get_product_id( $product ) {
		global $post;
		if ( false === $product && isset( $post, $post->ID ) && 'product' === get_post_type( $post->ID ) ) {
			return absint( $post->ID );
		} elseif ( is_numeric( $product ) ) {
			return $product;
		} elseif ( $product instanceof WC_Product ) {
			return $product->get_id();
		} elseif ( ! empty( $product->ID ) ) {
			return $product->ID;
		} else {
			return false;
		}
	}
}
PK     K[¶3có  ó  &