From 33a2e69ef7be7afea3b2b23dca58b836caed6caf Mon Sep 17 00:00:00 2001 From: Petar Date: Sun, 28 Jun 2020 14:27:23 +0200 Subject: [PATCH 1/3] Add dynamic shipping tax calculation --- Model/Config.php | 3 + Model/System/Config/Source/Tax/Dynamic.php | 38 +++++ Plugin/Email/Model/Source/Variables.php | 4 +- Plugin/Tax/Config/ShippingTaxPlugin.php | 156 +++++++++++++++++++++ composer.json | 14 +- etc/adminhtml/system.xml | 8 ++ etc/di.xml | 4 + etc/module.xml | 6 +- i18n/de_DE.csv | 2 + 9 files changed, 229 insertions(+), 6 deletions(-) create mode 100644 Model/System/Config/Source/Tax/Dynamic.php create mode 100644 Plugin/Tax/Config/ShippingTaxPlugin.php diff --git a/Model/Config.php b/Model/Config.php index 0693431..7f8e3fe 100644 --- a/Model/Config.php +++ b/Model/Config.php @@ -13,6 +13,9 @@ */ class Config implements ConfigInterface { + public const DYNAMIC_TYPE_DEFAULT = 0; + public const DYNAMIC_TYPE_HIGHEST_PRODUCT_TAX = 1; + /** * Configuration reader * diff --git a/Model/System/Config/Source/Tax/Dynamic.php b/Model/System/Config/Source/Tax/Dynamic.php new file mode 100644 index 0000000..874c160 --- /dev/null +++ b/Model/System/Config/Source/Tax/Dynamic.php @@ -0,0 +1,38 @@ +options) { + $options = [ + [ + 'value' => FireGentoConfig::DYNAMIC_TYPE_DEFAULT, + 'label' => __('No dynamic shipping tax calculation') + ], + [ + 'value' => FireGentoConfig::DYNAMIC_TYPE_HIGHEST_PRODUCT_TAX, + 'label' => __('Use the highest product tax') + ] + ]; + + $this->options = $options; + } + + return $this->options; + } +} diff --git a/Plugin/Email/Model/Source/Variables.php b/Plugin/Email/Model/Source/Variables.php index 8685628..feafcb7 100644 --- a/Plugin/Email/Model/Source/Variables.php +++ b/Plugin/Email/Model/Source/Variables.php @@ -8,7 +8,9 @@ namespace FireGento\MageSetup\Plugin\Email\Model\Source; /** - * Plugin to add additional config variables to be inserted into emails. + * Class Variables + * + * @package FireGento\MageSetup\Plugin\Email\Model\Source */ class Variables { diff --git a/Plugin/Tax/Config/ShippingTaxPlugin.php b/Plugin/Tax/Config/ShippingTaxPlugin.php new file mode 100644 index 0000000..88fe666 --- /dev/null +++ b/Plugin/Tax/Config/ShippingTaxPlugin.php @@ -0,0 +1,156 @@ +scopeConfig = $scopeConfig; + $this->cart = $cart; + $this->customerSession = $customerSession; + $this->groupRepository = $groupRepository; + $this->taxCalculation = $taxCalculation; + } + + public function afterGetShippingTaxClass(Config $config, int $shippingTaxClass, $store = null) + { + $dynamicType = (int)$this->scopeConfig->getValue( + self::CONFIG_PATH_DYNAMIC_SHIPPING_TAX_CLASS, + ScopeInterface::SCOPE_STORE, + $store + ); + + $quoteItems = $this->cart->getItems(); + + // If the default behaviour was configured or there are no products in cart, use default tax class id + if ($dynamicType === FireGentoConfig::DYNAMIC_TYPE_DEFAULT || count($quoteItems) === 0) { + return $shippingTaxClass; + } + + $taxClassId = false; + + // Retrieve the highest product tax class + if ($dynamicType === FireGentoConfig::DYNAMIC_TYPE_HIGHEST_PRODUCT_TAX) { + $taxClassId = $this->getHighestProductTaxClassId($quoteItems, $store); + } + + // If no tax class id was found, use default one + if (!$taxClassId) { + $taxClassId = $shippingTaxClass; + } + + return $taxClassId; + } + + /** + * @param array $quoteItems + * @param null|string|bool|int|Store $store + * + * @return bool|mixed|null + */ + private function getHighestProductTaxClassId($quoteItems, $store) + { + $taxClassIds = []; + $highestTaxRate = null; + + foreach ($quoteItems as $quoteItem) { + /** @var $quoteItem Item */ + if ($quoteItem->getParentItem()) { + continue; + } + + // Retrieve the tax percent + $taxPercent = $quoteItem->getTaxPercent(); + if (!$taxPercent) { + $taxPercent = $this->getTaxPercent($quoteItem->getTaxClassId(), $store); + } + + // Add the tax class + if (($taxPercent) && !in_array($taxPercent, $taxClassIds)) { + $taxClassIds[$taxPercent] = $quoteItem->getTaxClassId(); + } + } + + // Fetch the highest tax rate + ksort($taxClassIds); + if (count($taxClassIds) > 0) { + $highestTaxRate = array_pop($taxClassIds); + } + if (!$highestTaxRate || is_null($highestTaxRate)) { + return false; + } + + return $highestTaxRate; + } + + private function getTaxPercent(int $productTaxClassId, $store): int + { + $groupId = $this->customerSession->getCustomerGroupId(); + $group = $this->groupRepository->getById($groupId); + $customerTaxClassId = $group->getTaxClassId(); + + $request = $this->taxCalculation->getRateRequest(null, null, $customerTaxClassId, $store); + $request->setData('product_class_id', $productTaxClassId); + + $taxPercent = $this->taxCalculation->getRate($request); + if (!$taxPercent) { + $taxPercent = 0; + } + + return $taxPercent; + } +} diff --git a/composer.json b/composer.json index 6c835da..8e66d39 100644 --- a/composer.json +++ b/composer.json @@ -2,10 +2,16 @@ "name": "firegento/magesetup2", "description": "MageSetup provides the necessary configuration (system config, tax, agreements, etc. for a national market.", "require": { - "php": "~7.2.0||~7.3.0", - "magento/module-store": "*", - "magento/module-backend": "*", - "magento/framework": "*" + "php": "~7.1.0|~7.2.0|~7.3.0", + "magento/framework": "*", + "magento/module-backend": "*", + "magento/module-catalog": "*", + "magento/module-checkout": "*", + "magento/module-configurable-product": "*", + "magento/module-customer": "*", + "magento/module-quote": "*", + "magento/module-store": "*", + "magento/module-tax": "*" }, "type": "magento2-module", "license": "GPL-3.0", diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index e1842de..2fab24d 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -142,5 +142,13 @@ +
+ + + + FireGento\MageSetup\Model\System\Config\Source\Tax\Dynamic + + +
diff --git a/etc/di.xml b/etc/di.xml index 62be3c4..f77877e 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -57,6 +57,10 @@ + + + + diff --git a/etc/module.xml b/etc/module.xml index f7c9eb2..28a299e 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -8,10 +8,14 @@ - + + + + + diff --git a/i18n/de_DE.csv b/i18n/de_DE.csv index 59d0888..be042ff 100644 --- a/i18n/de_DE.csv +++ b/i18n/de_DE.csv @@ -55,3 +55,5 @@ SWIFT,SWIFT "CMS Page for Shipping Info","CMS-Seite für Versandkosten" "Show ""incl. Shipping Cost"" instead of ""excl. Shipping Cost""","Zeige ""inkl. Versandkosten"" anstatt ""exkl. Versandkosten""" "Display Delivery Time on Product Listing","Lieferzeit auf Produktübersichtsseiten anzeigen" +"No dynamic shipping tax calculation","Keine dynamische Versandsteuerberechnung" +"Use the highest product tax","Verwenden Sie die höchste Produktsteuer" From 48d7a9acdc78586bf1de2832b3c1b375aee33236 Mon Sep 17 00:00:00 2001 From: Petar Date: Sun, 28 Jun 2020 15:57:19 +0200 Subject: [PATCH 2/3] Fix review issues and travis errors --- Model/Config.php | 24 +++++++---- Model/System/Config/Source/Tax/Dynamic.php | 18 ++++++-- Plugin/Email/Model/Source/Variables.php | 4 +- Plugin/Tax/Config/ShippingTaxPlugin.php | 48 +++++++++++++++++----- composer.json | 2 +- i18n/en_US.csv | 3 ++ 6 files changed, 74 insertions(+), 25 deletions(-) diff --git a/Model/Config.php b/Model/Config.php index 7f8e3fe..51a3f3b 100644 --- a/Model/Config.php +++ b/Model/Config.php @@ -3,6 +3,7 @@ * Copyright © 2016 FireGento e.V. * See LICENSE.md bundled with this module for license details. */ + namespace FireGento\MageSetup\Model; use FireGento\MageSetup\Model\Config\Reader; @@ -13,8 +14,7 @@ */ class Config implements ConfigInterface { - public const DYNAMIC_TYPE_DEFAULT = 0; - public const DYNAMIC_TYPE_HIGHEST_PRODUCT_TAX = 1; + public const CONFIG_PATH_DYNAMIC_SHIPPING_TAX_CLASS = 'tax/classes/dynamic_shipping_tax_class'; /** * Configuration reader @@ -43,17 +43,17 @@ class Config implements ConfigInterface /** * Config constructor. * - * @param Reader $reader + * @param Reader $reader * @param CacheInterface $cache - * @param mixed $country + * @param mixed $country */ public function __construct( Reader $reader, CacheInterface $cache, $country ) { - $this->reader = $reader; - $this->cache = $cache; + $this->reader = $reader; + $this->cache = $cache; $this->country = $country; $this->initialize(); @@ -191,7 +191,17 @@ public function getCmsBlocks() */ private function initialize() { - $data = $this->reader->read(); + $data = $this->reader->read(); $this->loadedConfig = $data; } + + /** + * Method for retrieving dynamic config shipping path + * + * @return string + */ + public function getDynamicShippingConfigPath(): string + { + return self::CONFIG_PATH_DYNAMIC_SHIPPING_TAX_CLASS; + } } diff --git a/Model/System/Config/Source/Tax/Dynamic.php b/Model/System/Config/Source/Tax/Dynamic.php index 874c160..b12d6af 100644 --- a/Model/System/Config/Source/Tax/Dynamic.php +++ b/Model/System/Config/Source/Tax/Dynamic.php @@ -6,26 +6,36 @@ namespace FireGento\MageSetup\Model\System\Config\Source\Tax; -use FireGento\MageSetup\Model\Config as FireGentoConfig; use Magento\Framework\Data\OptionSourceInterface; +/** + * Source model for Dynamic taxes. + */ class Dynamic implements OptionSourceInterface { + public const DYNAMIC_TYPE_SHIPPING_TAX_DEFAULT = 0; + public const DYNAMIC_TYPE_HIGHEST_PRODUCT_TAX = 1; + /** * @var array */ - protected $options; + private $options; + /** + * Method for returning options array + * + * @return array|array[] + */ public function toOptionArray(): array { if (null === $this->options) { $options = [ [ - 'value' => FireGentoConfig::DYNAMIC_TYPE_DEFAULT, + 'value' => self::DYNAMIC_TYPE_SHIPPING_TAX_DEFAULT, 'label' => __('No dynamic shipping tax calculation') ], [ - 'value' => FireGentoConfig::DYNAMIC_TYPE_HIGHEST_PRODUCT_TAX, + 'value' => self::DYNAMIC_TYPE_HIGHEST_PRODUCT_TAX, 'label' => __('Use the highest product tax') ] ]; diff --git a/Plugin/Email/Model/Source/Variables.php b/Plugin/Email/Model/Source/Variables.php index feafcb7..8685628 100644 --- a/Plugin/Email/Model/Source/Variables.php +++ b/Plugin/Email/Model/Source/Variables.php @@ -8,9 +8,7 @@ namespace FireGento\MageSetup\Plugin\Email\Model\Source; /** - * Class Variables - * - * @package FireGento\MageSetup\Plugin\Email\Model\Source + * Plugin to add additional config variables to be inserted into emails. */ class Variables { diff --git a/Plugin/Tax/Config/ShippingTaxPlugin.php b/Plugin/Tax/Config/ShippingTaxPlugin.php index 88fe666..c2f6947 100644 --- a/Plugin/Tax/Config/ShippingTaxPlugin.php +++ b/Plugin/Tax/Config/ShippingTaxPlugin.php @@ -5,6 +5,7 @@ namespace FireGento\MageSetup\Plugin\Tax\Config; use FireGento\MageSetup\Model\Config as FireGentoConfig; +use FireGento\MageSetup\Model\System\Config\Source\Tax\Dynamic as FireGentoSource; use Magento\Checkout\Model\Cart; use Magento\Customer\Model\ResourceModel\GroupRepository; use Magento\Customer\Model\Session; @@ -15,9 +16,13 @@ use Magento\Tax\Model\Calculation\Proxy; use Magento\Tax\Model\Config; +/** + * Class ShippingTaxPlugin + * + * FireGento\MageSetup\Plugin\Tax\Config + */ class ShippingTaxPlugin { - public const CONFIG_PATH_DYNAMIC_SHIPPING_TAX_CLASS = 'tax/classes/dynamic_shipping_tax_class'; /** * @var ScopeConfigInterface @@ -45,31 +50,38 @@ class ShippingTaxPlugin private $taxCalculation; /** + * Constructor class * * @param ScopeConfigInterface $scopeConfig * @param Cart $cart * @param Session $customerSession * @param GroupRepository $groupRepository - * @param Proxy $taxCalculation */ public function __construct( ScopeConfigInterface $scopeConfig, Cart $cart, Session $customerSession, - GroupRepository $groupRepository, - Proxy $taxCalculation + GroupRepository $groupRepository ) { $this->scopeConfig = $scopeConfig; $this->cart = $cart; $this->customerSession = $customerSession; $this->groupRepository = $groupRepository; - $this->taxCalculation = $taxCalculation; } + /** + * After plugin for \Magento\Tax\Model\Config::getShippingTaxClass + * + * @param Config $config + * @param int $shippingTaxClass + * @param null $store + * + * @return bool|int|mixed + */ public function afterGetShippingTaxClass(Config $config, int $shippingTaxClass, $store = null) { $dynamicType = (int)$this->scopeConfig->getValue( - self::CONFIG_PATH_DYNAMIC_SHIPPING_TAX_CLASS, + FireGentoConfig::CONFIG_PATH_DYNAMIC_SHIPPING_TAX_CLASS, ScopeInterface::SCOPE_STORE, $store ); @@ -77,14 +89,14 @@ public function afterGetShippingTaxClass(Config $config, int $shippingTaxClass, $quoteItems = $this->cart->getItems(); // If the default behaviour was configured or there are no products in cart, use default tax class id - if ($dynamicType === FireGentoConfig::DYNAMIC_TYPE_DEFAULT || count($quoteItems) === 0) { + if ($dynamicType === FireGentoSource::DYNAMIC_TYPE_SHIPPING_TAX_DEFAULT || count($quoteItems) === 0) { return $shippingTaxClass; } $taxClassId = false; // Retrieve the highest product tax class - if ($dynamicType === FireGentoConfig::DYNAMIC_TYPE_HIGHEST_PRODUCT_TAX) { + if ($dynamicType === FireGentoSource::DYNAMIC_TYPE_HIGHEST_PRODUCT_TAX) { $taxClassId = $this->getHighestProductTaxClassId($quoteItems, $store); } @@ -97,17 +109,21 @@ public function afterGetShippingTaxClass(Config $config, int $shippingTaxClass, } /** + * Method for getting highest product tax class id + * * @param array $quoteItems * @param null|string|bool|int|Store $store * * @return bool|mixed|null */ + private function getHighestProductTaxClassId($quoteItems, $store) { $taxClassIds = []; $highestTaxRate = null; foreach ($quoteItems as $quoteItem) { + /** @var $quoteItem Item */ if ($quoteItem->getParentItem()) { continue; @@ -125,18 +141,30 @@ private function getHighestProductTaxClassId($quoteItems, $store) } } - // Fetch the highest tax rate + /** + * Fetch the highest tax rate + */ ksort($taxClassIds); if (count($taxClassIds) > 0) { $highestTaxRate = array_pop($taxClassIds); } - if (!$highestTaxRate || is_null($highestTaxRate)) { + if (!$highestTaxRate || $highestTaxRate === null) { return false; } return $highestTaxRate; } + /** + * Method for getting tax prcentage + * + * @param int $productTaxClassId + * @param null|string|bool|int|Store $store + * + * @return int + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ private function getTaxPercent(int $productTaxClassId, $store): int { $groupId = $this->customerSession->getCustomerGroupId(); diff --git a/composer.json b/composer.json index 8e66d39..23720ae 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "firegento/magesetup2", "description": "MageSetup provides the necessary configuration (system config, tax, agreements, etc. for a national market.", "require": { - "php": "~7.1.0|~7.2.0|~7.3.0", + "php": "~7.2.0|~7.3.0", "magento/framework": "*", "magento/module-backend": "*", "magento/module-catalog": "*", diff --git a/i18n/en_US.csv b/i18n/en_US.csv index eeefbb9..1163c94 100644 --- a/i18n/en_US.csv +++ b/i18n/en_US.csv @@ -1,6 +1,8 @@ %1%,%1% "SubProcessor %1 does not exist.","SubProcessor %1 does not exist." "-- No Page --","-- No Page --" +"No dynamic shipping tax calculation","No dynamic shipping tax calculation" +"Use the highest product tax","Use the highest product tax" "Visible in Checkout","Visible in Checkout" Imprint,Imprint "Shop Name","Shop Name" @@ -55,3 +57,4 @@ SWIFT,SWIFT "CMS Page for Shipping Info","CMS Page for Shipping Info" "Show ""incl. Shipping Cost"" instead of ""excl. Shipping Cost""","Show ""incl. Shipping Cost"" instead of ""excl. Shipping Cost""" "Display Delivery Time on Product Listing","Display Delivery Time on Product Listing" +"Dynamic Shipping Tax Class","Dynamic Shipping Tax Class" From 9bb2804be40f380be507e1d10035bc5fd15a78a2 Mon Sep 17 00:00:00 2001 From: Petar Date: Sun, 28 Jun 2020 16:03:51 +0200 Subject: [PATCH 3/3] Use method to get configuration --- Plugin/Tax/Config/ShippingTaxPlugin.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Plugin/Tax/Config/ShippingTaxPlugin.php b/Plugin/Tax/Config/ShippingTaxPlugin.php index c2f6947..4d3cb86 100644 --- a/Plugin/Tax/Config/ShippingTaxPlugin.php +++ b/Plugin/Tax/Config/ShippingTaxPlugin.php @@ -49,6 +49,11 @@ class ShippingTaxPlugin */ private $taxCalculation; + /** + * @var FireGentoConfig + */ + private $config; + /** * Constructor class * @@ -56,17 +61,20 @@ class ShippingTaxPlugin * @param Cart $cart * @param Session $customerSession * @param GroupRepository $groupRepository + * @param FireGentoConfig $config */ public function __construct( ScopeConfigInterface $scopeConfig, Cart $cart, Session $customerSession, - GroupRepository $groupRepository + GroupRepository $groupRepository, + FireGentoConfig $config ) { $this->scopeConfig = $scopeConfig; $this->cart = $cart; $this->customerSession = $customerSession; $this->groupRepository = $groupRepository; + $this->config = $config; } /** @@ -81,7 +89,7 @@ public function __construct( public function afterGetShippingTaxClass(Config $config, int $shippingTaxClass, $store = null) { $dynamicType = (int)$this->scopeConfig->getValue( - FireGentoConfig::CONFIG_PATH_DYNAMIC_SHIPPING_TAX_CLASS, + $this->config->getDynamicShippingConfigPath(), ScopeInterface::SCOPE_STORE, $store );