File "Settings.php"

Full Path: /home/warrior1/public_html/wp-content/plugins/google-listings-and-ads/src/API/Google/Settings.php
File size: 10.69 KB
MIME-type: text/x-php
Charset: utf-8

<?php
declare( strict_types=1 );

namespace Automattic\WooCommerce\GoogleListingsAndAds\API\Google;

use Automattic\WooCommerce\GoogleListingsAndAds\DB\Query\ShippingRateQuery as RateQuery;
use Automattic\WooCommerce\GoogleListingsAndAds\DB\Query\ShippingTimeQuery as TimeQuery;
use Automattic\WooCommerce\GoogleListingsAndAds\MerchantCenter\TargetAudience;
use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsInterface;
use Automattic\WooCommerce\GoogleListingsAndAds\Proxies\WC;
use Automattic\WooCommerce\GoogleListingsAndAds\Shipping\CountryRatesCollection;
use Automattic\WooCommerce\GoogleListingsAndAds\Shipping\GoogleAdapter\DBShippingSettingsAdapter;
use Automattic\WooCommerce\GoogleListingsAndAds\Shipping\GoogleAdapter\WCShippingSettingsAdapter;
use Automattic\WooCommerce\GoogleListingsAndAds\Shipping\ShippingZone;
use Google\Service\ShoppingContent;
use Google\Service\ShoppingContent\AccountAddress;
use Google\Service\ShoppingContent\AccountTax;
use Google\Service\ShoppingContent\AccountTaxTaxRule as TaxRule;
use Google\Service\ShoppingContent\ShippingSettings;
use Psr\Container\ContainerInterface;

defined( 'ABSPATH' ) || exit;

/**
 * Class Settings
 *
 * @package Automattic\WooCommerce\GoogleListingsAndAds\API\Google
 */
class Settings {

	use LocationIDTrait;

	/** @var ContainerInterface */
	protected $container;

	/**
	 * Settings constructor.
	 *
	 * @param ContainerInterface $container
	 */
	public function __construct( ContainerInterface $container ) {
		$this->container = $container;
	}

	/**
	 * Sync the shipping settings with Google.
	 */
	public function sync_shipping() {
		if ( ! $this->should_sync_shipping() ) {
			return;
		}

		$settings = $this->generate_shipping_settings();

		$this->get_shopping_service()->shippingsettings->update(
			$this->get_merchant_id(),
			$this->get_account_id(),
			$settings
		);
	}

	/**
	 * Whether we should synchronize settings with the Merchant Center
	 *
	 * @return bool
	 */
	protected function should_sync_shipping(): bool {
		$shipping_rate = $this->get_settings()['shipping_rate'] ?? '';
		$shipping_time = $this->get_settings()['shipping_time'] ?? '';
		return in_array( $shipping_rate, [ 'flat', 'automatic' ], true ) && 'flat' === $shipping_time;
	}

	/**
	 * Whether we should get the shipping settings from the WooCommerce settings.
	 *
	 * @return bool
	 *
	 * @since 1.12.0
	 */
	public function should_get_shipping_rates_from_woocommerce(): bool {
		return 'automatic' === ( $this->get_settings()['shipping_rate'] ?? '' );
	}

	/**
	 * Generate a ShippingSettings object for syncing the store shipping settings to Merchant Center.
	 *
	 * @return ShippingSettings
	 *
	 * @since 2.1.0
	 */
	protected function generate_shipping_settings(): ShippingSettings {
		$times = $this->get_shipping_times();

		/** @var WC $wc_proxy */
		$wc_proxy = $this->container->get( WC::class );
		$currency = $wc_proxy->get_woocommerce_currency();

		if ( $this->should_get_shipping_rates_from_woocommerce() ) {
			return new WCShippingSettingsAdapter(
				[
					'currency'          => $currency,
					'rates_collections' => $this->get_shipping_rates_collections_from_woocommerce(),
					'delivery_times'    => $times,
					'accountId'         => $this->get_account_id(),
				]
			);
		}

		return new DBShippingSettingsAdapter(
			[
				'currency'       => $currency,
				'db_rates'       => $this->get_shipping_rates_from_database(),
				'delivery_times' => $times,
				'accountId'      => $this->get_account_id(),
			]
		);
	}

	/**
	 * Get the current tax settings from the API.
	 *
	 * @return AccountTax
	 */
	public function get_taxes(): AccountTax {
		return $this->get_shopping_service()->accounttax->get(
			$this->get_merchant_id(),
			$this->get_account_id()
		);
	}

	/**
	 * Whether we should sync tax settings.
	 *
	 * This depends on the store being in the US
	 *
	 * @return bool
	 */
	protected function should_sync_taxes(): bool {
		if ( 'US' !== $this->get_store_country() ) {
			return false;
		}

		return 'destination' === ( $this->get_settings()['tax_rate'] ?? 'destination' );
	}

	/**
	 * Sync tax setting with Google.
	 */
	public function sync_taxes() {
		if ( ! $this->should_sync_taxes() ) {
			return;
		}

		$taxes = new AccountTax();
		$taxes->setAccountId( $this->get_account_id() );

		$tax_rule = new TaxRule();
		$tax_rule->setUseGlobalRate( true );
		$tax_rule->setLocationId( $this->get_state_id( $this->get_store_state() ) );
		$tax_rule->setCountry( $this->get_store_country() );

		$taxes->setRules( [ $tax_rule ] );

		$this->get_shopping_service()->accounttax->update(
			$this->get_merchant_id(),
			$this->get_account_id(),
			$taxes
		);
	}

	/**
	 * Get shipping time data.
	 *
	 * @return array
	 */
	protected function get_shipping_times(): array {
		static $times = null;

		if ( null === $times ) {
			$time_query = $this->container->get( TimeQuery::class );
			$times      = array_column( $time_query->get_results(), 'time', 'country' );
		}

		return $times;
	}

	/**
	 * Get shipping rate data.
	 *
	 * @return array
	 */
	protected function get_shipping_rates_from_database(): array {
		$rate_query = $this->container->get( RateQuery::class );

		return $rate_query->get_results();
	}

	/**
	 * Get shipping rate data from WooCommerce shipping settings.
	 *
	 * @return CountryRatesCollection[] Array of rates collections for each target country specified in settings.
	 */
	protected function get_shipping_rates_collections_from_woocommerce(): array {
		/** @var TargetAudience $target_audience */
		$target_audience  = $this->container->get( TargetAudience::class );
		$target_countries = $target_audience->get_target_countries();
		/** @var ShippingZone $shipping_zone */
		$shipping_zone = $this->container->get( ShippingZone::class );

		$rates = [];
		foreach ( $target_countries as $country ) {
			$location_rates    = $shipping_zone->get_shipping_rates_for_country( $country );
			$rates[ $country ] = new CountryRatesCollection( $country, $location_rates );
		}

		return $rates;
	}

	/**
	 * @return OptionsInterface
	 */
	protected function get_options_object(): OptionsInterface {
		return $this->container->get( OptionsInterface::class );
	}

	/**
	 * Get the Merchant ID
	 *
	 * @return int
	 */
	protected function get_merchant_id(): int {
		return $this->get_options_object()->get( OptionsInterface::MERCHANT_ID );
	}

	/**
	 * Get the account ID.
	 *
	 * @return int
	 */
	protected function get_account_id(): int {
		// todo: there are some cases where this might be different than the Merchant ID.
		return $this->get_merchant_id();
	}

	/**
	 * Get the Shopping Service object.
	 *
	 * @return ShoppingContent
	 */
	protected function get_shopping_service(): ShoppingContent {
		return $this->container->get( ShoppingContent::class );
	}

	/**
	 * Get the country for the store.
	 *
	 * @return string
	 */
	protected function get_store_country(): string {
		/** @var WC $wc */
		$wc = $this->container->get( WC::class );

		return $wc->get_wc_countries()->get_base_country();
	}

	/**
	 * Get the state for the store.
	 *
	 * @return string
	 */
	protected function get_store_state(): string {
		/** @var WC $wc */
		$wc = $this->container->get( WC::class );

		return $wc->get_wc_countries()->get_base_state();
	}

	/**
	 * Get the WooCommerce store physical address.
	 *
	 * @return AccountAddress
	 *
	 * @since 1.4.0
	 */
	public function get_store_address(): AccountAddress {
		/** @var WC $wc */
		$wc = $this->container->get( WC::class );

		$countries   = $wc->get_wc_countries();
		$postal_code = ! empty( $countries->get_base_postcode() ) ? $countries->get_base_postcode() : null;
		$locality    = ! empty( $countries->get_base_city() ) ? $countries->get_base_city() : null;
		$country     = ! empty( $countries->get_base_country() ) ? $countries->get_base_country() : null;
		$region      = ! empty( $countries->get_base_state() ) ? $countries->get_base_state() : null;

		$mc_address = new AccountAddress();
		$mc_address->setPostalCode( $postal_code );
		$mc_address->setLocality( $locality );
		$mc_address->setCountry( $country );

		if ( ! empty( $region ) && ! empty( $country ) ) {
			$mc_address->setRegion( $this->maybe_get_state_name( $region, $country ) );
		}

		$address   = ! empty( $countries->get_base_address() ) ? $countries->get_base_address() : null;
		$address_2 = ! empty( $countries->get_base_address_2() ) ? $countries->get_base_address_2() : null;
		$separator = ! empty( $address ) && ! empty( $address_2 ) ? "\n" : '';
		$address   = sprintf( '%s%s%s', $countries->get_base_address(), $separator, $countries->get_base_address_2() );
		if ( ! empty( $address ) ) {
			$mc_address->setStreetAddress( $address );
		}

		return $mc_address;
	}

	/**
	 * Check whether the address has errors
	 *
	 * @param AccountAddress $address to be validated.
	 *
	 * @return array
	 */
	public function wc_address_errors( AccountAddress $address ): array {
		/** @var WC $wc */
		$wc = $this->container->get( WC::class );

		$countries = $wc->get_wc_countries();

		$locale          = $countries->get_country_locale();
		$locale_settings = $locale[ $address->getCountry() ] ?? [];

		$fields_to_validate = [
			'address_1' => $address->getStreetAddress(),
			'city'      => $address->getLocality(),
			'country'   => $address->getCountry(),
			'postcode'  => $address->getPostalCode(),
		];

		return $this->validate_address( $fields_to_validate, $locale_settings );
	}

	/**
	 * Check whether the required address fields are empty
	 *
	 * @param array $address_fields to be validated.
	 * @param array $locale_settings locale settings
	 * @return array
	 */
	public function validate_address( array $address_fields, array $locale_settings ): array {
		$errors = array_filter(
			$address_fields,
			function ( $field ) use ( $locale_settings, $address_fields ) {
				$is_required = $locale_settings[ $field ]['required'] ?? true;
				return $is_required && empty( $address_fields[ $field ] );
			},
			ARRAY_FILTER_USE_KEY
		);

		return array_keys( $errors );
	}

	/**
	 * Return a state name.
	 *
	 * @param string $state_code State code.
	 * @param string $country    Country code.
	 *
	 * @return string
	 *
	 * @since 1.4.0
	 */
	protected function maybe_get_state_name( string $state_code, string $country ): string {
		/** @var WC $wc */
		$wc = $this->container->get( WC::class );

		$states = $country ? array_filter( (array) $wc->get_wc_countries()->get_states( $country ) ) : [];

		if ( ! empty( $states ) ) {
			$state_code = wc_strtoupper( $state_code );
			if ( isset( $states[ $state_code ] ) ) {
				return $states[ $state_code ];
			}
		}

		return $state_code;
	}

	/**
	 * Get the array of settings for the Merchant Center.
	 *
	 * @return array
	 */
	protected function get_settings(): array {
		$settings = $this->get_options_object()->get( OptionsInterface::MERCHANT_CENTER );
		return is_array( $settings ) ? $settings : [];
	}
}