diff --git a/CHANGELOG.md b/CHANGELOG.md
index 55f2cc4ad577c..f08223e7f01d3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,67 @@
+2.0.0.0-dev79
+=============
+* Tax calculation updates:
+ * Fixed issues in tax calculation rounding with discount applied
+ * Fixed an issue with extra penny when exact tax amount ended with 0.5 cent
+ * Fixed an issue where there were tax calculation errors when customer tax rate was different from store tax rate
+ * Added support to round tax at individual tax rate
+ * Fixed price inconsistencies between catalog and shopping cart
+ * Added support for maintaining consistent price including tax for customers with different tax rates
+ * Added support for applying tax rules with different priorities to subtotal only
+* Fixed bugs:
+ * Removed the extra '%' sign in the error\notice message on Gift Card Accounts page on the backend
+ * Fixed an issue with image uploading functionality in the Catalog configuration
+ * Fixed an issue where a customer could not navigate the store when downloading the downloadable product
+ * Fixed an issue where adding CMS block Catalog Events Lister caused an error
+ * Fixed an issue where the price was displayed twice on the Product page on the frontend
+ * Fixed an issue where an admin could not open search results on the backend
+ * Fixed an issue where the Rule Based Product Relations functionality was generating incorrect SQL when product category attribute was set through "is one of" or "contains" operator by constant value
+ * Fixed an issue where it was impossible to add a product to the Compare list for categories with three-column page layout
+ * Fixed an issue where a blank page opened when changing store view on a product page on the frontend
+ * Fixed an issue where the "Please specify at least one search term." error message was not displayed if search is performed without search data specified on the frontend
+ * Fixed a Google Chrome specific issue where page layout was broken when updating status for reviews on the backend
+ * Fixed admin look and feel issues
+ * Fixed an issue where the order notices and error messages were not red
+ * Fixed a UI issue which appeared during custom attribute creation
+ * Fixed an issue where the popup did not open after clicking What's this? next to the Remember Me check box when persistent shopping cart was enabled
+ * Fixed an issue where the options of the Add Product split dropdown did not fit the page
+ * Fixed an issue where the default theme preview image sample link was missing
+ * Fixed a Safari and Internet Explorer 9 specific issue where the backend menu is not displayed for users with custom admin roles
+ * Fixed an issue where the price of bundle products was not displayed correctly on the product page on the frontend
+ * Fixed a UI issue in the debug mode configuration
+ * Fixed minor issues with page layout
+ * Fixed an issue where the mini shopping cart loaded data from cache
+ * Fixed an issue where there was an incorrect value in the Grand Total (Base) column in the Orders grid if Catalog Price Scope was set to Website
+ * Fixed an issue where the Entity Generator tool did not accept the "class" parameter
+ * Fixed an issue where the default email template was not applied when the custom template in use was deleted
+ * Fixed an issue where shipping price for flat rate was set to 0 in the side block during checkout of a product with a configured recurring profile
+ * Fixed an issue where it was possible to create more Shipping Labels than there were products in the shipment
+ * Fixed an issue where data about "SHA-IN Pass Phrase" was missing after changing "Payment Action" in the Ogone payment method configuration
+ * Fixed performance issues with reindexing of the Price indexer
+ * Fixed an issue where importing tax rates with postal code = * led to incorrect data entered into database
+ * Fixed an issue where incorrect link to reset password was sent if secure URL was used on the frontend
+ * Fixed an issue where the Links section was absent while editing downloadable products from the Wishlist
+ * Fixed an issue where specified details for composite products were lost after adding to Gift Card and Downloadable products to the Wishlist
+ * Fixed and issue where the Date widget was set to incorrect date when creating a new customer
+ * Fixed an issue where a customer was redirected to Dashboard if the Redirect user to dashboard after login option was set to вЂNo’
+ * Fixed an issue where a customer was not able to register during checkout if Guest Checkout was not allowed
+ * Fixed an issue where System logs were not generated properly in integration tests
+ * Fixed benchmarking script
+ * Fixed an issue where it was impossible to put store to the maintenance mode during backup
+ * Fixed insecure use of mt_rand()
+ * Fixed an issue where Quoted price was displayed incorrectly from the shopping cart in the backend
+* Functional tests:
+ * Tax Rule Creation
+ * Admin User Roe Creation
+ * Simple Product Creation
+ * Customer Group Creation
+ * Update Backend Customer
+ * Newsletter Creation
+* Updated composer.json.dist to download and install MTF from Public GitHub repository
+* GitHub requests:
+ * [#542] (https://github.com/magento/magento2/pull/542) Fix ImportExport bug which occurs while importing multiple rows per entity
+ * [#507] (https://github.com/magento/magento2/issues/507) "Insert Image" window is overlapped on menu
+
2.0.0.0-dev78
=============
* Fixed bugs:
@@ -158,7 +222,7 @@
* `lib/Magento/Framework/Data/Form/Element/Submit.php`
* `lib/Magento/Framework/Data/Form/Element/Text.php`
* `lib/Magento/Framework/Data/Form/Element/Textarea.php`
-
+
2.0.0.0-dev75
=============
* Modularity improvements:
@@ -2678,4 +2742,3 @@ Deprecated code & minor fixes update:
2.0.0.0-dev01
=============
* Added initial version of Magento 2.x CE to public repository
-
diff --git a/app/bootstrap.php b/app/bootstrap.php
index ecc668e5d2d19..202866a451bed 100644
--- a/app/bootstrap.php
+++ b/app/bootstrap.php
@@ -72,9 +72,10 @@
}
if (!defined('BARE_BOOTSTRAP')) {
- if (file_exists(BP . '/maintenance.flag')) {
-
- if (!in_array($_SERVER['REMOTE_ADDR'], explode(",", file_get_contents(BP . '/maintenance.flag')))) {
+ $maintenanceFlag = BP . '/' . \Magento\Framework\App\State\MaintenanceMode::FLAG_DIR . '/'
+ . \Magento\Framework\App\State\MaintenanceMode::FLAG_FILENAME;
+ if (file_exists($maintenanceFlag)) {
+ if (!in_array($_SERVER['REMOTE_ADDR'], explode(",", file_get_contents($maintenanceFlag)))) {
if (PHP_SAPI == 'cli') {
echo 'Service temporarily unavailable due to maintenance downtime.';
} else {
diff --git a/app/code/Magento/Backend/Block/System/Config/Form/Field/Image.php b/app/code/Magento/Backend/Block/System/Config/Form/Field/Image.php
index 2d49f5d0a043f..bfac382917cba 100644
--- a/app/code/Magento/Backend/Block/System/Config/Form/Field/Image.php
+++ b/app/code/Magento/Backend/Block/System/Config/Form/Field/Image.php
@@ -27,6 +27,11 @@
*/
namespace Magento\Backend\Block\System\Config\Form\Field;
+/**
+ * Class Image Field
+ * @method getFieldConfig()
+ * @method setFieldConfig()
+ */
class Image extends \Magento\Framework\Data\Form\Element\Image
{
/**
@@ -39,10 +44,10 @@ protected function _getUrl()
$url = parent::_getUrl();
$config = $this->getFieldConfig();
/* @var $config array */
- if (array_key_exists('base_url', $config)) {
+ if (isset($config['base_url'])) {
$element = $config['base_url'];
$urlType = empty($element['type']) ? 'link' : (string)$element['type'];
- $url = $this->_urlBuilder->getBaseUrl($urlType) . $element['value'] . '/' . $url;
+ $url = $this->_urlBuilder->getBaseUrl(['_type' => $urlType]) . $element['value'] . '/' . $url;
}
return $url;
}
diff --git a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Currency.php b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Currency.php
index 94981c4764024..d2bf0763a0833 100644
--- a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Currency.php
+++ b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Currency.php
@@ -57,7 +57,7 @@ class Currency extends \Magento\Backend\Block\Widget\Grid\Column\Renderer\Abstra
/**
* @var \Magento\Directory\Model\Currency
*/
- protected $_baseCurrency;
+ protected $_defaultBaseCurrency;
/**
* @var \Magento\Framework\Locale\CurrencyInterface
@@ -84,11 +84,11 @@ public function __construct(
$this->_storeManager = $storeManager;
$this->_currencyLocator = $currencyLocator;
$this->_localeCurrency = $localeCurrency;
- $baseCurrencyCode = $this->_scopeConfig->getValue(
+ $defaultBaseCurrencyCode = $this->_scopeConfig->getValue(
\Magento\Directory\Model\Currency::XML_PATH_CURRENCY_BASE,
'default'
);
- $this->_baseCurrency = $currencyFactory->create()->load($baseCurrencyCode);
+ $this->_defaultBaseCurrency = $currencyFactory->create()->load($defaultBaseCurrencyCode);
}
/**
@@ -142,7 +142,7 @@ protected function _getRate($row)
if ($rate = $row->getData($this->getColumn()->getRateField())) {
return floatval($rate);
}
- return $this->_baseCurrency->getRate($this->_getCurrencyCode($row));
+ return $this->_defaultBaseCurrency->getRate($this->_getCurrencyCode($row));
}
/**
diff --git a/app/code/Magento/Backend/etc/adminhtml/di.xml b/app/code/Magento/Backend/etc/adminhtml/di.xml
index 056b8a58b9c1a..55d4bff058eb3 100644
--- a/app/code/Magento/Backend/etc/adminhtml/di.xml
+++ b/app/code/Magento/Backend/etc/adminhtml/di.xml
@@ -51,40 +51,8 @@
adminhtml
-
-
- backend_system_configuration_structure
-
-
-
-
-
- Magento\Backend\Model\Config\Structure\Element\Group\Proxy
-
-
-
-
- Magento\Backend\Model\Config\Structure\Search\Proxy
-
-
-
-
- Magento\Backend\Model\Config\Structure\Element\Iterator\Section
-
-
-
-
- Magento\Backend\Model\Config\Structure\Element\Iterator\Group
-
-
-
-
- Magento\Backend\Model\Config\Structure\Element\Iterator\Field
-
-
-
adminhtml
diff --git a/app/code/Magento/Backend/etc/di.xml b/app/code/Magento/Backend/etc/di.xml
index 85f461366af5f..cd6813917a314 100644
--- a/app/code/Magento/Backend/etc/di.xml
+++ b/app/code/Magento/Backend/etc/di.xml
@@ -183,4 +183,38 @@
Magento\Backend\Model\Session\Quote\Storage
+
+
+
+
+
+
+ Magento\Backend\Model\Config\Structure\Search\Proxy
+
+
+
+
+ backend_system_configuration_structure
+
+
+
+
+ Magento\Backend\Model\Config\Structure\Element\Iterator\Section
+
+
+
+
+ Magento\Backend\Model\Config\Structure\Element\Iterator\Group
+
+
+
+
+ Magento\Backend\Model\Config\Structure\Element\Group\Proxy
+
+
+
+
+ Magento\Backend\Model\Config\Structure\Element\Iterator\Field
+
+
diff --git a/app/code/Magento/Backend/view/adminhtml/page/js/calendar.phtml b/app/code/Magento/Backend/view/adminhtml/page/js/calendar.phtml
index 91f134d95ff6e..0b0c8c0b660f2 100644
--- a/app/code/Magento/Backend/view/adminhtml/page/js/calendar.phtml
+++ b/app/code/Magento/Backend/view/adminhtml/page/js/calendar.phtml
@@ -64,7 +64,8 @@
showHour: false,
showMinute: false,
localTimezone: getTimezoneOffsetSeconds() ?>,
- serverTimezoneSeconds:getStoreTimestamp() ?>
+ serverTimezoneSeconds:getStoreTimestamp() ?>,
+ yearRange: 'getYearRange() ?>'
}
});
})(jQuery);
diff --git a/app/code/Magento/Backup/Controller/Adminhtml/Index.php b/app/code/Magento/Backup/Controller/Adminhtml/Index.php
index 0447af34ced79..0bee25a18940e 100644
--- a/app/code/Magento/Backup/Controller/Adminhtml/Index.php
+++ b/app/code/Magento/Backup/Controller/Adminhtml/Index.php
@@ -52,24 +52,32 @@ class Index extends \Magento\Backend\App\Action
*/
protected $_backupModelFactory;
+ /**
+ * @var \Magento\Framework\App\State\MaintenanceMode
+ */
+ protected $maintenanceMode;
+
/**
* @param \Magento\Backend\App\Action\Context $context
* @param \Magento\Framework\Registry $coreRegistry
* @param \Magento\Framework\Backup\Factory $backupFactory
* @param \Magento\Framework\App\Response\Http\FileFactory $fileFactory
* @param \Magento\Backup\Model\BackupFactory $backupModelFactory
+ * @param \Magento\Framework\App\State\MaintenanceMode $maintenanceMode
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\Registry $coreRegistry,
\Magento\Framework\Backup\Factory $backupFactory,
\Magento\Framework\App\Response\Http\FileFactory $fileFactory,
- \Magento\Backup\Model\BackupFactory $backupModelFactory
+ \Magento\Backup\Model\BackupFactory $backupModelFactory,
+ \Magento\Framework\App\State\MaintenanceMode $maintenanceMode
) {
$this->_coreRegistry = $coreRegistry;
$this->_backupFactory = $backupFactory;
$this->_fileFactory = $fileFactory;
$this->_backupModelFactory = $backupModelFactory;
+ $this->maintenanceMode = $maintenanceMode;
parent::__construct($context);
}
@@ -150,9 +158,7 @@ public function createAction()
$this->_coreRegistry->register('backup_manager', $backupManager);
if ($this->getRequest()->getParam('maintenance_mode')) {
- $turnedOn = $helper->turnOnMaintenanceMode();
-
- if (!$turnedOn) {
+ if (!$this->maintenanceMode->turnOn()) {
$response->setError(
__(
'You need more permissions to activate maintenance mode right now.'
@@ -202,7 +208,7 @@ public function createAction()
}
if ($this->getRequest()->getParam('maintenance_mode')) {
- $helper->turnOffMaintenanceMode();
+ $this->maintenanceMode->turnOff();
}
$this->getResponse()->setBody($response->toJson());
@@ -306,9 +312,7 @@ public function rollbackAction()
}
if ($this->getRequest()->getParam('maintenance_mode')) {
- $turnedOn = $helper->turnOnMaintenanceMode();
-
- if (!$turnedOn) {
+ if (!$this->maintenanceMode->turnOn()) {
$response->setError(
__(
'You need more permissions to activate maintenance mode right now.'
@@ -373,7 +377,7 @@ public function rollbackAction()
}
if ($this->getRequest()->getParam('maintenance_mode')) {
- $helper->turnOffMaintenanceMode();
+ $this->maintenanceMode->turnOff();
}
$this->getResponse()->setBody($response->toJson());
diff --git a/app/code/Magento/Backup/Helper/Data.php b/app/code/Magento/Backup/Helper/Data.php
index f0811f751ee7c..faa0625d4794a 100644
--- a/app/code/Magento/Backup/Helper/Data.php
+++ b/app/code/Magento/Backup/Helper/Data.php
@@ -23,6 +23,8 @@
*/
namespace Magento\Backup\Helper;
+use Magento\Framework\App\State\MaintenanceMode;
+
/**
* Backup data helper
*/
@@ -191,7 +193,7 @@ public function getBackupIgnorePaths()
return array(
'.git',
'.svn',
- 'maintenance.flag',
+ $this->_filesystem->getPath(MaintenanceMode::FLAG_DIR) . '/' . MaintenanceMode::FLAG_FILENAME,
$this->_filesystem->getPath(\Magento\Framework\App\Filesystem::SESSION_DIR),
$this->_filesystem->getPath(\Magento\Framework\App\Filesystem::CACHE_DIR),
$this->_filesystem->getPath(\Magento\Framework\App\Filesystem::LOG_DIR),
@@ -211,7 +213,7 @@ public function getRollbackIgnorePaths()
return array(
'.svn',
'.git',
- 'maintenance.flag',
+ $this->_filesystem->getPath(MaintenanceMode::FLAG_DIR) . '/' . MaintenanceMode::FLAG_FILENAME,
$this->_filesystem->getPath(\Magento\Framework\App\Filesystem::SESSION_DIR),
$this->_filesystem->getPath(\Magento\Framework\App\Filesystem::LOG_DIR),
$this->_filesystem->getPath(\Magento\Framework\App\Filesystem::VAR_DIR) . '/locks',
@@ -221,35 +223,6 @@ public function getRollbackIgnorePaths()
);
}
- /**
- * Put store into maintenance mode
- *
- * @return bool
- */
- public function turnOnMaintenanceMode()
- {
- $maintenanceFlagFile = $this->getMaintenanceFlagFilePath();
- $result = $this->_filesystem->getDirectoryWrite(
- \Magento\Framework\App\Filesystem::ROOT_DIR
- )->writeFile(
- $maintenanceFlagFile,
- 'maintenance'
- );
-
- return $result !== false;
- }
-
- /**
- * Turn off store maintenance mode
- *
- * @return void
- */
- public function turnOffMaintenanceMode()
- {
- $maintenanceFlagFile = $this->getMaintenanceFlagFilePath();
- $this->_filesystem->getDirectoryWrite(\Magento\Framework\App\Filesystem::ROOT_DIR)->delete($maintenanceFlagFile);
- }
-
/**
* Get backup create success message by backup type
*
@@ -274,16 +247,6 @@ public function getCreateSuccessMessageByType($type)
return $messagesMap[$type];
}
- /**
- * Get path to maintenance flag file
- *
- * @return string
- */
- protected function getMaintenanceFlagFilePath()
- {
- return 'maintenance.flag';
- }
-
/**
* Invalidate Cache
*
diff --git a/app/code/Magento/Backup/Model/Observer.php b/app/code/Magento/Backup/Model/Observer.php
index 71af7b60dd733..afc884990cf82 100644
--- a/app/code/Magento/Backup/Model/Observer.php
+++ b/app/code/Magento/Backup/Model/Observer.php
@@ -84,6 +84,11 @@ class Observer
*/
protected $_backupFactory;
+ /**
+ * @var \Magento\Framework\App\State\MaintenanceMode
+ */
+ protected $maintenanceMode;
+
/**
* @param \Magento\Backup\Helper\Data $backupData
* @param \Magento\Framework\Registry $coreRegistry
@@ -91,6 +96,7 @@ class Observer
* @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
* @param \Magento\Framework\App\Filesystem $filesystem
* @param \Magento\Framework\Backup\Factory $backupFactory
+ * @param \Magento\Framework\App\State\MaintenanceMode $maintenanceMode
*/
public function __construct(
\Magento\Backup\Helper\Data $backupData,
@@ -98,7 +104,8 @@ public function __construct(
\Magento\Framework\Logger $logger,
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
\Magento\Framework\App\Filesystem $filesystem,
- \Magento\Framework\Backup\Factory $backupFactory
+ \Magento\Framework\Backup\Factory $backupFactory,
+ \Magento\Framework\App\State\MaintenanceMode $maintenanceMode
) {
$this->_backupData = $backupData;
$this->_coreRegistry = $coreRegistry;
@@ -106,6 +113,7 @@ public function __construct(
$this->_scopeConfig = $scopeConfig;
$this->_filesystem = $filesystem;
$this->_backupFactory = $backupFactory;
+ $this->maintenanceMode = $maintenanceMode;
}
/**
@@ -120,7 +128,7 @@ public function scheduledBackup()
}
if ($this->_scopeConfig->isSetFlag(self::XML_PATH_BACKUP_MAINTENANCE_MODE, ScopeInterface::SCOPE_STORE)) {
- $this->_backupData->turnOnMaintenanceMode();
+ $this->maintenanceMode->turnOn();
}
$type = $this->_scopeConfig->getValue(self::XML_PATH_BACKUP_TYPE, ScopeInterface::SCOPE_STORE);
@@ -158,7 +166,7 @@ public function scheduledBackup()
}
if ($this->_scopeConfig->isSetFlag(self::XML_PATH_BACKUP_MAINTENANCE_MODE, ScopeInterface::SCOPE_STORE)) {
- $this->_backupData->turnOffMaintenanceMode();
+ $this->maintenanceMode->turnOff();
}
return $this;
diff --git a/app/code/Magento/Backup/view/adminhtml/backup/dialogs.phtml b/app/code/Magento/Backup/view/adminhtml/backup/dialogs.phtml
index 65c6f0d90a243..a3d7602ce28f9 100644
--- a/app/code/Magento/Backup/view/adminhtml/backup/dialogs.phtml
+++ b/app/code/Magento/Backup/view/adminhtml/backup/dialogs.phtml
@@ -65,7 +65,7 @@
-
diff --git a/app/code/Magento/Sales/Model/Order.php b/app/code/Magento/Sales/Model/Order.php
index 176dc4ca2d1fa..c9b48275f7902 100644
--- a/app/code/Magento/Sales/Model/Order.php
+++ b/app/code/Magento/Sales/Model/Order.php
@@ -2464,7 +2464,7 @@ protected function _beforeSave()
$this->unsShippingAddressId();
}
- $this->setData('protect_code', substr(md5(uniqid(mt_rand(), true) . ':' . microtime(true)), 5, 6));
+ $this->setData('protect_code', substr(md5(uniqid(\Magento\Framework\Math\Random::getRandomNumber(), true) . ':' . microtime(true)), 5, 6));
return $this;
}
diff --git a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_grid_block.xml b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_grid_block.xml
index 58ac6acc42c30..3e7e62cbc8f1d 100644
--- a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_grid_block.xml
+++ b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_grid_block.xml
@@ -166,6 +166,7 @@
Grand Total (Base)
currency
base_currency_code
+ 1
base_grand_total
col-gtbase
col-gtbase
diff --git a/app/code/Magento/Sales/view/email/shipment_new.html b/app/code/Magento/Sales/view/email/shipment_new.html
index 68c65c5ebba01..4c0f8f332d606 100644
--- a/app/code/Magento/Sales/view/email/shipment_new.html
+++ b/app/code/Magento/Sales/view/email/shipment_new.html
@@ -96,7 +96,7 @@ Your Shipment #{{var s
{{layout handle="sales_email_order_shipment_items" shipment=$shipment order=$order}}
- {{block type='Magento\\Framework\\View\\Element\\Template' area='frontend' template='Magento_Sales::email/shipment/track.phtml' shipment=$shipment order=$order}}
+ {{block class='Magento\\Framework\\View\\Element\\Template' area='frontend' template='Magento_Sales::email/shipment/track.phtml' shipment=$shipment order=$order}}
{{var comment}}
diff --git a/app/code/Magento/Sales/view/email/shipment_new_guest.html b/app/code/Magento/Sales/view/email/shipment_new_guest.html
index e4bf8b235a3b7..334bfe7504fd7 100644
--- a/app/code/Magento/Sales/view/email/shipment_new_guest.html
+++ b/app/code/Magento/Sales/view/email/shipment_new_guest.html
@@ -94,7 +94,7 @@ Your Shipment #{{var s
{{layout handle="sales_email_order_shipment_items" shipment=$shipment order=$order}}
- {{block type='Magento\\Framework\\View\\Element\\Template' area='frontend' template='Magento_Sales::email/shipment/track.phtml' shipment=$shipment order=$order}}
+ {{block class='Magento\\Framework\\View\\Element\\Template' area='frontend' template='Magento_Sales::email/shipment/track.phtml' shipment=$shipment order=$order}}
{{var comment}}
diff --git a/app/code/Magento/SalesRule/Model/Coupon/Massgenerator.php b/app/code/Magento/SalesRule/Model/Coupon/Massgenerator.php
index 292c6ac602a1e..d47bb71a5afaa 100644
--- a/app/code/Magento/SalesRule/Model/Coupon/Massgenerator.php
+++ b/app/code/Magento/SalesRule/Model/Coupon/Massgenerator.php
@@ -129,7 +129,7 @@ public function generateCode()
$code = '';
$charsetSize = count($charset);
for ($i = 0; $i < $length; $i++) {
- $char = $charset[mt_rand(0, $charsetSize - 1)];
+ $char = $charset[\Magento\Framework\Math\Random::getRandomNumber(0, $charsetSize - 1)];
if ($split > 0 && $i % $split == 0 && $i != 0) {
$char = $splitChar . $char;
}
diff --git a/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment.php b/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment.php
index 703c92391526d..89797072eaf91 100644
--- a/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment.php
+++ b/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment.php
@@ -740,7 +740,7 @@ protected function _createPdfPageFromImageString($imageString)
$page = new \Zend_Pdf_Page($xSize, $ySize);
imageinterlace($image, 0);
- $tmpFileName = $directory->getAbsolutePath('shipping_labels_' . uniqid(mt_rand()) . time() . '.png');
+ $tmpFileName = $directory->getAbsolutePath('shipping_labels_' . uniqid(\Magento\Framework\Math\Random::getRandomNumber()) . time() . '.png');
imagepng($image, $tmpFileName);
$pdfImage = \Zend_Pdf_Image::imageWithPath($tmpFileName);
$page->drawImage($pdfImage, 0, 0, $xSize, $ySize);
diff --git a/app/code/Magento/Shipping/view/adminhtml/create/items.phtml b/app/code/Magento/Shipping/view/adminhtml/create/items.phtml
index 4e11459c57ee9..055d69d0a798d 100644
--- a/app/code/Magento/Shipping/view/adminhtml/create/items.phtml
+++ b/app/code/Magento/Shipping/view/adminhtml/create/items.phtml
@@ -96,18 +96,15 @@ if (sendEmailCheckbox) {
Event.observe(sendEmailCheckbox, 'change', bindSendEmail);
bindSendEmail();
}
-function bindSendEmail()
-{
+function bindSendEmail() {
if (sendEmailCheckbox.checked == true) {
notifyCustomerCheckbox.disabled = false;
- //shipmentCommentText.disabled = false;
}
else {
notifyCustomerCheckbox.disabled = true;
- //shipmentCommentText.disabled = true;
}
}
-function toggleCreateLabelCheckbox(){
+function toggleCreateLabelCheckbox() {
var checkbox = $('create_shipping_label');
var submitButton = checkbox.up('.order-totals').select('.submit-button span')[0];
if (checkbox.checked) {
@@ -116,7 +113,11 @@ function toggleCreateLabelCheckbox(){
submitButton.innerText = submitButton.innerText.replace(/\.\.\.$/, '');
}
}
-function submitShipment(btn){
+function submitShipment(btn) {
+ if (!validQtyItems()) {
+ alert('');
+ return;
+ }
var checkbox = $(btn).up('.order-totals').select('#create_shipping_label')[0];
if (checkbox && checkbox.checked) {
packaging.showWindow();
@@ -130,5 +131,15 @@ function submitShipment(btn){
jQuery('#edit_form').triggerHandler('save');
}
}
+function validQtyItems() {
+ var valid = true;
+ $$('.qty-item').each(function(item) {
+ var val = parseFloat(item.value);
+ if (isNaN(val) || val < 0) {
+ valid = false;
+ }
+ });
+ return valid;
+}
//]]>
diff --git a/app/code/Magento/Shipping/view/adminhtml/create/items/renderer/default.phtml b/app/code/Magento/Shipping/view/adminhtml/create/items/renderer/default.phtml
index a46e9d55141e1..64d3910cbc163 100644
--- a/app/code/Magento/Shipping/view/adminhtml/create/items/renderer/default.phtml
+++ b/app/code/Magento/Shipping/view/adminhtml/create/items/renderer/default.phtml
@@ -28,7 +28,7 @@
getColumnHtml($_item, 'qty') ?>
canShipPartiallyItem()): ?>
-
+
getQty()*1 ?>
diff --git a/app/code/Magento/Tax/Helper/Data.php b/app/code/Magento/Tax/Helper/Data.php
index 63bd13db488cf..c3c4371f52800 100644
--- a/app/code/Magento/Tax/Helper/Data.php
+++ b/app/code/Magento/Tax/Helper/Data.php
@@ -32,12 +32,24 @@
*/
class Data extends \Magento\Framework\App\Helper\AbstractHelper
{
+ /**
+ * Price conversion constant for positive
+ */
const PRICE_CONVERSION_PLUS = 1;
+ /**
+ * Price conversion constant for negative
+ */
const PRICE_CONVERSION_MINUS = 2;
+ /**
+ * Default tax class for customers
+ */
const CONFIG_DEFAULT_CUSTOMER_TAX_CLASS = 'tax/classes/default_customer_tax_class';
+ /**
+ * Default tax class for products
+ */
const CONFIG_DEFAULT_PRODUCT_TAX_CLASS = 'tax/classes/default_product_tax_class';
/**
@@ -48,45 +60,12 @@ class Data extends \Magento\Framework\App\Helper\AbstractHelper
protected $_config;
/**
+ * Tax calculator
+ *
* @var \Magento\Tax\Model\Calculation
*/
protected $_calculation;
- /**
- * @var mixed
- */
- protected $_displayTaxColumn;
-
- /**
- * @var mixed
- */
- protected $_taxData;
-
- /**
- * @var mixed
- */
- protected $_priceIncludesTax;
-
- /**
- * @var mixed
- */
- protected $_shippingPriceIncludesTax;
-
- /**
- * @var mixed
- */
- protected $_applyTaxAfterDiscount;
-
- /**
- * @var int
- */
- protected $_priceDisplayType;
-
- /**
- * @var mixed
- */
- protected $_shippingPriceDisplayType;
-
/**
* Postcode cut to this length when creating search templates
*
@@ -135,6 +114,11 @@ class Data extends \Magento\Framework\App\Helper\AbstractHelper
*/
protected $_taxItemFactory;
+ /**
+ * @var \Magento\Tax\Model\Resource\Sales\Order\Tax\CollectionFactory
+ */
+ protected $_orderTaxCollectionFactory;
+
/**
* @var \Magento\Framework\Locale\ResolverInterface
*/
@@ -151,6 +135,7 @@ class Data extends \Magento\Framework\App\Helper\AbstractHelper
* @param \Magento\Framework\Locale\FormatInterface $localeFormat
* @param \Magento\Eav\Model\Entity\AttributeFactory $attributeFactory
* @param \Magento\Tax\Model\Resource\Sales\Order\Tax\ItemFactory $taxItemFactory
+ * @param \Magento\Tax\Model\Resource\Sales\Order\Tax\CollectionFactory $orderTaxCollectionFactory
* @param \Magento\Framework\Locale\ResolverInterface $localeResolver
*/
public function __construct(
@@ -164,6 +149,7 @@ public function __construct(
\Magento\Framework\Locale\FormatInterface $localeFormat,
\Magento\Eav\Model\Entity\AttributeFactory $attributeFactory,
\Magento\Tax\Model\Resource\Sales\Order\Tax\ItemFactory $taxItemFactory,
+ \Magento\Tax\Model\Resource\Sales\Order\Tax\CollectionFactory $orderTaxCollectionFactory,
\Magento\Framework\Locale\ResolverInterface $localeResolver
) {
parent::__construct($context);
@@ -176,6 +162,7 @@ public function __construct(
$this->_localeFormat = $localeFormat;
$this->_attributeFactory = $attributeFactory;
$this->_taxItemFactory = $taxItemFactory;
+ $this->_orderTaxCollectionFactory = $orderTaxCollectionFactory;
$this->_localeResolver = $localeResolver;
}
@@ -254,13 +241,12 @@ public function applyTaxAfterDiscount($store = null)
}
/**
- * Output
+ * Retrieves the "including tax" or "excluding tax" label
*
* @param bool $flag
- * @param null|int|string|Store $store
* @return string
*/
- public function getIncExcText($flag, $store = null)
+ public function getIncExcText($flag)
{
return $flag ? __('Incl. Tax') : __('Excl. Tax');
}
@@ -314,7 +300,7 @@ public function needPriceConversion($store = null)
}
if ($res === false) {
- $res = $this->displayTaxColumn($store);
+ $res = $this->displayTaxColumn();
}
return $res;
}
@@ -443,10 +429,9 @@ public function displaySalesSubtotalExclTax($store = null)
/**
* Check if need display tax column in for shopping cart/order items
*
- * @param null|string|bool|int|Store $store
* @return bool
*/
- public function displayTaxColumn($store = null)
+ public function displayTaxColumn()
{
return $this->_config->displayCartPricesBoth();
}
@@ -488,8 +473,8 @@ public function getAllRatesByProductClass($store = null)
protected function _getAllRatesByProductClass($store = null)
{
$result = array();
- $originRate = $this->_calculation->getRateOriginRequest($store);
- $rates = $this->_calculation->getRatesForAllProductTaxClasses($originRate);
+ $defaultRate = $this->_calculation->getDefaultRateRequest($store);
+ $rates = $this->_calculation->getRatesForAllProductTaxClasses($defaultRate);
foreach ($rates as $class => $rate) {
$result["value_{$class}"] = $rate;
}
@@ -507,6 +492,7 @@ protected function _getAllRatesByProductClass($store = null)
* @param null|int $ctc customer tax class
* @param null|string|bool|int|Store $store
* @param bool $priceIncludesTax flag what price parameter contain tax
+ * @param bool $roundPrice
* @return float
*/
public function getPrice(
@@ -517,7 +503,8 @@ public function getPrice(
$billingAddress = null,
$ctc = null,
$store = null,
- $priceIncludesTax = null
+ $priceIncludesTax = null,
+ $roundPrice = true
) {
if (!$price) {
return $price;
@@ -541,8 +528,12 @@ public function getPrice(
}
}
if ($taxClassId && $priceIncludesTax) {
- $request = $this->_calculation->getRateRequest(false, false, false, $store);
- $includingPercent = $this->_calculation->getRate($request->setProductClassId($taxClassId));
+ if ($this->isCrossBorderTradeEnabled($store)) {
+ $includingPercent = $percent;
+ } else {
+ $request = $this->_calculation->getRateOriginRequest($store);
+ $includingPercent = $this->_calculation->getRate($request->setProductClassId($taxClassId));
+ }
}
if ($percent === false || is_null($percent)) {
@@ -552,39 +543,34 @@ public function getPrice(
}
$product->setTaxPercent($percent);
+ if ($product->getAppliedRates() == null) {
+ $request = $this->_calculation->getRateRequest($shippingAddress, $billingAddress, $ctc, $store);
+ $request->setProductClassId($taxClassId);
+ $appliedRates = $this->_calculation->getAppliedRates($request);
+ $product->setAppliedRates($appliedRates);
+ }
if (!is_null($includingTax)) {
if ($priceIncludesTax) {
if ($includingTax) {
/**
- * Recalculate price include tax in case of different rates
+ * Recalculate price include tax in case of different rates. Otherwise price remains the same.
*/
if ($includingPercent != $percent) {
- $price = $this->_calculatePrice($price, $includingPercent, false);
- /**
- * Using regular rounding. Ex:
- * price incl tax = 52.76
- * store tax rate = 19.6%
- * customer tax rate= 19%
- *
- * price excl tax = 52.76 / 1.196 = 44.11371237 ~ 44.11
- * tax = 44.11371237 * 0.19 = 8.381605351 ~ 8.38
- * price incl tax = 52.49531773 ~ 52.50 != 52.49
- *
- * that why we need round prices excluding tax before applying tax
- * this calculation is used for showing prices on catalog pages
- */
- if ($percent != 0) {
- $price = $this->getCalculator()->round($price);
- $price = $this->_calculatePrice($price, $percent, true);
- }
+ // determine the customer's price that includes tax
+ $price = $this->_calculatePriceInclTax($price, $includingPercent, $percent, $store);
}
} else {
$price = $this->_calculatePrice($price, $includingPercent, false);
}
} else {
if ($includingTax) {
- $price = $this->_calculatePrice($price, $percent, true);
+ $appliedRates = $product->getAppliedRates();
+ if (count($appliedRates) > 1) {
+ $price = $this->_calculatePriceInclTaxWithMultipleRates($price, $appliedRates);
+ } else {
+ $price = $this->_calculatePrice($price, $percent, true);
+ }
}
}
} else {
@@ -592,7 +578,18 @@ public function getPrice(
switch ($this->getPriceDisplayType($store)) {
case Config::DISPLAY_TYPE_EXCLUDING_TAX:
case Config::DISPLAY_TYPE_BOTH:
- $price = $this->_calculatePrice($price, $includingPercent, false);
+ if ($includingPercent != $percent) {
+ // determine the customer's price that includes tax
+ $taxablePrice = $this->_calculatePriceInclTax($price, $includingPercent, $percent, $store);
+ // determine the customer's tax amount,
+ // round tax unless $roundPrice is set explicitly to false
+ $tax = $this->_calculation->calcTaxAmount($taxablePrice, $percent, true, $roundPrice);
+ // determine the customer's price without taxes
+ $price = $taxablePrice - $tax;
+ } else {
+ //round tax first unless $roundPrice is set to false explicitly
+ $price = $this->_calculatePrice($price, $includingPercent, false, $roundPrice);
+ }
break;
case Config::DISPLAY_TYPE_INCLUDING_TAX:
$price = $this->_calculatePrice($price, $includingPercent, false);
@@ -604,7 +601,12 @@ public function getPrice(
} else {
switch ($this->getPriceDisplayType($store)) {
case Config::DISPLAY_TYPE_INCLUDING_TAX:
- $price = $this->_calculatePrice($price, $percent, true);
+ $appliedRates = $product->getAppliedRates();
+ if (count($appliedRates) > 1) {
+ $price = $this->_calculatePriceInclTaxWithMultipleRates($price, $appliedRates);
+ } else {
+ $price = $this->_calculatePrice($price, $percent, true);
+ }
break;
case Config::DISPLAY_TYPE_BOTH:
case Config::DISPLAY_TYPE_EXCLUDING_TAX:
@@ -614,7 +616,29 @@ public function getPrice(
}
}
}
- return $store->roundPrice($price);
+ if ($roundPrice) {
+ return $store->roundPrice($price);
+ } else {
+ return $price;
+ }
+ }
+
+ /**
+ * Given a store price that includes tax at the store rate, this function will back out the store's tax, and add in
+ * the customer's tax. Returns this new price which is the customer's price including tax.
+ *
+ * @param float $storePriceInclTax
+ * @param float $storePercent
+ * @param float $customerPercent
+ * @param null|int|string|Store $store
+ * @return float
+ */
+ protected function _calculatePriceInclTax($storePriceInclTax, $storePercent, $customerPercent, $store)
+ {
+ $priceExclTax = $this->_calculatePrice($storePriceInclTax, $storePercent, false, false);
+ $customerTax = $this->_calculation->calcTaxAmount($priceExclTax, $customerPercent, false, false);
+ $customerPriceInclTax = $store->roundPrice($priceExclTax + $customerTax);
+ return $customerPriceInclTax;
}
/**
@@ -640,34 +664,55 @@ public function displayPriceExcludingTax()
/**
* Check if we have display in catalog prices including and excluding tax
*
+ * @param null|int|string|Store $store
* @return bool
*/
- public function displayBothPrices()
+ public function displayBothPrices($store = null)
{
- return $this->getPriceDisplayType() == Config::DISPLAY_TYPE_BOTH;
+ return $this->getPriceDisplayType($store) == Config::DISPLAY_TYPE_BOTH;
}
/**
- * Calculate price including/excluding tax base on tax rate percent
+ * Calculate price including/excluding tax based on tax rate percent
*
* @param float $price
* @param float $percent
- * @param bool $type true - for calculate price including tax and false if price excluding tax
+ * @param bool $type - true to calculate the price including tax or false if calculating price to exclude tax
+ * @param bool $roundTaxFirst
* @return float
*/
- protected function _calculatePrice($price, $percent, $type)
+ protected function _calculatePrice($price, $percent, $type, $roundTaxFirst = false)
{
if ($type) {
- $taxAmount = $this->_calculation->calcTaxAmount($price, $percent, false, false);
+ $taxAmount = $this->_calculation->calcTaxAmount($price, $percent, false, $roundTaxFirst);
return $price + $taxAmount;
} else {
- $taxAmount = $this->_calculation->calcTaxAmount($price, $percent, true, false);
+ $taxAmount = $this->_calculation->calcTaxAmount($price, $percent, true, $roundTaxFirst);
return $price - $taxAmount;
}
}
/**
- * @param bool $flag
+ * Calculate price including tax when multiple taxes is applied and rounded independently.
+ *
+ * @param float $price
+ * @param array $appliedRates
+ * @return float
+ */
+ protected function _calculatePriceInclTaxWithMultipleRates($price, $appliedRates)
+ {
+ $tax = 0;
+ foreach ($appliedRates as $appliedRate) {
+ $taxRate = $appliedRate['percent'];
+ $tax += $this->_calculation->round($price * $taxRate / 100);
+ }
+ return $tax + $price;
+ }
+
+ /**
+ * Returns the include / exclude tax label
+ *
+ * @param bool $flag
* @return string
*/
public function getIncExcTaxLabel($flag)
@@ -677,6 +722,8 @@ public function getIncExcTaxLabel($flag)
}
/**
+ * Check if shipping prices include tax
+ *
* @param null|string|bool|int|Store $store
* @return bool
*/
@@ -686,6 +733,8 @@ public function shippingPriceIncludesTax($store = null)
}
/**
+ * Get shipping price display type
+ *
* @param null|string|bool|int|Store $store
* @return int
*/
@@ -695,6 +744,8 @@ public function getShippingPriceDisplayType($store = null)
}
/**
+ * Returns whether the shipping price should display with taxes included
+ *
* @return bool
*/
public function displayShippingPriceIncludingTax()
@@ -703,6 +754,8 @@ public function displayShippingPriceIncludingTax()
}
/**
+ * Returns whether the shipping price should display without taxes
+ *
* @return bool
*/
public function displayShippingPriceExcludingTax()
@@ -711,6 +764,8 @@ public function displayShippingPriceExcludingTax()
}
/**
+ * Returns whether the shipping price should display both with and without taxes
+ *
* @return bool
*/
public function displayShippingBothPrices()
@@ -719,6 +774,8 @@ public function displayShippingBothPrices()
}
/**
+ * Get tax class id specified for shipping tax estimation
+ *
* @param null|string|bool|int|Store $store
* @return int
*/
@@ -761,6 +818,8 @@ public function getShippingPrice($price, $includingTax = null, $shippingAddress
}
/**
+ * Returns the SQL for the price tax
+ *
* @param string $priceField
* @param string $taxClassField
* @return string
@@ -771,7 +830,7 @@ public function getPriceTaxSql($priceField, $taxClassField)
return '';
}
- $request = $this->_calculation->getRateRequest(false, false, false);
+ $request = $this->_calculation->getDefaultRateRequest();
$defaultTaxes = $this->_calculation->getRatesForAllProductTaxClasses($request);
$request = $this->_calculation->getRateRequest();
@@ -779,7 +838,10 @@ public function getPriceTaxSql($priceField, $taxClassField)
$defaultTaxString = $currentTaxString = '';
- $rateToVariable = array('defaultTaxString' => 'defaultTaxes', 'currentTaxString' => 'currentTaxes');
+ $rateToVariable = array(
+ 'defaultTaxString' => 'defaultTaxes',
+ 'currentTaxString' => 'currentTaxes',
+ );
foreach ($rateToVariable as $rateVariable => $rateArray) {
if (${$rateArray} && is_array(${$rateArray})) {
${$rateVariable} = '';
@@ -944,8 +1006,8 @@ public function getCalculationAgorithm($store = null)
* $index => array(
* 'tax_amount' => $taxAmount,
* 'base_tax_amount' => $baseTaxAmount,
- * 'hidden_tax_amount' => $hiddenTaxAmount
- * 'title' => $title
+ * 'hidden_tax_amount' => $hiddenTaxAmount,
+ * 'title' => $title,
* 'percent' => $percent
* )
* )
@@ -965,33 +1027,55 @@ public function getCalculatedTaxes($source)
$taxClassAmount = array();
if ($current && $source) {
- /** @var $item \Magento\Sales\Model\Order\Item */
- foreach ($current->getItemsCollection() as $item) {
- /** @var $taxCollection \Magento\Tax\Model\Resource\Sales\Order\Tax\Item */
- $taxCollection = $this->_taxItemFactory->create();
- $taxCollection->getTaxItemsByItemId(
- $item->getOrderItemId() ? $item->getOrderItemId() : $item->getItemId()
- );
-
- foreach ($taxCollection as $tax) {
- $taxClassId = $tax['tax_id'];
- $percent = $tax['tax_percent'];
-
- $price = $item->getRowTotal();
- $basePrice = $item->getBaseRowTotal();
- if ($this->applyTaxAfterDiscount($item->getStoreId())) {
- $price = $price - $item->getDiscountAmount() + $item->getHiddenTaxAmount();
- $basePrice = $basePrice - $item->getBaseDiscountAmount() + $item->getBaseHiddenTaxAmount();
- }
+ if ($current == $source) {
+ // use the actuals
+ $rates = $this->_getTaxRateSubtotals($source);
+ foreach ($rates['items'] as $rate) {
+ $taxClassId = $rate['tax_id'];
+ $taxClassAmount[$taxClassId]['tax_amount'] = $rate['amount'];
+ $taxClassAmount[$taxClassId]['base_tax_amount'] = $rate['base_amount'];
+ $taxClassAmount[$taxClassId]['title'] = $rate['title'];
+ $taxClassAmount[$taxClassId]['percent'] = $rate['percent'];
+ }
+ } else {
+ // regenerate tax subtotals
+ // Calculate taxes for shipping
+ $shippingTaxAmount = $current->getShippingTaxAmount();
+ if ($shippingTaxAmount) {
+ $shippingTax = $this->getShippingTax($current);
+ $taxClassAmount = array_merge($taxClassAmount, $shippingTax);
+ }
- if (isset($taxClassAmount[$taxClassId])) {
- $taxClassAmount[$taxClassId]['tax_amount'] += $price * $percent / 100;
- $taxClassAmount[$taxClassId]['base_tax_amount'] += $basePrice * $percent / 100;
- } else {
- $taxClassAmount[$taxClassId]['tax_amount'] = $price * $percent / 100;
- $taxClassAmount[$taxClassId]['base_tax_amount'] = $basePrice * $percent / 100;
- $taxClassAmount[$taxClassId]['title'] = $tax['title'];
- $taxClassAmount[$taxClassId]['percent'] = $tax['percent'];
+ /** @var $item \Magento\Sales\Model\Order\Item */
+ foreach ($current->getItemsCollection() as $item) {
+ /** @var $taxCollection \Magento\Tax\Model\Resource\Sales\Order\Tax\Item */
+ $taxCollection = $this->_taxItemFactory->create();
+ $taxCollection->getTaxItemsByItemId(
+ $item->getOrderItemId() ? $item->getOrderItemId() : $item->getItemId()
+ );
+
+ foreach ($taxCollection as $tax) {
+ $taxClassId = $tax['tax_id'];
+ $percent = $tax['tax_percent'];
+
+ $price = $item->getRowTotal();
+ $basePrice = $item->getBaseRowTotal();
+ if ($this->applyTaxAfterDiscount($item->getStoreId())) {
+ $price = $price - $item->getDiscountAmount() + $item->getHiddenTaxAmount();
+ $basePrice = $basePrice - $item->getBaseDiscountAmount() + $item->getBaseHiddenTaxAmount();
+ }
+ $taxAmount = $price * $percent / 100;
+ $baseTaxAmount = $basePrice * $percent / 100;
+
+ if (isset($taxClassAmount[$taxClassId])) {
+ $taxClassAmount[$taxClassId]['tax_amount'] += $taxAmount;
+ $taxClassAmount[$taxClassId]['base_tax_amount'] += $baseTaxAmount;
+ } else {
+ $taxClassAmount[$taxClassId]['tax_amount'] = $taxAmount;
+ $taxClassAmount[$taxClassId]['base_tax_amount'] = $baseTaxAmount;
+ $taxClassAmount[$taxClassId]['title'] = $tax['title'];
+ $taxClassAmount[$taxClassId]['percent'] = $tax['percent'];
+ }
}
}
}
@@ -1008,6 +1092,17 @@ public function getCalculatedTaxes($source)
return $taxClassAmount;
}
+ /**
+ * Returns the array of tax rates for the order
+ *
+ * @param \Magento\Sales\Model\Order $order
+ * @return array
+ */
+ protected function _getTaxRateSubtotals($order)
+ {
+ return $this->_orderTaxCollectionFactory->create()->loadByOrder($order)->toArray();
+ }
+
/**
* Get calculated Shipping & Handling Tax
*
@@ -1075,4 +1170,15 @@ public function getDefaultProductTaxClass()
\Magento\Store\Model\ScopeInterface::SCOPE_STORE
);
}
+
+ /**
+ * Return whether cross border trade is enabled or not
+ *
+ * @param null|int|string|Store $store
+ * @return bool
+ */
+ public function isCrossBorderTradeEnabled($store = null)
+ {
+ return (bool)$this->_config->crossBorderTradeEnabled($store);
+ }
}
diff --git a/app/code/Magento/Tax/Model/Calculation.php b/app/code/Magento/Tax/Model/Calculation.php
index f9896b5168edf..8570082ea315b 100644
--- a/app/code/Magento/Tax/Model/Calculation.php
+++ b/app/code/Magento/Tax/Model/Calculation.php
@@ -31,52 +31,86 @@
use Magento\Customer\Service\V1\CustomerGroupServiceInterface as GroupServiceInterface;
use Magento\Customer\Service\V1\CustomerAccountServiceInterface;
use Magento\Framework\Exception\NoSuchEntityException;
+use Magento\Tax\Model\Config;
/**
* Tax Calculation Model
*/
class Calculation extends \Magento\Framework\Model\AbstractModel
{
+ /**
+ * Identifier constant for Tax calculation before discount excluding TAX
+ */
const CALC_TAX_BEFORE_DISCOUNT_ON_EXCL = '0_0';
+ /**
+ * Identifier constant for Tax calculation before discount including TAX
+ */
const CALC_TAX_BEFORE_DISCOUNT_ON_INCL = '0_1';
+ /**
+ * Identifier constant for Tax calculation after discount excluding TAX
+ */
const CALC_TAX_AFTER_DISCOUNT_ON_EXCL = '1_0';
+ /**
+ * Identifier constant for Tax calculation after discount including TAX
+ */
const CALC_TAX_AFTER_DISCOUNT_ON_INCL = '1_1';
+ /**
+ * Identifier constant for unit based calculation
+ */
const CALC_UNIT_BASE = 'UNIT_BASE_CALCULATION';
+ /**
+ * Identifier constant for row based calculation
+ */
const CALC_ROW_BASE = 'ROW_BASE_CALCULATION';
+ /**
+ * Identifier constant for total based calculation
+ */
const CALC_TOTAL_BASE = 'TOTAL_BASE_CALCULATION';
/**
+ * Identifier constant for unit based calculation
+ *
* @var array
*/
protected $_rates = array();
/**
+ * Identifier constant for row based calculation
+ *
* @var array
*/
protected $_ctc = array();
/**
+ * Identifier constant for total based calculation
+ *
* @var array
*/
protected $_ptc = array();
/**
+ * Cache to hold the rates
+ *
* @var array
*/
protected $_rateCache = array();
/**
+ * Store the rate calculation process
+ *
* @var array
*/
protected $_rateCalculationProcess = array();
/**
+ * Hold the customer
+ *
* @var CustomerDataObject|bool
*/
protected $_customer;
@@ -113,6 +147,13 @@ class Calculation extends \Magento\Framework\Model\AbstractModel
*/
protected $_classesFactory;
+ /**
+ * Tax configuration object
+ *
+ * @var Config
+ */
+ protected $_config;
+
/**
* @var GroupServiceInterface
*/
@@ -132,6 +173,7 @@ class Calculation extends \Magento\Framework\Model\AbstractModel
* @param \Magento\Framework\Model\Context $context
* @param \Magento\Framework\Registry $registry
* @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
+ * @param Config $taxConfig
* @param \Magento\Store\Model\StoreManagerInterface $storeManager
* @param \Magento\Customer\Model\Session $customerSession
* @param \Magento\Customer\Model\CustomerFactory $customerFactory
@@ -149,6 +191,7 @@ public function __construct(
\Magento\Framework\Model\Context $context,
\Magento\Framework\Registry $registry,
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
+ Config $taxConfig,
\Magento\Store\Model\StoreManagerInterface $storeManager,
\Magento\Customer\Model\Session $customerSession,
\Magento\Customer\Model\CustomerFactory $customerFactory,
@@ -162,6 +205,7 @@ public function __construct(
array $data = array()
) {
$this->_scopeConfig = $scopeConfig;
+ $this->_config = $taxConfig;
$this->_storeManager = $storeManager;
$this->_customerSession = $customerSession;
$this->_customerFactory = $customerFactory;
@@ -402,6 +446,33 @@ public function getRateOriginRequest($store = null)
return $request;
}
+ /**
+ * Return the default rate request. It can be either based on store address or customer address
+ *
+ * @param null|int|string|Store $store
+ * @return \Magento\Framework\Object
+ */
+ public function getDefaultRateRequest($store = null)
+ {
+ if ($this->_isCrossBorderTradeEnabled($store)) {
+ //If cross border trade is enabled, we will use customer tax rate as store tax rate
+ return $this->getRateRequest(null, null, null, $store);
+ } else {
+ return $this->getRateOriginRequest($store);
+ }
+ }
+
+ /**
+ * Return whether cross border trade is enabled or not
+ *
+ * @param null|int|string|Store $store
+ * @return bool
+ */
+ protected function _isCrossBorderTradeEnabled($store = null)
+ {
+ return (bool)$this->_config->crossBorderTradeEnabled($store);
+ }
+
/**
* Get request object with information necessary for getting tax rate
*
@@ -507,7 +578,7 @@ public function getRateRequest(
if (is_null($customerTaxClass) && $customerData->getId()) {
$customerTaxClass = $this->_groupService->getGroup($customerData->getGroupId())->getTaxClassId();
} elseif ($customerTaxClass === false || !$customerData->getId()) {
- $customerTaxClass = $this->getDefaultCustomerTaxClass($store);
+ $customerTaxClass = $this->_groupService->getGroup(GroupServiceInterface::NOT_LOGGED_IN_ID)->getTaxClassId();
}
$request = new \Magento\Framework\Object();
@@ -589,6 +660,8 @@ public function compareRequests($first, $second)
}
/**
+ * Gets the tax rates by type
+ *
* @param \Magento\Framework\Object $request
* @param string|array $fieldName
* @param string|array $type
@@ -608,6 +681,8 @@ protected function _getRates($request, $fieldName, $type)
}
/**
+ * Gets rates for all the product tax classes
+ *
* @param \Magento\Framework\Object $request
* @return array
*/
@@ -617,6 +692,8 @@ public function getRatesForAllProductTaxClasses($request)
}
/**
+ * Gets rates for all the customer tax classes
+ *
* @param \Magento\Framework\Object $request
* @return array
*/
@@ -633,6 +710,10 @@ public function getRatesForAllCustomerTaxClasses($request)
*/
public function getAppliedRates($request)
{
+ if (!$request->getCountryId() || !$request->getCustomerClassId() || !$request->getProductClassId()) {
+ return array();
+ }
+
$cacheKey = $this->_getRequestCacheKey($request);
if (!isset($this->_rateCalculationProcess[$cacheKey])) {
$this->_rateCalculationProcess[$cacheKey] = $this->_getResource()->getCalculationProcess($request);
@@ -641,6 +722,8 @@ public function getAppliedRates($request)
}
/**
+ * Gets the calculation process
+ *
* @param array $rates
* @return array
*/
@@ -650,6 +733,8 @@ public function reproduceProcess($rates)
}
/**
+ * Get rates by customer tax class
+ *
* @param int $customerTaxClass
* @return array
*/
@@ -659,6 +744,8 @@ public function getRatesByCustomerTaxClass($customerTaxClass)
}
/**
+ * Get rates by customer and product classes
+ *
* @param int $customerTaxClass
* @param int $productTaxClass
* @return array
diff --git a/app/code/Magento/Tax/Model/Calculation/Rate.php b/app/code/Magento/Tax/Model/Calculation/Rate.php
index 8a179c28e0bb6..fbb5f71e6241f 100644
--- a/app/code/Magento/Tax/Model/Calculation/Rate.php
+++ b/app/code/Magento/Tax/Model/Calculation/Rate.php
@@ -21,6 +21,7 @@
* @copyright Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
*/
+namespace Magento\Tax\Model\Calculation;
/**
* Tax Rate Model
@@ -44,17 +45,17 @@
* @method int getZipTo()
* @method \Magento\Tax\Model\Calculation\Rate setZipTo(int $value)
*/
-namespace Magento\Tax\Model\Calculation;
-
class Rate extends \Magento\Framework\Model\AbstractModel
{
/**
- * @var mixed
+ * List of tax titles
+ *
+ * @var array
*/
protected $_titles = null;
/**
- * @var mixed
+ * @var \Magento\Tax\Model\Calculation\Rate\Title
*/
protected $_titleModel = null;
@@ -104,7 +105,7 @@ protected function _construct()
/**
* Prepare location settings and tax postcode before save rate
*
- * @return $this
+ * @return \Magento\Tax\Model\Calculation\Rate
* @throws \Magento\Framework\Model\Exception
*/
protected function _beforeSave()
@@ -120,7 +121,7 @@ protected function _beforeSave()
throw new \Magento\Framework\Model\Exception(__('Please fill all required fields with valid information.'));
}
- if (!is_numeric($this->getRate()) || $this->getRate() <= 0) {
+ if (!is_numeric($this->getRate()) || $this->getRate() < 0) {
throw new \Magento\Framework\Model\Exception(__('Rate Percent should be a positive number.'));
}
@@ -166,7 +167,7 @@ protected function _beforeSave()
/**
* Save rate titles
*
- * @return $this
+ * @return \Magento\Tax\Model\Calculation\Rate
*/
protected function _afterSave()
{
@@ -178,7 +179,7 @@ protected function _afterSave()
/**
* Processing object before delete data
*
- * @return $this
+ * @return \Magento\Tax\Model\Calculation\Rate
* @throws \Magento\Framework\Model\Exception
*/
protected function _beforeDelete()
@@ -193,7 +194,7 @@ protected function _beforeDelete()
* After rate delete
* redeclared for dispatch tax_settings_change_after event
*
- * @return $this
+ * @return \Magento\Tax\Model\Calculation\Rate
*/
protected function _afterDelete()
{
@@ -202,6 +203,8 @@ protected function _afterDelete()
}
/**
+ * Saves the tax titles
+ *
* @param array|null $titles
* @return void
*/
@@ -230,7 +233,9 @@ public function saveTitles($titles = null)
}
/**
- * @return mixed
+ * Returns a tax title
+ *
+ * @return \Magento\Tax\Model\Calculation\Rate\Title
*/
public function getTitleModel()
{
@@ -241,7 +246,9 @@ public function getTitleModel()
}
/**
- * @return mixed
+ * Returns the list of tax titles
+ *
+ * @return array
*/
public function getTitles()
{
@@ -252,7 +259,9 @@ public function getTitles()
}
/**
- * @return $this
+ * Deletes all tax rates
+ *
+ * @return \Magento\Tax\Model\Calculation\Rate
*/
public function deleteAllRates()
{
@@ -265,7 +274,7 @@ public function deleteAllRates()
* Load rate model by code
*
* @param string $code
- * @return $this
+ * @return \Magento\Tax\Model\Calculation\Rate
*/
public function loadByCode($code)
{
diff --git a/app/code/Magento/Tax/Model/Calculation/Rule.php b/app/code/Magento/Tax/Model/Calculation/Rule.php
index 0efad2bc0ff73..86bd3f79decda 100644
--- a/app/code/Magento/Tax/Model/Calculation/Rule.php
+++ b/app/code/Magento/Tax/Model/Calculation/Rule.php
@@ -21,6 +21,7 @@
* @copyright Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
*/
+namespace Magento\Tax\Model\Calculation;
/**
* Tax Rule Model
@@ -34,40 +35,8 @@
* @method int getPosition()
* @method \Magento\Tax\Model\Calculation\Rule setPosition(int $value)
*/
-namespace Magento\Tax\Model\Calculation;
-
class Rule extends \Magento\Framework\Model\AbstractModel
{
- /**
- * @var mixed
- */
- protected $_ctcs = null;
-
- /**
- * @var mixed
- */
- protected $_ptcs = null;
-
- /**
- * @var mixed
- */
- protected $_rates = null;
-
- /**
- * @var mixed
- */
- protected $_ctcModel = null;
-
- /**
- * @var mixed
- */
- protected $_ptcModel = null;
-
- /**
- * @var mixed
- */
- protected $_rateModel = null;
-
/**
* Prefix of model events names
*
@@ -125,7 +94,7 @@ public function __construct(
/**
* After save rule
- * Redeclared for populate rate calculations
+ * Re-declared for populate rate calculations
*
* @return $this
*/
@@ -139,7 +108,7 @@ protected function _afterSave()
/**
* After rule delete
- * re-declared for dispatch tax_settings_change_after event
+ * Re-declared for dispatch tax_settings_change_after event
*
* @return $this
*/
@@ -260,4 +229,18 @@ public function getAllOptionsForClass($classFilter)
return $classes;
}
+
+ /**
+ * Fetches rules by rate, customer tax class and product tax class
+ * and product tax class combination
+ *
+ * @param array $rateId
+ * @param array $customerTaxClassIds
+ * @param array $productTaxClassIds
+ * @return array
+ */
+ public function fetchRuleCodes($rateId, $customerTaxClassIds, $productTaxClassIds)
+ {
+ return $this->getResource()->fetchRuleCodes($rateId, $customerTaxClassIds, $productTaxClassIds);
+ }
}
diff --git a/app/code/Magento/Tax/Model/Config.php b/app/code/Magento/Tax/Model/Config.php
index a660384ab1c12..83aae6075cfec 100644
--- a/app/code/Magento/Tax/Model/Config.php
+++ b/app/code/Magento/Tax/Model/Config.php
@@ -51,6 +51,8 @@ class Config
const XML_PATH_ALGORITHM = 'tax/calculation/algorithm';
+ const CONFIG_XML_PATH_CROSS_BORDER_TRADE_ENABLED = 'tax/calculation/cross_border_trade_enabled';
+
// tax defaults
const CONFIG_XML_PATH_DEFAULT_COUNTRY = 'tax/defaults/country';
@@ -107,6 +109,15 @@ class Config
const DISPLAY_TYPE_BOTH = 3;
+ /**
+ * Indexes for FPT Configuration Types
+ */
+ const FPT_NOT_TAXED = 0;
+
+ const FPT_TAXED = 1;
+
+ const FPT_LOADED_DISPLAY_WITH_TAX = 2;
+
/**
* @var bool|null
*/
@@ -732,4 +743,19 @@ public function displaySalesZeroTax($store = null)
$store
);
}
+
+ /**
+ * Return the config value for self::CONFIG_XML_PATH_CROSS_BORDER_TRADE_ENABLED
+ *
+ * @param null|string|bool|int|Store $store
+ * @return bool
+ */
+ public function crossBorderTradeEnabled($store = null)
+ {
+ return (bool)$this->_scopeConfig->getValue(
+ self::CONFIG_XML_PATH_CROSS_BORDER_TRADE_ENABLED,
+ \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
+ $store
+ );
+ }
}
diff --git a/app/code/Magento/Tax/Model/Rate/CsvImportHandler.php b/app/code/Magento/Tax/Model/Rate/CsvImportHandler.php
index 2bb5942d8079c..a2b63a019e519 100644
--- a/app/code/Magento/Tax/Model/Rate/CsvImportHandler.php
+++ b/app/code/Magento/Tax/Model/Rate/CsvImportHandler.php
@@ -252,7 +252,7 @@ protected function _importRate(array $rateData, array $regionsCache, array $stor
if (!empty($regionsCache[$countryCode][$regionCode])) {
$regionId = $regionsCache[$countryCode][$regionCode] == '*' ? 0 : $regionsCache[$countryCode][$regionCode];
// data with index 3 must represent postcode
- $postCode = empty($rateData[3]) || $rateData[3] == '*' ? null : $rateData[3];
+ $postCode = empty($rateData[3]) ? null : $rateData[3];
$modelData = array(
'code' => $rateData[0],
'tax_country_id' => $rateData[1],
diff --git a/app/code/Magento/Tax/Model/Resource/Calculation.php b/app/code/Magento/Tax/Model/Resource/Calculation.php
index 8dd4d4a9f1312..46f270187aef1 100644
--- a/app/code/Magento/Tax/Model/Resource/Calculation.php
+++ b/app/code/Magento/Tax/Model/Resource/Calculation.php
@@ -207,13 +207,18 @@ public function getCalculationProcess($request, $rates = null)
$rates[$i + 1]['process']
) && $rates[$i + 1]['process'] != $rate['process']
) {
- $row['percent'] = (100 + $totalPercent) * ($currentRate / 100);
+ if (!empty($rates[$i]['calculate_subtotal'])) {
+ $row['percent'] = $currentRate;
+ $totalPercent += $currentRate;
+ } else {
+ $row['percent'] = $this->_collectPercent($totalPercent, $currentRate);
+ $totalPercent += $row['percent'];
+ }
$row['id'] = implode($ids);
$result[] = $row;
$row = array();
$ids = array();
- $totalPercent += (100 + $totalPercent) * ($currentRate / 100);
$currentRate = 0;
}
}
@@ -221,6 +226,18 @@ public function getCalculationProcess($request, $rates = null)
return $result;
}
+ /**
+ * Return combined percent value
+ *
+ * @param float|int $percent
+ * @param float|int $rate
+ * @return float
+ */
+ protected function _collectPercent($percent, $rate)
+ {
+ return (100 + $percent) * ($rate / 100);
+ }
+
/**
* Create search templates for postcode
*
@@ -236,7 +253,7 @@ protected function _createSearchPostCodeTemplates($postcode)
$strlen = $len;
}
- $strArr = array($postcode, $postcode . '*');
+ $strArr = array((string)$postcode, $postcode . '*');
if ($strlen > 1) {
for ($i = 1; $i < $strlen; $i++) {
$strArr[] = sprintf('%s*', substr($postcode, 0, -$i));
@@ -305,7 +322,7 @@ protected function _getRates($request)
$select->join(
array('rule' => $this->getTable('tax_calculation_rule')),
$ruleTableAliasName . ' = main_table.tax_calculation_rule_id',
- array('rule.priority', 'rule.position')
+ array('rule.priority', 'rule.position', 'rule.calculate_subtotal')
)->join(
array('rate' => $this->getTable('tax_calculation_rate')),
'rate.tax_calculation_rate_id = main_table.tax_calculation_rate_id',
@@ -417,7 +434,11 @@ protected function _calculateRate($rates)
$currentRate += $value;
if (!isset($rates[$i + 1]) || $rates[$i + 1]['priority'] != $priority) {
- $result += (100 + $result) * ($currentRate / 100);
+ if (!empty($rates[$i]['calculate_subtotal'])) {
+ $result += $currentRate;
+ } else {
+ $result += $this->_collectPercent($result, $currentRate);
+ }
$currentRate = 0;
}
}
diff --git a/app/code/Magento/Tax/Model/Resource/Calculation/Rate/Collection.php b/app/code/Magento/Tax/Model/Resource/Calculation/Rate/Collection.php
index e507eba83c0bd..4392e21914795 100644
--- a/app/code/Magento/Tax/Model/Resource/Calculation/Rate/Collection.php
+++ b/app/code/Magento/Tax/Model/Resource/Calculation/Rate/Collection.php
@@ -30,6 +30,11 @@
class Collection extends \Magento\Framework\Model\Resource\Db\Collection\AbstractCollection
{
+ /**
+ * Value of fetched from DB of rules per cycle
+ */
+ const TAX_RULES_CHUNK_SIZE = 1000;
+
/**
* @var \Magento\Store\Model\StoreManagerInterface
*/
@@ -196,4 +201,32 @@ public function toOptionHashOptimized()
}
return $result;
}
+
+ /**
+ * Get rates array without memory leak
+ *
+ * @return array
+ */
+ public function getOptionRates()
+ {
+ $size = self::TAX_RULES_CHUNK_SIZE;
+ $page = 1;
+ $rates = array();
+ do {
+ $offset = $size * ($page - 1);
+ $this->getSelect()->reset();
+ $this->getSelect()
+ ->from(
+ array('rates' => $this->getMainTable()),
+ array('tax_calculation_rate_id', 'code')
+ )
+ ->limit($size, $offset);
+
+ $rates = array_merge($rates, $this->toOptionArray());
+ $this->clear();
+ $page++;
+ } while ($this->getSize() > $offset);
+
+ return $rates;
+ }
}
diff --git a/app/code/Magento/Tax/Model/Resource/Calculation/Rule.php b/app/code/Magento/Tax/Model/Resource/Calculation/Rule.php
index 9b3e0f94bba6d..b16cd4bec9070 100644
--- a/app/code/Magento/Tax/Model/Resource/Calculation/Rule.php
+++ b/app/code/Magento/Tax/Model/Resource/Calculation/Rule.php
@@ -50,4 +50,30 @@ protected function _initUniqueFields()
$this->_uniqueFields = array(array('field' => array('code'), 'title' => __('Code')));
return $this;
}
+
+ /**
+ * Fetches rules by rate, customer tax classes and product tax classes. Returns array of rule codes.
+ *
+ * @param array $rateId
+ * @param array $customerTaxClassIds
+ * @param array $productTaxClassIds
+ * @return array
+ */
+ public function fetchRuleCodes($rateId, $customerTaxClassIds, $productTaxClassIds)
+ {
+ $adapter = $this->_getReadAdapter();
+ $select = $adapter->select()
+ ->from(array('main' => $this->getTable('tax_calculation')), null)
+ ->joinLeft(
+ array('d' => $this->getTable('tax_calculation_rule')),
+ 'd.tax_calculation_rule_id = main.tax_calculation_rule_id',
+ array('d.code')
+ )
+ ->where('main.tax_calculation_rate_id in (?)', $rateId)
+ ->where('main.customer_tax_class_id in (?)', $customerTaxClassIds)
+ ->where('main.product_tax_class_id in (?)', $productTaxClassIds)
+ ->distinct(true);
+
+ return $adapter->fetchCol($select);
+ }
}
diff --git a/app/code/Magento/Tax/Model/Sales/Total/Quote/Shipping.php b/app/code/Magento/Tax/Model/Sales/Total/Quote/Shipping.php
index fdbf31f0b8667..7b1d6683e6f4a 100644
--- a/app/code/Magento/Tax/Model/Sales/Total/Quote/Shipping.php
+++ b/app/code/Magento/Tax/Model/Sales/Total/Quote/Shipping.php
@@ -41,6 +41,13 @@ class Shipping extends \Magento\Sales\Model\Quote\Address\Total\AbstractTotal
*/
protected $_config = null;
+ /**
+ * Tax helper instance
+ *
+ * @var \Magento\Tax\Helper\Data|null
+ */
+ protected $_taxHelper = null;
+
/**
* Flag which is initialized when collect method is started and catalog prices include tax.
* It is used for checking if store tax and customer tax requests are similar
@@ -61,12 +68,17 @@ class Shipping extends \Magento\Sales\Model\Quote\Address\Total\AbstractTotal
*
* @param \Magento\Tax\Model\Calculation $calculation
* @param \Magento\Tax\Model\Config $taxConfig
+ * @param \Magento\Tax\Helper\Data $taxHelper
*/
- public function __construct(\Magento\Tax\Model\Calculation $calculation, \Magento\Tax\Model\Config $taxConfig)
- {
+ public function __construct(
+ \Magento\Tax\Model\Calculation $calculation,
+ \Magento\Tax\Model\Config $taxConfig,
+ \Magento\Tax\Helper\Data $taxHelper
+ ) {
$this->setCode('shipping');
$this->_calculator = $calculation;
$this->_config = $taxConfig;
+ $this->_taxHelper = $taxHelper;
}
/**
@@ -94,7 +106,12 @@ public function collect(Address $address)
$priceIncludesTax = $this->_config->shippingPriceIncludesTax($store);
if ($priceIncludesTax) {
- $this->_areTaxRequestsSimilar = $calc->compareRequests($addressTaxRequest, $storeTaxRequest);
+ if ($this->_taxHelper->isCrossBorderTradeEnabled($store)) {
+ $this->_areTaxRequestsSimilar = true;
+ } else {
+ $this->_areTaxRequestsSimilar =
+ $this->_calculator->compareRequests($storeTaxRequest, $addressTaxRequest);
+ }
}
$shipping = $taxShipping = $address->getShippingAmount();
@@ -111,36 +128,54 @@ public function collect(Address $address)
$taxable = $taxShipping;
$baseTaxable = $baseTaxShipping;
$isPriceInclTax = true;
+ $address->setTotalAmount('shipping', $shipping);
+ $address->setBaseTotalAmount('shipping', $baseShipping);
} else {
$storeRate = $calc->getStoreRate($addressTaxRequest, $store);
$storeTax = $calc->calcTaxAmount($shipping, $storeRate, true, false);
$baseStoreTax = $calc->calcTaxAmount($baseShipping, $storeRate, true, false);
$shipping = $calc->round($shipping - $storeTax);
$baseShipping = $calc->round($baseShipping - $baseStoreTax);
- $tax = $this->_round($calc->calcTaxAmount($shipping, $rate, false, false), $rate, false);
+ $tax = $this->_round($calc->calcTaxAmount($shipping, $rate, false, false), $rate, true);
$baseTax = $this->_round(
$calc->calcTaxAmount($baseShipping, $rate, false, false),
$rate,
- false,
+ true,
'base'
);
$taxShipping = $shipping + $tax;
$baseTaxShipping = $baseShipping + $baseTax;
- $taxable = $shipping;
- $baseTaxable = $baseShipping;
- $isPriceInclTax = false;
+ $taxable = $taxShipping;
+ $baseTaxable = $baseTaxShipping;
+ $isPriceInclTax = true;
+ $address->setTotalAmount('shipping', $shipping);
+ $address->setBaseTotalAmount('shipping', $baseShipping);
}
} else {
- $tax = $this->_round($calc->calcTaxAmount($shipping, $rate, false, false), $rate, false);
- $baseTax = $this->_round($calc->calcTaxAmount($baseShipping, $rate, false, false), $rate, false, 'base');
+ $appliedRates = $calc->getAppliedRates($addressTaxRequest);
+ $taxes = array();
+ $baseTaxes = array();
+ foreach ($appliedRates as $appliedRate) {
+ $taxRate = $appliedRate['percent'];
+ $taxId = $appliedRate['id'];
+ $taxes[] = $this->_round($calc->calcTaxAmount($shipping, $taxRate, false, false), $taxId, false);
+ $baseTaxes[] = $this->_round(
+ $calc->calcTaxAmount($baseShipping, $taxRate, false, false),
+ $taxId,
+ false,
+ 'base'
+ );
+ }
+ $tax = array_sum($taxes);
+ $baseTax = array_sum($baseTaxes);
$taxShipping = $shipping + $tax;
$baseTaxShipping = $baseShipping + $baseTax;
$taxable = $shipping;
$baseTaxable = $baseShipping;
$isPriceInclTax = false;
+ $address->setTotalAmount('shipping', $shipping);
+ $address->setBaseTotalAmount('shipping', $baseShipping);
}
- $address->setTotalAmount('shipping', $shipping);
- $address->setBaseTotalAmount('shipping', $baseShipping);
$address->setShippingInclTax($taxShipping);
$address->setBaseShippingInclTax($baseTaxShipping);
$address->setShippingTaxable($taxable);
diff --git a/app/code/Magento/Tax/Model/Sales/Total/Quote/Subtotal.php b/app/code/Magento/Tax/Model/Sales/Total/Quote/Subtotal.php
index 25d3bfb0b5dde..c3a32e2eec2f4 100644
--- a/app/code/Magento/Tax/Model/Sales/Total/Quote/Subtotal.php
+++ b/app/code/Magento/Tax/Model/Sales/Total/Quote/Subtotal.php
@@ -29,6 +29,7 @@
use Magento\Sales\Model\Quote\Address;
use Magento\Sales\Model\Quote\Item\AbstractItem;
+use Magento\Tax\Model\Calculation;
class Subtotal extends \Magento\Sales\Model\Quote\Address\Total\AbstractTotal
{
@@ -47,30 +48,12 @@ class Subtotal extends \Magento\Sales\Model\Quote\Address\Total\AbstractTotal
protected $_config = null;
/**
+ * Tax helper
+ *
* @var \Magento\Tax\Helper\Data|null
*/
protected $_helper = null;
- /**
- * @var int
- */
- protected $_subtotalInclTax = 0;
-
- /**
- * @var int
- */
- protected $_baseSubtotalInclTax = 0;
-
- /**
- * @var int
- */
- protected $_subtotal = 0;
-
- /**
- * @var int
- */
- protected $_baseSubtotal = 0;
-
/**
* Flag which is initialized when collect method is started and catalog prices include tax.
* Is used for checking if store tax and customer tax requests are similar
@@ -100,13 +83,6 @@ class Subtotal extends \Magento\Sales\Model\Quote\Address\Total\AbstractTotal
*/
protected $_roundingDeltas = array();
- /**
- * Tax data
- *
- * @var \Magento\Tax\Helper\Data
- */
- protected $_taxData = null;
-
/**
* Class constructor
*
@@ -119,9 +95,8 @@ public function __construct(
\Magento\Tax\Model\Calculation $calculation,
\Magento\Tax\Model\Config $taxConfig
) {
- $this->_taxData = $taxData;
$this->setCode('tax_subtotal');
- $this->_helper = $this->_taxData;
+ $this->_helper = $taxData;
$this->_calculator = $calculation;
$this->_config = $taxConfig;
}
@@ -139,10 +114,6 @@ public function collect(Address $address)
$this->_store = $address->getQuote()->getStore();
$this->_address = $address;
- $this->_subtotalInclTax = 0;
- $this->_baseSubtotalInclTax = 0;
- $this->_subtotal = 0;
- $this->_baseSubtotal = 0;
$this->_roundingDeltas = array();
$address->setSubtotalInclTax(0);
@@ -155,9 +126,10 @@ public function collect(Address $address)
return $this;
}
+ $this->_calculator->setCustomerData($address->getQuote()->getCustomerData());
+
$addressRequest = $this->_getAddressTaxRequest($address);
$storeRequest = $this->_getStoreTaxRequest($address);
- $this->_calculator->setCustomerData($address->getQuote()->getCustomerData());
if ($this->_config->priceIncludesTax($this->_store)) {
$classIds = array();
foreach ($items as $item) {
@@ -171,7 +143,11 @@ public function collect(Address $address)
$classIds = array_unique($classIds);
$storeRequest->setProductClassId($classIds);
$addressRequest->setProductClassId($classIds);
- $this->_areTaxRequestsSimilar = $this->_calculator->compareRequests($storeRequest, $addressRequest);
+ if ($this->_helper->isCrossBorderTradeEnabled($this->_store)) {
+ $this->_areTaxRequestsSimilar = true;
+ } else {
+ $this->_areTaxRequestsSimilar = $this->_calculator->compareRequests($storeRequest, $addressRequest);
+ }
}
foreach ($items as $item) {
@@ -193,7 +169,7 @@ public function collect(Address $address)
}
/**
- * Caclulate item price and row total with customized rounding level
+ * Calculate item price and row total with customized rounding level
*
* @param AbstractItem $item
* @param \Magento\Framework\Object $taxRequest
@@ -202,13 +178,13 @@ public function collect(Address $address)
protected function _processItem($item, $taxRequest)
{
switch ($this->_config->getAlgorithm($this->_store)) {
- case \Magento\Tax\Model\Calculation::CALC_UNIT_BASE:
+ case Calculation::CALC_UNIT_BASE:
$this->_unitBaseCalculation($item, $taxRequest);
break;
- case \Magento\Tax\Model\Calculation::CALC_ROW_BASE:
+ case Calculation::CALC_ROW_BASE:
$this->_rowBaseCalculation($item, $taxRequest);
break;
- case \Magento\Tax\Model\Calculation::CALC_TOTAL_BASE:
+ case Calculation::CALC_TOTAL_BASE:
$this->_totalBaseCalculation($item, $taxRequest);
break;
default:
@@ -230,69 +206,78 @@ protected function _unitBaseCalculation($item, $request)
$rate = $this->_calculator->getRate($request);
$qty = $item->getTotalQty();
- $price = $taxPrice = $item->getCalculationPriceOriginal();
- $basePrice = $baseTaxPrice = $item->getBaseCalculationPriceOriginal();
- $subtotal = $taxSubtotal = $item->getRowTotal();
- $baseSubtotal = $baseTaxSubtotal = $item->getBaseRowTotal();
+ $price = $taxPrice = $this->_calculator->round($item->getCalculationPriceOriginal());
+ $basePrice = $baseTaxPrice = $this->_calculator->round($item->getBaseCalculationPriceOriginal());
+ $subtotal = $taxSubtotal = $this->_calculator->round($item->getRowTotal());
+ $baseSubtotal = $baseTaxSubtotal = $this->_calculator->round($item->getBaseRowTotal());
+
+ // if we have a custom price, determine if tax should be based on the original price
$taxOnOrigPrice = !$this->_helper->applyTaxOnCustomPrice($this->_store) && $item->hasCustomPrice();
if ($taxOnOrigPrice) {
$origPrice = $item->getOriginalPrice();
$baseOrigPrice = $item->getBaseOriginalPrice();
}
-
$item->setTaxPercent($rate);
if ($this->_config->priceIncludesTax($this->_store)) {
if ($this->_sameRateAsStore($request)) {
- $tax = $this->_calculator->calcTaxAmount($price, $rate, true);
- $baseTax = $this->_calculator->calcTaxAmount($basePrice, $rate, true);
- $taxPrice = $price;
- $baseTaxPrice = $basePrice;
- $taxSubtotal = $subtotal;
+ // determine which price to use when we calculate the tax
+ if ($taxOnOrigPrice) {
+ $taxable = $origPrice;
+ $baseTaxable = $baseOrigPrice;
+ } else {
+ $taxable = $price;
+ $baseTaxable = $basePrice;
+ }
+ $tax = $this->_calculator->calcTaxAmount($taxable, $rate, true);
+ $baseTax = $this->_calculator->calcTaxAmount($baseTaxable, $rate, true);
+ $taxPrice = $price;
+ $baseTaxPrice = $basePrice;
+ $taxSubtotal = $subtotal;
$baseTaxSubtotal = $baseSubtotal;
$price = $price - $tax;
$basePrice = $basePrice - $baseTax;
$subtotal = $price * $qty;
$baseSubtotal = $basePrice * $qty;
+ $isPriceInclTax = true;
+
+ $item->setRowTax($tax * $qty);
+ $item->setBaseRowTax($baseTax * $qty);
+ } else {
+ $storeRate = $this->_calculator->getStoreRate($request, $this->_store);
if ($taxOnOrigPrice) {
- $taxable = $origPrice;
- $baseTaxable = $baseOrigPrice;
+ // the merchant already provided a customer's price that includes tax
+ $taxPrice = $price;
+ $baseTaxPrice = $basePrice;
+ // determine which price to use when we calculate the tax
+ $taxable = $this->_calculatePriceInclTax($origPrice, $storeRate, $rate);
+ $baseTaxable = $this->_calculatePriceInclTax($baseOrigPrice, $storeRate, $rate);
} else {
- $taxable = $taxPrice;
- $baseTaxable = $baseTaxPrice;
+ // determine the customer's price that includes tax
+ $taxPrice = $this->_calculatePriceInclTax($price, $storeRate, $rate);
+ $baseTaxPrice = $this->_calculatePriceInclTax($basePrice, $storeRate, $rate);
+ // determine which price to use when we calculate the tax
+ $taxable = $taxPrice;
+ $baseTaxable = $baseTaxPrice;
}
- $isPriceInclTax = true;
- } else {
- $storeRate = $this->_calculator->getStoreRate($request, $this->_store);
- $storeTax = $this->_calculator->calcTaxAmount($price, $storeRate, true);
- $baseStoreTax = $this->_calculator->calcTaxAmount($basePrice, $storeRate, true);
- $price = $price - $storeTax;
- $basePrice = $basePrice - $baseStoreTax;
+ // determine the customer's tax amount
+ $tax = $this->_calculator->calcTaxAmount($taxable, $rate, true, true);
+ $baseTax = $this->_calculator->calcTaxAmount($baseTaxable, $rate, true, true);
+ // determine the customer's price without taxes
+ $price = $taxPrice - $tax;
+ $basePrice = $baseTaxPrice - $baseTax;
+ // determine subtotal amounts
+ $taxSubtotal = $taxPrice * $qty;
+ $baseTaxSubtotal = $baseTaxPrice * $qty;
$subtotal = $price * $qty;
$baseSubtotal = $basePrice * $qty;
+ $isPriceInclTax = true;
- $tax = $this->_calculator->calcTaxAmount($price, $rate, false);
- $baseTax = $this->_calculator->calcTaxAmount($basePrice, $rate, false);
- $taxPrice = $price + $tax;
- $baseTaxPrice = $basePrice + $baseTax;
- $taxSubtotal = $taxPrice * $qty;
- $baseTaxSubtotal = $baseTaxPrice * $qty;
- if ($taxOnOrigPrice) {
- $taxable = $origPrice - $storeTax;
- $baseTaxable = $baseOrigPrice - $baseStoreTax;
- } else {
- $taxable = $price;
- $baseTaxable = $basePrice;
- }
- $isPriceInclTax = false;
+ $item->setRowTax($tax * $qty);
+ $item->setBaseRowTax($baseTax * $qty);
}
} else {
- $tax = $this->_calculator->calcTaxAmount($price, $rate, false);
- $baseTax = $this->_calculator->calcTaxAmount($basePrice, $rate, false);
- $taxPrice = $price + $tax;
- $baseTaxPrice = $basePrice + $baseTax;
- $taxSubtotal = $taxPrice * $qty;
- $baseTaxSubtotal = $baseTaxPrice * $qty;
+ // determine which price to use when we calculate the tax
if ($taxOnOrigPrice) {
$taxable = $origPrice;
$baseTaxable = $baseOrigPrice;
@@ -300,7 +285,21 @@ protected function _unitBaseCalculation($item, $request)
$taxable = $price;
$baseTaxable = $basePrice;
}
- $isPriceInclTax = false;
+ $appliedRates = $this->_calculator->getAppliedRates($request);
+ $taxes = array();
+ $baseTaxes = array();
+ foreach ($appliedRates as $appliedRate) {
+ $taxRate = $appliedRate['percent'];
+ $taxes[] = $this->_calculator->calcTaxAmount($taxable, $taxRate, false);
+ $baseTaxes[] = $this->_calculator->calcTaxAmount($baseTaxable, $taxRate, false);
+ }
+ $tax = array_sum($taxes);
+ $baseTax = array_sum($baseTaxes);
+ $taxPrice = $price + $tax;
+ $baseTaxPrice = $basePrice + $baseTax;
+ $taxSubtotal = $taxPrice * $qty;
+ $baseTaxSubtotal = $baseTaxPrice * $qty;
+ $isPriceInclTax = false;
}
if ($item->hasCustomPrice()) {
@@ -342,10 +341,12 @@ protected function _rowBaseCalculation($item, $request)
$rate = $this->_calculator->getRate($request);
$qty = $item->getTotalQty();
- $price = $taxPrice = $item->getCalculationPriceOriginal();
- $basePrice = $baseTaxPrice = $item->getBaseCalculationPriceOriginal();
- $subtotal = $taxSubtotal = $item->getRowTotal();
- $baseSubtotal = $baseTaxSubtotal = $item->getBaseRowTotal();
+ $price = $taxPrice = $this->_calculator->round($item->getCalculationPriceOriginal());
+ $basePrice = $baseTaxPrice = $this->_calculator->round($item->getBaseCalculationPriceOriginal());
+ $subtotal = $taxSubtotal = $this->_calculator->round($item->getRowTotal());
+ $baseSubtotal = $baseTaxSubtotal = $this->_calculator->round($item->getBaseRowTotal());
+
+ // if we have a custom price, determine if tax should be based on the original price
$taxOnOrigPrice = !$this->_helper->applyTaxOnCustomPrice($this->_store) && $item->hasCustomPrice();
if ($taxOnOrigPrice) {
$origSubtotal = $item->getOriginalPrice() * $qty;
@@ -355,55 +356,67 @@ protected function _rowBaseCalculation($item, $request)
$item->setTaxPercent($rate);
if ($this->_config->priceIncludesTax($this->_store)) {
if ($this->_sameRateAsStore($request)) {
- $rowTax = $this->_calculator->calcTaxAmount($subtotal, $rate, true, false);
- $baseRowTax = $this->_calculator->calcTaxAmount($baseSubtotal, $rate, true, false);
- $taxPrice = $price;
- $baseTaxPrice = $basePrice;
- $taxSubtotal = $subtotal;
- $baseTaxSubtotal = $baseSubtotal;
- $subtotal = $this->_calculator->round($subtotal - $rowTax);
- $baseSubtotal = $this->_calculator->round($baseSubtotal - $baseRowTax);
- $price = $this->_calculator->round($subtotal / $qty);
- $basePrice = $this->_calculator->round($baseSubtotal / $qty);
+ // determine which price to use when we calculate the tax
if ($taxOnOrigPrice) {
- $taxable = $origSubtotal;
- $baseTaxable = $baseOrigSubtotal;
+ $taxable = $origSubtotal;
+ $baseTaxable = $baseOrigSubtotal;
} else {
- $taxable = $taxSubtotal;
- $baseTaxable = $baseTaxSubtotal;
+ $taxable = $taxSubtotal;
+ $baseTaxable = $baseTaxSubtotal;
}
- $isPriceInclTax = true;
- } else {
- $storeRate = $this->_calculator->getStoreRate($request, $this->_store);
- $storeTax = $this->_calculator->calcTaxAmount($subtotal, $storeRate, true, false);
- $baseStoreTax = $this->_calculator->calcTaxAmount($baseSubtotal, $storeRate, true, false);
- $subtotal = $this->_calculator->round($subtotal - $storeTax);
- $baseSubtotal = $this->_calculator->round($baseSubtotal - $baseStoreTax);
+ $rowTax = $this->_calculator->calcTaxAmount($taxable, $rate, true, true);
+ $baseRowTax = $this->_calculator->calcTaxAmount($baseTaxable, $rate, true, true);
+ $taxPrice = $price;
+ $baseTaxPrice = $basePrice;
+ $taxSubtotal = $subtotal;
+ $baseTaxSubtotal = $baseSubtotal;
+ $subtotal = $this->_calculator->round($subtotal - $rowTax);
+ $baseSubtotal = $this->_calculator->round($baseSubtotal - $baseRowTax);
$price = $this->_calculator->round($subtotal / $qty);
$basePrice = $this->_calculator->round($baseSubtotal / $qty);
+ $isPriceInclTax = true;
- $rowTax = $this->_calculator->calcTaxAmount($subtotal, $rate, false, false);
- $baseRowTax = $this->_calculator->calcTaxAmount($baseSubtotal, $rate, false, false);
- $taxSubtotal = $subtotal + $rowTax;
- $baseTaxSubtotal = $baseSubtotal + $baseRowTax;
- $taxPrice = $this->_calculator->round($taxSubtotal / $qty);
- $baseTaxPrice = $this->_calculator->round($baseTaxSubtotal / $qty);
+ $item->setRowTax($rowTax);
+ $item->setBaseRowTax($baseRowTax);
+ } else {
+ $storeRate = $this->_calculator->getStoreRate($request, $this->_store);
if ($taxOnOrigPrice) {
- $taxable = $this->_calculator->round($origSubtotal - $storeTax);
- $baseTaxable = $this->_calculator->round($baseOrigSubtotal - $baseStoreTax);
+ // the merchant already provided a customer's price that includes tax
+ $taxPrice = $price;
+ $baseTaxPrice = $basePrice;
+ // determine which price to use when we calculate the tax
+ $taxable = $this->_calculatePriceInclTax($item->getOriginalPrice(), $storeRate, $rate);
+ $baseTaxable = $this->_calculatePriceInclTax($item->getBaseOriginalPrice(), $storeRate, $rate);
} else {
- $taxable = $subtotal;
- $baseTaxable = $baseSubtotal;
+ // determine the customer's price that includes tax
+ $taxPrice = $this->_calculatePriceInclTax($price, $storeRate, $rate);
+ $baseTaxPrice = $this->_calculatePriceInclTax($basePrice, $storeRate, $rate);
+ // determine which price to use when we calculate the tax
+ $taxable = $taxPrice;
+ $baseTaxable = $baseTaxPrice;
}
- $isPriceInclTax = false;
+ // determine the customer's tax amount
+ $tax = $this->_calculator->calcTaxAmount($taxable, $rate, true, true);
+ $baseTax = $this->_calculator->calcTaxAmount($baseTaxable, $rate, true, true);
+ // determine the customer's price without taxes
+ $price = $taxPrice - $tax;
+ $basePrice = $baseTaxPrice - $baseTax;
+ // determine subtotal amounts
+ $taxable *= $qty;
+ $baseTaxable *= $qty;
+ $taxSubtotal = $taxPrice * $qty;
+ $baseTaxSubtotal = $baseTaxPrice * $qty;
+ $rowTax = $this->_calculator->calcTaxAmount($taxable, $rate, true, true);
+ $baseRowTax = $this->_calculator->calcTaxAmount($baseTaxable, $rate, true, true);
+ $subtotal = $taxSubtotal - $rowTax;
+ $baseSubtotal = $baseTaxSubtotal - $baseRowTax;
+ $isPriceInclTax = true;
+
+ $item->setRowTax($rowTax);
+ $item->setBaseRowTax($baseRowTax);
}
} else {
- $rowTax = $this->_calculator->calcTaxAmount($subtotal, $rate, false, false);
- $baseRowTax = $this->_calculator->calcTaxAmount($baseSubtotal, $rate, false, false);
- $taxSubtotal = $subtotal + $rowTax;
- $baseTaxSubtotal = $baseSubtotal + $baseRowTax;
- $taxPrice = $this->_calculator->round($taxSubtotal / $qty);
- $baseTaxPrice = $this->_calculator->round($baseTaxSubtotal / $qty);
+ // determine which price to use when we calculate the tax
if ($taxOnOrigPrice) {
$taxable = $origSubtotal;
$baseTaxable = $baseOrigSubtotal;
@@ -411,7 +424,22 @@ protected function _rowBaseCalculation($item, $request)
$taxable = $subtotal;
$baseTaxable = $baseSubtotal;
}
- $isPriceInclTax = false;
+
+ $appliedRates = $this->_calculator->getAppliedRates($request);
+ $rowTaxes = array();
+ $baseRowTaxes = array();
+ foreach ($appliedRates as $appliedRate) {
+ $taxRate = $appliedRate['percent'];
+ $rowTaxes[] = $this->_calculator->calcTaxAmount($taxable, $taxRate, false, true);
+ $baseRowTaxes[] = $this->_calculator->calcTaxAmount($baseTaxable, $taxRate, false, true);
+ }
+ $rowTax = array_sum($rowTaxes);
+ $baseRowTax = array_sum($baseRowTaxes);
+ $taxSubtotal = $subtotal + $rowTax;
+ $baseTaxSubtotal = $baseSubtotal + $baseRowTax;
+ $taxPrice = $this->_calculator->round($taxSubtotal/$qty);
+ $baseTaxPrice = $this->_calculator->round($baseTaxSubtotal/$qty);
+ $isPriceInclTax = false;
}
if ($item->hasCustomPrice()) {
@@ -458,119 +486,119 @@ protected function _totalBaseCalculation($item, $request)
$rate = $calc->getRate($request);
$qty = $item->getTotalQty();
- $price = $taxPrice = $item->getCalculationPriceOriginal();
- $basePrice = $baseTaxPrice = $item->getBaseCalculationPriceOriginal();
- $subtotal = $taxSubtotal = $item->getRowTotal();
- $baseSubtotal = $baseTaxSubtotal = $item->getBaseRowTotal();
+ $price = $taxPrice = $this->_calculator->round($item->getCalculationPriceOriginal());
+ $basePrice = $baseTaxPrice = $this->_calculator->round($item->getBaseCalculationPriceOriginal());
+ $subtotal = $taxSubtotal = $this->_calculator->round($item->getRowTotal());
+ $baseSubtotal = $baseTaxSubtotal = $this->_calculator->round($item->getBaseRowTotal());
+ // if we have a custom price, determine if tax should be based on the original price
$taxOnOrigPrice = !$this->_helper->applyTaxOnCustomPrice($this->_store) && $item->hasCustomPrice();
if ($taxOnOrigPrice) {
$origSubtotal = $item->getOriginalPrice() * $qty;
$baseOrigSubtotal = $item->getBaseOriginalPrice() * $qty;
}
+
$item->setTaxPercent($rate);
if ($this->_config->priceIncludesTax($this->_store)) {
if ($this->_sameRateAsStore($request)) {
+ // determine which price to use when we calculate the tax
if ($taxOnOrigPrice) {
- $rowTax = $this->_deltaRound($calc->calcTaxAmount($origSubtotal, $rate, true, false), $rate, true);
- $baseRowTax = $this->_deltaRound(
- $calc->calcTaxAmount($baseOrigSubtotal, $rate, true, false),
- $rate,
- true,
- 'base'
- );
-
$taxable = $origSubtotal;
$baseTaxable = $baseOrigSubtotal;
} else {
- $rowTax = $this->_deltaRound($calc->calcTaxAmount($subtotal, $rate, true, false), $rate, true);
- $baseRowTax = $this->_deltaRound(
- $calc->calcTaxAmount($baseSubtotal, $rate, true, false),
- $rate,
- true,
- 'base'
- );
-
$taxable = $subtotal;
$baseTaxable = $baseSubtotal;
}
- $taxPrice = $price;
- $baseTaxPrice = $basePrice;
+ $rowTaxExact = $calc->calcTaxAmount($taxable, $rate, true, false);
+ $rowTax = $this->_deltaRound($rowTaxExact, $rate, true);
+ $baseRowTaxExact = $calc->calcTaxAmount($baseTaxable, $rate, true, false);
+ $baseRowTax = $this->_deltaRound($baseRowTaxExact, $rate, true, 'base');
+ $taxPrice = $price;
+ $baseTaxPrice = $basePrice;
$taxSubtotal = $subtotal;
$baseTaxSubtotal = $baseSubtotal;
- $subtotal = $subtotal - $rowTax;
- $baseSubtotal = $baseSubtotal - $baseRowTax;
+ $subtotal = $subtotal - $rowTax;
+ $baseSubtotal = $baseSubtotal - $baseRowTax;
$price = $calc->round($subtotal / $qty);
$basePrice = $calc->round($baseSubtotal / $qty);
- $isPriceInclTax = true;
+ $isPriceInclTax = true;
+
+ //Save the tax calculated
+ $item->setRowTax($rowTax);
+ $item->setBaseRowTax($baseRowTax);
} else {
$storeRate = $calc->getStoreRate($request, $this->_store);
if ($taxOnOrigPrice) {
- $storeTax = $calc->calcTaxAmount($origSubtotal, $storeRate, true, false);
- $baseStoreTax = $calc->calcTaxAmount($baseOrigSubtotal, $storeRate, true, false);
+ // the merchant already provided a customer's price that includes tax
+ $taxPrice = $price;
+ $baseTaxPrice = $basePrice;
+ // determine which price to use when we calculate the tax
+ $taxable = $this->_calculatePriceInclTax($item->getOriginalPrice(), $storeRate, $rate);
+ $baseTaxable = $this->_calculatePriceInclTax($item->getBaseOriginalPrice(), $storeRate, $rate);
} else {
- $storeTax = $calc->calcTaxAmount($subtotal, $storeRate, true, false);
- $baseStoreTax = $calc->calcTaxAmount($baseSubtotal, $storeRate, true, false);
+ // determine the customer's price that includes tax
+ $taxPrice = $this->_calculatePriceInclTax($price, $storeRate, $rate);
+ $baseTaxPrice = $this->_calculatePriceInclTax($basePrice, $storeRate, $rate);
+ // determine which price to use when we calculate the tax
+ $taxable = $taxPrice;
+ $baseTaxable = $baseTaxPrice;
}
- $subtotal = $calc->round($subtotal - $storeTax);
- $baseSubtotal = $calc->round($baseSubtotal - $baseStoreTax);
-
- $price = $calc->round($subtotal / $qty);
- $basePrice = $calc->round($baseSubtotal / $qty);
-
- $rowTax = $this->_deltaRound($calc->calcTaxAmount($subtotal, $rate, false, false), $rate, true);
- $baseRowTax = $this->_deltaRound(
- $calc->calcTaxAmount($baseSubtotal, $rate, false, false),
- $rate,
- true,
- 'base'
- );
-
- $taxSubtotal = $subtotal + $rowTax;
- $baseTaxSubtotal = $baseSubtotal + $baseRowTax;
-
- $taxPrice = $calc->round($taxSubtotal / $qty);
- $baseTaxPrice = $calc->round($baseTaxSubtotal / $qty);
-
- $taxable = $subtotal;
- $baseTaxable = $baseSubtotal;
-
- $isPriceInclTax = false;
+ // determine the customer's tax amount based on the taxable price
+ $tax = $this->_calculator->calcTaxAmount($taxable, $rate, true, true);
+ $baseTax = $this->_calculator->calcTaxAmount($baseTaxable, $rate, true, true);
+ // determine the customer's price without taxes
+ $price = $taxPrice - $tax;
+ $basePrice = $baseTaxPrice - $baseTax;
+ // determine subtotal amounts
+ $taxable *= $qty;
+ $baseTaxable *= $qty;
+ $taxSubtotal = $taxPrice * $qty;
+ $baseTaxSubtotal = $baseTaxPrice * $qty;
+ $rowTax =
+ $this->_deltaRound($calc->calcTaxAmount($taxable, $rate, true, false), $rate, true);
+ $baseRowTax =
+ $this->_deltaRound($calc->calcTaxAmount($baseTaxable, $rate, true, false), $rate, true, 'base');
+ $subtotal = $taxSubtotal - $rowTax;
+ $baseSubtotal = $baseTaxSubtotal - $baseRowTax;
+ $isPriceInclTax = true;
+
+ $item->setRowTax($rowTax);
+ $item->setBaseRowTax($baseRowTax);
}
} else {
+ // determine which price to use when we calculate the tax
if ($taxOnOrigPrice) {
- $rowTax = $this->_deltaRound($calc->calcTaxAmount($origSubtotal, $rate, false, false), $rate, true);
- $baseRowTax = $this->_deltaRound(
- $calc->calcTaxAmount($baseOrigSubtotal, $rate, false, false),
- $rate,
- true,
- 'base'
- );
-
$taxable = $origSubtotal;
$baseTaxable = $baseOrigSubtotal;
} else {
- $rowTax = $this->_deltaRound($calc->calcTaxAmount($subtotal, $rate, false, false), $rate, true);
- $baseRowTax = $this->_deltaRound(
- $calc->calcTaxAmount($baseSubtotal, $rate, false, false),
- $rate,
- true,
+ $taxable = $subtotal;
+ $baseTaxable = $baseSubtotal;
+ }
+ $appliedRates = $this->_calculator->getAppliedRates($request);
+ $rowTaxes = array();
+ $baseRowTaxes = array();
+ foreach ($appliedRates as $appliedRate) {
+ $taxId = $appliedRate['id'];
+ $taxRate = $appliedRate['percent'];
+ $rowTaxes[] = $this->_deltaRound($calc->calcTaxAmount($taxable, $taxRate, false, false), $taxId, false);
+ $baseRowTaxes[] = $this->_deltaRound(
+ $calc->calcTaxAmount($baseTaxable, $taxRate, false, false),
+ $taxId,
+ false,
'base'
);
- $taxable = $subtotal;
- $baseTaxable = $baseSubtotal;
}
- $taxSubtotal = $subtotal + $rowTax;
- $baseTaxSubtotal = $baseSubtotal + $baseRowTax;
+ $taxSubtotal = $subtotal + array_sum($rowTaxes);
+ $baseTaxSubtotal = $baseSubtotal + array_sum($baseRowTaxes);
- $taxPrice = $calc->round($taxSubtotal / $qty);
- $baseTaxPrice = $calc->round($baseTaxSubtotal / $qty);
+ $taxPrice = $calc->round($taxSubtotal/$qty);
+ $baseTaxPrice = $calc->round($baseTaxSubtotal/$qty);
$isPriceInclTax = false;
}
@@ -606,6 +634,25 @@ protected function _totalBaseCalculation($item, $request)
return $this;
}
+ /**
+ * Given a store price that includes tax at the store rate, this function will back out the store's tax, and add in
+ * the customer's tax. Returns this new price which is the customer's price including tax.
+ *
+ * @param float $storePriceInclTax
+ * @param float $storeRate
+ * @param float $customerRate
+ *
+ * @return float
+ */
+ protected function _calculatePriceInclTax($storePriceInclTax, $storeRate, $customerRate)
+ {
+ $storeTax = $this->_calculator->calcTaxAmount($storePriceInclTax, $storeRate, true, false);
+ $priceExclTax = $storePriceInclTax - $storeTax;
+ $customerTax = $this->_calculator->calcTaxAmount($priceExclTax, $customerRate, false, false);
+ $customerPriceInclTax = $this->_calculator->round($priceExclTax + $customerTax);
+ return $customerPriceInclTax;
+ }
+
/**
* Checks whether request for an item has same rate as store one
* Used only after collect() started, as far as uses optimized $_areTaxRequestsSimilar property
@@ -641,7 +688,8 @@ protected function _deltaRound($price, $rate, $direction, $type = 'regular')
if ($price) {
$rate = (string)$rate;
$type = $type . $direction;
- $delta = isset($this->_roundingDeltas[$type][$rate]) ? $this->_roundingDeltas[$type][$rate] : 0;
+ // initialize the delta to a small number to avoid non-deterministic behavior with rounding of 0.5
+ $delta = isset($this->_roundingDeltas[$type][$rate]) ? $this->_roundingDeltas[$type][$rate] :0.000001;
$price += $delta;
$this->_roundingDeltas[$type][$rate] = $price - $this->_calculator->round($price);
$price = $this->_calculator->round($price);
@@ -657,32 +705,34 @@ protected function _deltaRound($price, $rate, $direction, $type = 'regular')
*/
protected function _recalculateParent(AbstractItem $item)
{
- $price = 0;
- $basePrice = 0;
$rowTotal = 0;
$baseRowTotal = 0;
- $priceInclTax = 0;
- $basePriceInclTax = 0;
$rowTotalInclTax = 0;
$baseRowTotalInclTax = 0;
+ $rowTax = 0;
+ $baseRowTax = 0;
+ $store = $item->getStore();
+ $qty = $item->getQty();
+
foreach ($item->getChildren() as $child) {
- $price += $child->getPrice() * $child->getQty();
- $basePrice += $child->getBasePrice() * $child->getQty();
$rowTotal += $child->getRowTotal();
$baseRowTotal += $child->getBaseRowTotal();
- $priceInclTax += $child->getPriceInclTax() * $child->getQty();
- $basePriceInclTax += $child->getBasePriceInclTax() * $child->getQty();
$rowTotalInclTax += $child->getRowTotalInclTax();
$baseRowTotalInclTax += $child->getBaseRowTotalInclTax();
+ $rowTax += $child->getRowTax();
+ $baseRowTax += $child->getBaseRowTax();
}
- $item->setConvertedPrice($price);
- $item->setPrice($basePrice);
+
+ $item->setConvertedPrice($store->roundPrice($rowTotal) / $qty);
+ $item->setPrice($store->roundPrice($baseRowTotal) / $qty);
$item->setRowTotal($rowTotal);
$item->setBaseRowTotal($baseRowTotal);
- $item->setPriceInclTax($priceInclTax);
- $item->setBasePriceInclTax($basePriceInclTax);
+ $item->setPriceInclTax($store->roundPrice($rowTotalInclTax) / $qty);
+ $item->setBasePriceInclTax($store->roundPrice($baseRowTotalInclTax) / $qty);
$item->setRowTotalInclTax($rowTotalInclTax);
$item->setBaseRowTotalInclTax($baseRowTotalInclTax);
+ $item->setRowTax($rowTax);
+ $item->setBaseRowTax($baseRowTax);
return $this;
}
@@ -726,8 +776,21 @@ protected function _getAddressTaxRequest($address)
*/
protected function _addSubtotalAmount(Address $address, $item)
{
- $address->setTotalAmount('subtotal', $address->getTotalAmount('subtotal') + $item->getRowTotal());
- $address->setBaseTotalAmount('subtotal', $address->getBaseTotalAmount('subtotal') + $item->getBaseRowTotal());
+ if ($this->_config->priceIncludesTax($this->_store)) {
+ $subTotal = $item->getRowTotalInclTax() - $item->getRowTax();
+ $baseSubTotal = $item->getBaseRowTotalInclTax() - $item->getBaseRowTax();
+ $address->setTotalAmount('subtotal', $address->getTotalAmount('subtotal') + $subTotal);
+ $address->setBaseTotalAmount('subtotal', $address->getBaseTotalAmount('subtotal') + $baseSubTotal);
+ } else {
+ $address->setTotalAmount(
+ 'subtotal',
+ $address->getTotalAmount('subtotal') + $item->getRowTotal()
+ );
+ $address->setBaseTotalAmount(
+ 'subtotal',
+ $address->getBaseTotalAmount('subtotal') + $item->getBaseRowTotal()
+ );
+ }
$address->setSubtotalInclTax($address->getSubtotalInclTax() + $item->getRowTotalInclTax());
$address->setBaseSubtotalInclTax($address->getBaseSubtotalInclTax() + $item->getBaseRowTotalInclTax());
return $this;
diff --git a/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php b/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php
index c43e6476973a8..fc1080efb468a 100644
--- a/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php
+++ b/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php
@@ -27,6 +27,7 @@
use Magento\Sales\Model\Quote\Address;
use Magento\Sales\Model\Quote\Address\Total\AbstractTotal;
use Magento\Sales\Model\Quote\Item\AbstractItem;
+use Magento\Tax\Model\Calculation;
/**
* Tax totals calculation model
@@ -139,20 +140,24 @@ public function collect(Address $address)
);
if ($this->_config->priceIncludesTax($this->_store)) {
- $this->_areTaxRequestsSimilar = $this->_calculator->compareRequests(
- $this->_calculator->getRateOriginRequest($this->_store),
- $request
- );
+ if ($this->_taxData->isCrossBorderTradeEnabled($this->_store)) {
+ $this->_areTaxRequestsSimilar = true;
+ } else {
+ $this->_areTaxRequestsSimilar = $this->_calculator->compareRequests(
+ $this->_calculator->getRateOriginRequest($this->_store),
+ $request
+ );
+ }
}
switch ($this->_config->getAlgorithm($this->_store)) {
- case \Magento\Tax\Model\Calculation::CALC_UNIT_BASE:
+ case Calculation::CALC_UNIT_BASE:
$this->_unitBaseCalculation($address, $request);
break;
- case \Magento\Tax\Model\Calculation::CALC_ROW_BASE:
+ case Calculation::CALC_ROW_BASE:
$this->_rowBaseCalculation($address, $request);
break;
- case \Magento\Tax\Model\Calculation::CALC_TOTAL_BASE:
+ case Calculation::CALC_TOTAL_BASE:
$this->_totalBaseCalculation($address, $request);
break;
default:
@@ -183,33 +188,18 @@ protected function _processHiddenTaxes()
if (isset($taxInfoItem['item'])) {
// Item hidden taxes
$item = $taxInfoItem['item'];
- $rateKey = $taxInfoItem['rate_key'];
$hiddenTax = $taxInfoItem['value'];
$baseHiddenTax = $taxInfoItem['base_value'];
- $inclTax = $taxInfoItem['incl_tax'];
$qty = $taxInfoItem['qty'];
- if ($this->_config->getAlgorithm($this->_store) == \Magento\Tax\Model\Calculation::CALC_TOTAL_BASE) {
- $hiddenTax = $this->_deltaRound($hiddenTax, $rateKey, $inclTax);
- $baseHiddenTax = $this->_deltaRound($baseHiddenTax, $rateKey, $inclTax, 'base');
- } else {
- $hiddenTax = $this->_calculator->round($hiddenTax);
- $baseHiddenTax = $this->_calculator->round($baseHiddenTax);
- }
-
$item->setHiddenTaxAmount(max(0, $qty * $hiddenTax));
$item->setBaseHiddenTaxAmount(max(0, $qty * $baseHiddenTax));
$this->_getAddress()->addTotalAmount('hidden_tax', $item->getHiddenTaxAmount());
$this->_getAddress()->addBaseTotalAmount('hidden_tax', $item->getBaseHiddenTaxAmount());
} else {
// Shipping hidden taxes
- $rateKey = $taxInfoItem['rate_key'];
$hiddenTax = $taxInfoItem['value'];
$baseHiddenTax = $taxInfoItem['base_value'];
- $inclTax = $taxInfoItem['incl_tax'];
-
- $hiddenTax = $this->_deltaRound($hiddenTax, $rateKey, $inclTax);
- $baseHiddenTax = $this->_deltaRound($baseHiddenTax, $rateKey, $inclTax, 'base');
$this->_getAddress()->setShippingHiddenTaxAmount(max(0, $hiddenTax));
$this->_getAddress()->setBaseShippingHiddenTaxAmnt(max(0, $baseHiddenTax));
@@ -220,34 +210,43 @@ protected function _processHiddenTaxes()
}
/**
- * Tax caclulation for shipping price
+ * Calculate shipping tax for a single tax rate
*
- * @param Address $address
- * @param \Magento\Framework\Object $taxRateRequest
- * @return $this
+ * @param Address $address
+ * @param float $rate
+ * @param array $appliedRates
+ * @param string $taxId
+ * @return $this
*/
- protected function _calculateShippingTax(Address $address, $taxRateRequest)
- {
- $taxRateRequest->setProductClassId($this->_config->getShippingTaxClass($this->_store));
- $rate = $this->_calculator->getRate($taxRateRequest);
+ protected function _calculateShippingTaxByRate(
+ $address,
+ $rate,
+ $appliedRates,
+ $taxId = null
+ ) {
$inclTax = $address->getIsShippingInclTax();
$shipping = $address->getShippingTaxable();
$baseShipping = $address->getBaseShippingTaxable();
- $rateKey = (string)$rate;
+ $rateKey = ($taxId == null) ? (string)$rate : $taxId;
$hiddenTax = null;
$baseHiddenTax = null;
switch ($this->_taxData->getCalculationSequence($this->_store)) {
- case \Magento\Tax\Model\Calculation::CALC_TAX_BEFORE_DISCOUNT_ON_EXCL:
- case \Magento\Tax\Model\Calculation::CALC_TAX_BEFORE_DISCOUNT_ON_INCL:
+ case Calculation::CALC_TAX_BEFORE_DISCOUNT_ON_EXCL:
+ case Calculation::CALC_TAX_BEFORE_DISCOUNT_ON_INCL:
$tax = $this->_calculator->calcTaxAmount($shipping, $rate, $inclTax, false);
$baseTax = $this->_calculator->calcTaxAmount($baseShipping, $rate, $inclTax, false);
break;
- case \Magento\Tax\Model\Calculation::CALC_TAX_AFTER_DISCOUNT_ON_EXCL:
- case \Magento\Tax\Model\Calculation::CALC_TAX_AFTER_DISCOUNT_ON_INCL:
+ case Calculation::CALC_TAX_AFTER_DISCOUNT_ON_EXCL:
+ case Calculation::CALC_TAX_AFTER_DISCOUNT_ON_INCL:
$discountAmount = $address->getShippingDiscountAmount();
$baseDiscountAmount = $address->getBaseShippingDiscountAmount();
- $tax = $this->_calculator->calcTaxAmount($shipping - $discountAmount, $rate, $inclTax, false);
+ $tax = $this->_calculator->calcTaxAmount(
+ $shipping - $discountAmount,
+ $rate,
+ $inclTax,
+ false
+ );
$baseTax = $this->_calculator->calcTaxAmount(
$baseShipping - $baseDiscountAmount,
$rate,
@@ -257,32 +256,188 @@ protected function _calculateShippingTax(Address $address, $taxRateRequest)
break;
}
- if ($this->_config->getAlgorithm($this->_store) == \Magento\Tax\Model\Calculation::CALC_TOTAL_BASE) {
- $tax = $this->_deltaRound($tax, $rate, $inclTax);
- $baseTax = $this->_deltaRound($baseTax, $rate, $inclTax, 'base');
+ if ($this->_config->getAlgorithm($this->_store) == Calculation::CALC_TOTAL_BASE) {
+ $tax = $this->_deltaRound($tax, $rateKey, $inclTax);
+ $baseTax = $this->_deltaRound($baseTax, $rateKey, $inclTax, 'base');
+ $this->_addAmount(max(0, $tax));
+ $this->_addBaseAmount(max(0, $baseTax));
} else {
$tax = $this->_calculator->round($tax);
$baseTax = $this->_calculator->round($baseTax);
+ $this->_addAmount(max(0, $tax));
+ $this->_addBaseAmount(max(0, $baseTax));
}
if ($inclTax && !empty($discountAmount)) {
- $hiddenTax = $this->_calculator->calcTaxAmount($discountAmount, $rate, $inclTax, false);
- $baseHiddenTax = $this->_calculator->calcTaxAmount($baseDiscountAmount, $rate, $inclTax, false);
+ $taxBeforeDiscount = $this->_calculator->calcTaxAmount(
+ $shipping,
+ $rate,
+ $inclTax,
+ false
+ );
+ $baseTaxBeforeDiscount = $this->_calculator->calcTaxAmount(
+ $baseShipping,
+ $rate,
+ $inclTax,
+ false
+ );
+ if ($this->_config->getAlgorithm($this->_store) == Calculation::CALC_TOTAL_BASE) {
+ $taxBeforeDiscount = $this->_deltaRound(
+ $taxBeforeDiscount,
+ $rateKey,
+ $inclTax,
+ 'tax_before_discount'
+ );
+ $baseTaxBeforeDiscount = $this->_deltaRound(
+ $baseTaxBeforeDiscount,
+ $rateKey,
+ $inclTax,
+ 'tax_before_discount_base'
+ );
+ } else {
+ $taxBeforeDiscount = $this->_calculator->round($taxBeforeDiscount);
+ $baseTaxBeforeDiscount = $this->_calculator->round($baseTaxBeforeDiscount);
+ }
+ $hiddenTax = max(0, $taxBeforeDiscount - max(0, $tax));
+ $baseHiddenTax = max(0, $baseTaxBeforeDiscount - max(0, $baseTax));
$this->_hiddenTaxes[] = array(
'rate_key' => $rateKey,
'value' => $hiddenTax,
'base_value' => $baseHiddenTax,
- 'incl_tax' => $inclTax
+ 'incl_tax' => $inclTax,
);
}
- $this->_addAmount(max(0, $tax));
- $this->_addBaseAmount(max(0, $baseTax));
- $address->setShippingTaxAmount(max(0, $tax));
- $address->setBaseShippingTaxAmount(max(0, $baseTax));
- $applied = $this->_calculator->getAppliedRates($taxRateRequest);
- $this->_saveAppliedTaxes($address, $applied, $tax, $baseTax, $rate);
+ $address->setShippingTaxAmount($address->getShippingTaxAmount() + max(0, $tax));
+ $address->setBaseShippingTaxAmount($address->getBaseShippingTaxAmount() + max(0, $baseTax));
+ $this->_saveAppliedTaxes($address, $appliedRates, $tax, $baseTax, $rate);
+
+ return $this;
+ }
+
+ /**
+ * Tax calculation for shipping price
+ *
+ * @param Address $address
+ * @param \Magento\Framework\Object $taxRateRequest
+ * @return $this
+ */
+ protected function _calculateShippingTax(
+ Address $address,
+ \Magento\Framework\Object $taxRateRequest
+ ) {
+ $taxRateRequest->setProductClassId($this->_config->getShippingTaxClass($this->_store));
+ $rate = $this->_calculator->getRate($taxRateRequest);
+ $inclTax = $address->getIsShippingInclTax();
+ $address->setShippingTaxAmount(0);
+ $address->setBaseShippingTaxAmount(0);
+ $address->setShippingHiddenTaxAmount(0);
+ $address->setBaseShippingHiddenTaxAmount(0);
+ $appliedRates = $this->_calculator->getAppliedRates($taxRateRequest);
+ if ($inclTax) {
+ $this->_calculateShippingTaxByRate($address, $rate, $appliedRates);
+ } else {
+ foreach ($appliedRates as $appliedRate) {
+ $taxRate = $appliedRate['percent'];
+ $taxId = $appliedRate['id'];
+ $this->_calculateShippingTaxByRate($address, $taxRate, array($appliedRate), $taxId);
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Initialize tax related fields in item
+ *
+ * @param AbstractItem $item
+ * @param array $appliedRates
+ * @param float $rate
+ * @param bool $isUnitBasedCalculation
+ * @return bool
+ */
+ protected function initializeItemTax(
+ AbstractItem $item,
+ $appliedRates,
+ $rate,
+ $isUnitBasedCalculation = false
+ ) {
+ $item->setTaxAmount(0);
+ $item->setBaseTaxAmount(0);
+ $item->setHiddenTaxAmount(0);
+ $item->setBaseHiddenTaxAmount(0);
+ $item->setTaxPercent($rate);
+ $item->setDiscountTaxCompensation(0);
+ $rowTotalInclTax = $item->getRowTotalInclTax();
+ $recalculateRowTotalInclTax = false;
+ if (!isset($rowTotalInclTax)) {
+ if ($isUnitBasedCalculation) {
+ $qty = $item->getTotalQty();
+ $item->setRowTotalInclTax($this->_store->roundPrice($item->getTaxableAmount() * $qty));
+ $item->setBaseRowTotalInclTax($this->_store->roundPrice($item->getBaseTaxableAmount() * $qty));
+ } else {
+ $item->setRowTotalInclTax($item->getTaxableAmount());
+ $item->setBaseRowTotalInclTax($item->getBaseTaxableAmount());
+ }
+ $recalculateRowTotalInclTax = true;
+ }
+ $item->setTaxRates($appliedRates);
+
+ return $recalculateRowTotalInclTax;
+ }
+
+ /**
+ *
+ * @param Address $address
+ * @param AbstractItem $item
+ * @param \Magento\Framework\Object $taxRateRequest
+ * @param array $itemTaxGroups
+ * @param boolean $catalogPriceInclTax
+ * @return $this
+ */
+ protected function _unitBaseProcessItemTax(
+ Address $address,
+ AbstractItem $item,
+ \Magento\Framework\Object $taxRateRequest,
+ &$itemTaxGroups,
+ $catalogPriceInclTax
+ ) {
+ $taxRateRequest->setProductClassId($item->getProduct()->getTaxClassId());
+ $appliedRates = $this->_calculator->getAppliedRates($taxRateRequest);
+ $rate = $this->_calculator->getRate($taxRateRequest);
+
+ $recalculateRowTotalInclTax = $this->initializeItemTax($item, $appliedRates, $rate, true);
+
+ if ($catalogPriceInclTax) {
+ $this->_calcUnitTaxAmount($item, $rate);
+ $this->_saveAppliedTaxes(
+ $address,
+ $appliedRates,
+ $item->getTaxAmount(),
+ $item->getBaseTaxAmount(),
+ $rate
+ );
+ } else {
+ //need to calculate each tax separately
+ $taxGroups = array();
+ foreach ($appliedRates as $appliedTax) {
+ $taxId = $appliedTax['id'];
+ $taxRate = $appliedTax['percent'];
+ $this->_calcUnitTaxAmount($item, $taxRate, $taxGroups, $taxId, $recalculateRowTotalInclTax);
+ $this->_saveAppliedTaxes(
+ $address,
+ array($appliedTax),
+ $taxGroups[$taxId]['tax'],
+ $taxGroups[$taxId]['base_tax'],
+ $taxRate
+ );
+ }
+ }
+ if ($rate > 0) {
+ $itemTaxGroups[$item->getId()] = $appliedRates;
+ }
+ $this->_addAmount($item->getTaxAmount());
+ $this->_addBaseAmount($item->getBaseTaxAmount());
return $this;
}
@@ -293,10 +448,15 @@ protected function _calculateShippingTax(Address $address, $taxRateRequest)
* @param \Magento\Framework\Object $taxRateRequest
* @return $this
*/
- protected function _unitBaseCalculation(Address $address, $taxRateRequest)
- {
+ protected function _unitBaseCalculation(
+ Address $address,
+ \Magento\Framework\Object $taxRateRequest
+ ) {
$items = $this->_getAddressItems($address);
$itemTaxGroups = array();
+ $store = $address->getQuote()->getStore();
+ $catalogPriceInclTax = $this->_config->priceIncludesTax($store);
+
foreach ($items as $item) {
if ($item->getParentItem()) {
continue;
@@ -304,37 +464,23 @@ protected function _unitBaseCalculation(Address $address, $taxRateRequest)
if ($item->getHasChildren() && $item->isChildrenCalculated()) {
foreach ($item->getChildren() as $child) {
- $taxRateRequest->setProductClassId($child->getProduct()->getTaxClassId());
- $rate = $this->_calculator->getRate($taxRateRequest);
- $this->_calcUnitTaxAmount($child, $rate);
- $this->_addAmount($child->getTaxAmount());
- $this->_addBaseAmount($child->getBaseTaxAmount());
- $applied = $this->_calculator->getAppliedRates($taxRateRequest);
- if ($rate > 0) {
- $itemTaxGroups[$child->getId()] = $applied;
- }
- $this->_saveAppliedTaxes(
+ $this->_unitBaseProcessItemTax(
$address,
- $applied,
- $child->getTaxAmount(),
- $child->getBaseTaxAmount(),
- $rate
+ $child,
+ $taxRateRequest,
+ $itemTaxGroups,
+ $catalogPriceInclTax
);
- $child->setTaxRates($applied);
}
$this->_recalculateParent($item);
} else {
- $taxRateRequest->setProductClassId($item->getProduct()->getTaxClassId());
- $rate = $this->_calculator->getRate($taxRateRequest);
- $this->_calcUnitTaxAmount($item, $rate);
- $this->_addAmount($item->getTaxAmount());
- $this->_addBaseAmount($item->getBaseTaxAmount());
- $applied = $this->_calculator->getAppliedRates($taxRateRequest);
- if ($rate > 0) {
- $itemTaxGroups[$item->getId()] = $applied;
- }
- $this->_saveAppliedTaxes($address, $applied, $item->getTaxAmount(), $item->getBaseTaxAmount(), $rate);
- $item->setTaxRates($applied);
+ $this->_unitBaseProcessItemTax(
+ $address,
+ $item,
+ $taxRateRequest,
+ $itemTaxGroups,
+ $catalogPriceInclTax
+ );
}
}
if ($address->getQuote()->getTaxesForItems()) {
@@ -345,51 +491,60 @@ protected function _unitBaseCalculation(Address $address, $taxRateRequest)
}
/**
- * Calculate unit tax anount based on unit price
+ * Calculate unit tax amount based on unit price
*
* @param AbstractItem $item
* @param float $rate
+ * @param array $taxGroups
+ * @param string $taxId
+ * @param bool $recalculateRowTotalInclTax
* @return $this
*/
- protected function _calcUnitTaxAmount(AbstractItem $item, $rate)
- {
+ protected function _calcUnitTaxAmount(
+ AbstractItem $item,
+ $rate,
+ &$taxGroups = null,
+ $taxId = null,
+ $recalculateRowTotalInclTax = false
+ ) {
$qty = $item->getTotalQty();
$inclTax = $item->getIsPriceInclTax();
$price = $item->getTaxableAmount() + $item->getExtraTaxableAmount();
$basePrice = $item->getBaseTaxableAmount() + $item->getBaseExtraTaxableAmount();
- $rateKey = (string)$rate;
- $item->setTaxPercent($rate);
+ $rateKey = ($taxId == null) ? (string)$rate : $taxId;
- $hiddenTax = null;
- $baseHiddenTax = null;
+ $unitTaxBeforeDiscount = 0;
+ $baseUnitTaxBeforeDiscount = 0;
switch ($this->_config->getCalculationSequence($this->_store)) {
- case \Magento\Tax\Model\Calculation::CALC_TAX_BEFORE_DISCOUNT_ON_EXCL:
- case \Magento\Tax\Model\Calculation::CALC_TAX_BEFORE_DISCOUNT_ON_INCL:
- $unitTax = $this->_calculator->calcTaxAmount($price, $rate, $inclTax);
- $baseUnitTax = $this->_calculator->calcTaxAmount($basePrice, $rate, $inclTax);
+ case Calculation::CALC_TAX_BEFORE_DISCOUNT_ON_EXCL:
+ case Calculation::CALC_TAX_BEFORE_DISCOUNT_ON_INCL:
+ $unitTaxBeforeDiscount = $this->_calculator->calcTaxAmount($price, $rate, $inclTax, false);
+ $baseUnitTaxBeforeDiscount = $this->_calculator->calcTaxAmount($basePrice, $rate, $inclTax, false);
+ $unitTaxBeforeDiscount = $unitTax = $this->_calculator->round($unitTaxBeforeDiscount);
+ $baseUnitTaxBeforeDiscount = $baseUnitTax = $this->_calculator->round($baseUnitTaxBeforeDiscount);
break;
- case \Magento\Tax\Model\Calculation::CALC_TAX_AFTER_DISCOUNT_ON_EXCL:
- case \Magento\Tax\Model\Calculation::CALC_TAX_AFTER_DISCOUNT_ON_INCL:
+ case Calculation::CALC_TAX_AFTER_DISCOUNT_ON_EXCL:
+ case Calculation::CALC_TAX_AFTER_DISCOUNT_ON_INCL:
$discountAmount = $item->getDiscountAmount() / $qty;
$baseDiscountAmount = $item->getBaseDiscountAmount() / $qty;
- $unitTax = $this->_calculator->calcTaxAmount($price, $rate, $inclTax);
- $discountRate = $unitTax / $price * 100;
- $unitTaxDiscount = $this->_calculator->calcTaxAmount($discountAmount, $discountRate, $inclTax, false);
- $unitTax = max($unitTax - $unitTaxDiscount, 0);
- $baseUnitTax = $this->_calculator->calcTaxAmount($basePrice, $rate, $inclTax);
- $baseDiscountRate = $baseUnitTax / $basePrice * 100;
- $baseUnitTaxDiscount = $this->_calculator->calcTaxAmount(
- $baseDiscountAmount,
- $baseDiscountRate,
- $inclTax,
- false
- );
- $baseUnitTax = max($baseUnitTax - $baseUnitTaxDiscount, 0);
+ $unitTaxBeforeDiscount = $this->_calculator->calcTaxAmount($price, $rate, $inclTax, false);
+ $unitTaxDiscount = $this->_calculator->calcTaxAmount($discountAmount, $rate, $inclTax, false);
+ $unitTax = $this->_calculator->round(max($unitTaxBeforeDiscount - $unitTaxDiscount, 0));
+
+ $baseUnitTaxBeforeDiscount = $this->_calculator->calcTaxAmount($basePrice, $rate, $inclTax, false);
+ $baseUnitTaxDiscount = $this->_calculator->calcTaxAmount($baseDiscountAmount, $rate, $inclTax, false);
+ $baseUnitTax = $this->_calculator->round(max($baseUnitTaxBeforeDiscount - $baseUnitTaxDiscount, 0));
+
+ $unitTax = $this->_calculator->round($unitTax);
+ $baseUnitTax = $this->_calculator->round($baseUnitTax);
+
+ $unitTaxBeforeDiscount = max(0, $this->_calculator->round($unitTaxBeforeDiscount));
+ $baseUnitTaxBeforeDiscount = max(0, $this->_calculator->round($baseUnitTaxBeforeDiscount));
if ($inclTax && $discountAmount > 0) {
- $hiddenTax = $this->_calculator->calcTaxAmount($discountAmount, $rate, $inclTax, false);
- $baseHiddenTax = $this->_calculator->calcTaxAmount($baseDiscountAmount, $rate, $inclTax, false);
+ $hiddenTax = $unitTaxBeforeDiscount - $unitTax;
+ $baseHiddenTax = $baseUnitTaxBeforeDiscount - $baseUnitTax;
$this->_hiddenTaxes[] = array(
'rate_key' => $rateKey,
'qty' => $qty,
@@ -413,12 +568,79 @@ protected function _calcUnitTaxAmount(AbstractItem $item, $rate)
}
break;
}
- $item->setTaxAmount($this->_store->roundPrice(max(0, $qty * $unitTax)));
- $item->setBaseTaxAmount($this->_store->roundPrice(max(0, $qty * $baseUnitTax)));
+ $rowTax = $this->_store->roundPrice(max(0, $qty * $unitTax));
+ $baseRowTax = $this->_store->roundPrice(max(0, $qty * $baseUnitTax));
+ $item->setTaxAmount($item->getTaxAmount() + $rowTax);
+ $item->setBaseTaxAmount($item->getBaseTaxAmount() + $baseRowTax);
+ if (is_array($taxGroups)) {
+ $taxGroups[$rateKey]['tax'] = max(0, $rowTax);
+ $taxGroups[$rateKey]['base_tax'] = max(0, $baseRowTax);
+ }
+
+ $rowTotalInclTax = $item->getRowTotalInclTax();
+ if (!isset($rowTotalInclTax) || $recalculateRowTotalInclTax) {
+ if ($this->_config->priceIncludesTax($this->_store)) {
+ $item->setRowTotalInclTax($price * $qty);
+ $item->setBaseRowTotalInclTax($basePrice * $qty);
+ } else {
+ $item->setRowTotalInclTax($item->getRowTotalInclTax() + $unitTaxBeforeDiscount * $qty);
+ $item->setBaseRowTotalInclTax($item->getBaseRowTotalInclTax() + $baseUnitTaxBeforeDiscount * $qty);
+ }
+ }
return $this;
}
+ /**
+ *
+ * @param Address $address
+ * @param AbstractItem $item
+ * @param \Magento\Framework\Object $taxRateRequest
+ * @param array $itemTaxGroups
+ * @param bool $catalogPriceInclTax
+ * @return $this
+ */
+ protected function _rowBaseProcessItemTax(
+ Address $address,
+ AbstractItem $item,
+ \Magento\Framework\Object $taxRateRequest,
+ &$itemTaxGroups,
+ $catalogPriceInclTax
+ ) {
+ $taxRateRequest->setProductClassId($item->getProduct()->getTaxClassId());
+ $appliedRates = $this->_calculator->getAppliedRates($taxRateRequest);
+ $rate = $this->_calculator->getRate($taxRateRequest);
+
+ $recalculateRowTotalInclTax = $this->initializeItemTax($item, $appliedRates, $rate);
+
+ if ($catalogPriceInclTax) {
+ $this->_calcRowTaxAmount($item, $rate);
+ $this->_saveAppliedTaxes($address, $appliedRates, $item->getTaxAmount(), $item->getBaseTaxAmount(), $rate);
+ } else {
+ //need to calculate each tax separately
+ $taxGroups = array();
+ foreach ($appliedRates as $appliedTax) {
+ $taxId = $appliedTax['id'];
+ $taxRate = $appliedTax['percent'];
+ $this->_calcRowTaxAmount($item, $taxRate, $taxGroups, $taxId, $recalculateRowTotalInclTax);
+ $this->_saveAppliedTaxes(
+ $address,
+ array($appliedTax),
+ $taxGroups[$taxId]['tax'],
+ $taxGroups[$taxId]['base_tax'],
+ $taxRate
+ );
+ }
+
+ }
+ if ($rate > 0) {
+ $itemTaxGroups[$item->getId()] = $appliedRates;
+ }
+ $this->_addAmount($item->getTaxAmount());
+ $this->_addBaseAmount($item->getBaseTaxAmount());
+ return $this;
+ }
+
/**
* Calculate address total tax based on row total
*
@@ -426,47 +648,38 @@ protected function _calcUnitTaxAmount(AbstractItem $item, $rate)
* @param \Magento\Framework\Object $taxRateRequest
* @return $this
*/
- protected function _rowBaseCalculation(Address $address, $taxRateRequest)
- {
+ protected function _rowBaseCalculation(
+ Address $address,
+ \Magento\Framework\Object $taxRateRequest
+ ) {
$items = $this->_getAddressItems($address);
$itemTaxGroups = array();
+ $store = $address->getQuote()->getStore();
+ $catalogPriceInclTax = $this->_config->priceIncludesTax($store);
+
foreach ($items as $item) {
if ($item->getParentItem()) {
continue;
}
if ($item->getHasChildren() && $item->isChildrenCalculated()) {
foreach ($item->getChildren() as $child) {
- $taxRateRequest->setProductClassId($child->getProduct()->getTaxClassId());
- $rate = $this->_calculator->getRate($taxRateRequest);
- $this->_calcRowTaxAmount($child, $rate);
- $this->_addAmount($child->getTaxAmount());
- $this->_addBaseAmount($child->getBaseTaxAmount());
- $applied = $this->_calculator->getAppliedRates($taxRateRequest);
- if ($rate > 0) {
- $itemTaxGroups[$child->getId()] = $applied;
- }
- $this->_saveAppliedTaxes(
+ $this->_rowBaseProcessItemTax(
$address,
- $applied,
- $child->getTaxAmount(),
- $child->getBaseTaxAmount(),
- $rate
+ $child,
+ $taxRateRequest,
+ $itemTaxGroups,
+ $catalogPriceInclTax
);
- $child->setTaxRates($applied);
}
$this->_recalculateParent($item);
} else {
- $taxRateRequest->setProductClassId($item->getProduct()->getTaxClassId());
- $rate = $this->_calculator->getRate($taxRateRequest);
- $this->_calcRowTaxAmount($item, $rate);
- $this->_addAmount($item->getTaxAmount());
- $this->_addBaseAmount($item->getBaseTaxAmount());
- $applied = $this->_calculator->getAppliedRates($taxRateRequest);
- if ($rate > 0) {
- $itemTaxGroups[$item->getId()] = $applied;
- }
- $this->_saveAppliedTaxes($address, $applied, $item->getTaxAmount(), $item->getBaseTaxAmount(), $rate);
- $item->setTaxRates($applied);
+ $this->_rowBaseProcessItemTax(
+ $address,
+ $item,
+ $taxRateRequest,
+ $itemTaxGroups,
+ $catalogPriceInclTax
+ );
}
}
@@ -482,26 +695,39 @@ protected function _rowBaseCalculation(Address $address, $taxRateRequest)
*
* @param AbstractItem $item
* @param float $rate
+ * @param array $taxGroups
+ * @param string $taxId
+ * @param bool $recalculateRowTotalInclTax
* @return $this
*/
- protected function _calcRowTaxAmount($item, $rate)
- {
+ protected function _calcRowTaxAmount(
+ AbstractItem $item,
+ $rate,
+ &$taxGroups = null,
+ $taxId = null,
+ $recalculateRowTotalInclTax = false
+ ) {
$inclTax = $item->getIsPriceInclTax();
- $subtotal = $item->getTaxableAmount() + $item->getExtraRowTaxableAmount();
- $baseSubtotal = $item->getBaseTaxableAmount() + $item->getBaseExtraRowTaxableAmount();
- $rateKey = (string)$rate;
- $item->setTaxPercent($rate);
+ $subtotal = $taxSubtotal = $item->getTaxableAmount() + $item->getExtraRowTaxableAmount();
+ $baseSubtotal = $baseTaxSubtotal = $item->getBaseTaxableAmount() + $item->getBaseExtraRowTaxableAmount();
+ $rateKey = ($taxId == null) ? (string)$rate : $taxId;
+
+ $hiddenTax = 0;
+ $baseHiddenTax = 0;
+ $rowTaxBeforeDiscount = 0;
+ $baseRowTaxBeforeDiscount = 0;
- $hiddenTax = null;
- $baseHiddenTax = null;
switch ($this->_taxData->getCalculationSequence($this->_store)) {
- case \Magento\Tax\Model\Calculation::CALC_TAX_BEFORE_DISCOUNT_ON_EXCL:
- case \Magento\Tax\Model\Calculation::CALC_TAX_BEFORE_DISCOUNT_ON_INCL:
- $rowTax = $this->_calculator->calcTaxAmount($subtotal, $rate, $inclTax);
- $baseRowTax = $this->_calculator->calcTaxAmount($baseSubtotal, $rate, $inclTax);
+ case Calculation::CALC_TAX_BEFORE_DISCOUNT_ON_EXCL:
+ case Calculation::CALC_TAX_BEFORE_DISCOUNT_ON_INCL:
+ $rowTaxBeforeDiscount = $this->_calculator->calcTaxAmount($subtotal, $rate, $inclTax, false);
+ $baseRowTaxBeforeDiscount = $this->_calculator->calcTaxAmount($baseSubtotal, $rate, $inclTax, false);
+
+ $rowTaxBeforeDiscount = $rowTax = $this->_calculator->round($rowTaxBeforeDiscount);
+ $baseRowTaxBeforeDiscount = $baseRowTax = $this->_calculator->round($baseRowTaxBeforeDiscount);
break;
- case \Magento\Tax\Model\Calculation::CALC_TAX_AFTER_DISCOUNT_ON_EXCL:
- case \Magento\Tax\Model\Calculation::CALC_TAX_AFTER_DISCOUNT_ON_INCL:
+ case Calculation::CALC_TAX_AFTER_DISCOUNT_ON_EXCL:
+ case Calculation::CALC_TAX_AFTER_DISCOUNT_ON_INCL:
$discountAmount = $item->getDiscountAmount();
$baseDiscountAmount = $item->getBaseDiscountAmount();
$rowTax = $this->_calculator->calcTaxAmount(max($subtotal - $discountAmount, 0), $rate, $inclTax);
@@ -510,9 +736,29 @@ protected function _calcRowTaxAmount($item, $rate)
$rate,
$inclTax
);
+ $rowTax = $this->_calculator->round($rowTax);
+ $baseRowTax = $this->_calculator->round($baseRowTax);
+
+ //Calculate the Row Tax before discount
+ $rowTaxBeforeDiscount = $this->_calculator->calcTaxAmount(
+ $subtotal,
+ $rate,
+ $inclTax,
+ false
+ );
+ $baseRowTaxBeforeDiscount = $this->_calculator->calcTaxAmount(
+ $baseSubtotal,
+ $rate,
+ $inclTax,
+ false
+ );
+
+ $rowTaxBeforeDiscount = max(0, $this->_calculator->round($rowTaxBeforeDiscount));
+ $baseRowTaxBeforeDiscount = max(0, $this->_calculator->round($baseRowTaxBeforeDiscount));
+
if ($inclTax && $discountAmount > 0) {
- $hiddenTax = $this->_calculator->calcTaxAmount($discountAmount, $rate, $inclTax, false);
- $baseHiddenTax = $this->_calculator->calcTaxAmount($baseDiscountAmount, $rate, $inclTax, false);
+ $hiddenTax = $rowTaxBeforeDiscount - $rowTax;
+ $baseHiddenTax = $baseRowTaxBeforeDiscount - $baseRowTax;
$this->_hiddenTaxes[] = array(
'rate_key' => $rateKey,
'qty' => 1,
@@ -536,8 +782,23 @@ protected function _calcRowTaxAmount($item, $rate)
}
break;
}
- $item->setTaxAmount(max(0, $rowTax));
- $item->setBaseTaxAmount(max(0, $baseRowTax));
+ $item->setTaxAmount($item->getTaxAmount() + max(0, $rowTax));
+ $item->setBaseTaxAmount($item->getBaseTaxAmount() + max(0, $baseRowTax));
+ if (is_array($taxGroups)) {
+ $taxGroups[$rateKey]['tax'] = max(0, $rowTax);
+ $taxGroups[$rateKey]['base_tax'] = max(0, $baseRowTax);
+ }
+
+ $rowTotalInclTax = $item->getRowTotalInclTax();
+ if (!isset($rowTotalInclTax) || $recalculateRowTotalInclTax) {
+ if ($this->_config->priceIncludesTax($this->_store)) {
+ $item->setRowTotalInclTax($subtotal);
+ $item->setBaseRowTotalInclTax($baseSubtotal);
+ } else {
+ $item->setRowTotalInclTax($item->getRowTotalInclTax() + $rowTaxBeforeDiscount);
+ $item->setBaseRowTotalInclTax($item->getBaseRowTotalInclTax() + $baseRowTaxBeforeDiscount);
+ }
+ }
return $this;
}
@@ -548,12 +809,15 @@ protected function _calcRowTaxAmount($item, $rate)
* @param \Magento\Framework\Object $taxRateRequest
* @return $this
*/
- protected function _totalBaseCalculation(Address $address, $taxRateRequest)
- {
+ protected function _totalBaseCalculation(
+ Address $address,
+ \Magento\Framework\Object $taxRateRequest
+ ) {
$items = $this->_getAddressItems($address);
$store = $address->getQuote()->getStore();
$taxGroups = array();
$itemTaxGroups = array();
+ $catalogPriceInclTax = $this->_config->priceIncludesTax($store);
foreach ($items as $item) {
if ($item->getParentItem()) {
@@ -562,27 +826,23 @@ protected function _totalBaseCalculation(Address $address, $taxRateRequest)
if ($item->getHasChildren() && $item->isChildrenCalculated()) {
foreach ($item->getChildren() as $child) {
- $taxRateRequest->setProductClassId($child->getProduct()->getTaxClassId());
- $rate = $this->_calculator->getRate($taxRateRequest);
- $applied_rates = $this->_calculator->getAppliedRates($taxRateRequest);
- $taxGroups[(string)$rate]['applied_rates'] = $applied_rates;
- $taxGroups[(string)$rate]['incl_tax'] = $child->getIsPriceInclTax();
- $this->_aggregateTaxPerRate($child, $rate, $taxGroups);
- if ($rate > 0) {
- $itemTaxGroups[$child->getId()] = $applied_rates;
- }
+ $this->_totalBaseProcessItemTax(
+ $child,
+ $taxRateRequest,
+ $taxGroups,
+ $itemTaxGroups,
+ $catalogPriceInclTax
+ );
}
$this->_recalculateParent($item);
} else {
- $taxRateRequest->setProductClassId($item->getProduct()->getTaxClassId());
- $rate = $this->_calculator->getRate($taxRateRequest);
- $applied_rates = $this->_calculator->getAppliedRates($taxRateRequest);
- $taxGroups[(string)$rate]['applied_rates'] = $applied_rates;
- $taxGroups[(string)$rate]['incl_tax'] = $item->getIsPriceInclTax();
- $this->_aggregateTaxPerRate($item, $rate, $taxGroups);
- if ($rate > 0) {
- $itemTaxGroups[$item->getId()] = $applied_rates;
- }
+ $this->_totalBaseProcessItemTax(
+ $item,
+ $taxRateRequest,
+ $taxGroups,
+ $itemTaxGroups,
+ $catalogPriceInclTax
+ );
}
}
@@ -591,14 +851,63 @@ protected function _totalBaseCalculation(Address $address, $taxRateRequest)
}
$address->getQuote()->setTaxesForItems($itemTaxGroups);
- foreach ($taxGroups as $rateKey => $data) {
- $rate = (double)$rateKey;
- $inclTax = $data['incl_tax'];
- $totalTax = $this->_calculator->calcTaxAmount(array_sum($data['totals']), $rate, $inclTax);
- $baseTotalTax = $this->_calculator->calcTaxAmount(array_sum($data['base_totals']), $rate, $inclTax);
+ foreach ($taxGroups as $taxId => $data) {
+ if ($catalogPriceInclTax) {
+ $rate = (float)$taxId;
+ } else {
+ $rate = $data['applied_rates'][0]['percent'];
+ }
+
+ $totalTax = array_sum($data['tax']);
+ $baseTotalTax = array_sum($data['base_tax']);
$this->_addAmount($totalTax);
$this->_addBaseAmount($baseTotalTax);
- $this->_saveAppliedTaxes($address, $data['applied_rates'], $totalTax, $baseTotalTax, $rate);
+ $totalTaxRounded = $this->_calculator->round($totalTax);
+ $baseTotalTaxRounded = $this->_calculator->round($totalTaxRounded);
+ $this->_saveAppliedTaxes($address, $data['applied_rates'], $totalTaxRounded, $baseTotalTaxRounded, $rate);
+ }
+
+ return $this;
+ }
+
+ /**
+ *
+ * @param AbstractItem $item
+ * @param \Magento\Framework\Object $taxRateRequest
+ * @param array $taxGroups
+ * @param array $itemTaxGroups
+ * @param bool $catalogPriceInclTax
+ * @return $this
+ */
+ protected function _totalBaseProcessItemTax(
+ AbstractItem $item,
+ \Magento\Framework\Object $taxRateRequest,
+ &$taxGroups,
+ &$itemTaxGroups,
+ $catalogPriceInclTax
+ ) {
+ $taxRateRequest->setProductClassId($item->getProduct()->getTaxClassId());
+ $appliedRates = $this->_calculator->getAppliedRates($taxRateRequest);
+ $rate = $this->_calculator->getRate($taxRateRequest);
+
+ $recalculateRowTotalInclTax = $this->initializeItemTax($item, $appliedRates, $rate);
+
+ if ($catalogPriceInclTax) {
+ $taxGroups[(string)$rate]['applied_rates'] = $appliedRates;
+ $taxGroups[(string)$rate]['incl_tax'] = $item->getIsPriceInclTax();
+ $this->_aggregateTaxPerRate($item, $rate, $taxGroups);
+ } else {
+ //need to calculate each tax separately
+ foreach ($appliedRates as $appliedTax) {
+ $taxId = $appliedTax['id'];
+ $taxRate = $appliedTax['percent'];
+ $taxGroups[$taxId]['applied_rates'] = array($appliedTax);
+ $taxGroups[$taxId]['incl_tax'] = $item->getIsPriceInclTax();
+ $this->_aggregateTaxPerRate($item, $taxRate, $taxGroups, $taxId, $recalculateRowTotalInclTax);
+ }
+ }
+ if ($rate > 0) {
+ $itemTaxGroups[$item->getId()] = $appliedRates;
}
return $this;
}
@@ -609,12 +918,19 @@ protected function _totalBaseCalculation(Address $address, $taxRateRequest)
* @param AbstractItem $item
* @param float $rate
* @param array &$taxGroups
+ * @param string $taxId
+ * @param bool $recalculateRowTotalInclTax
* @return $this
*/
- protected function _aggregateTaxPerRate($item, $rate, &$taxGroups)
- {
+ protected function _aggregateTaxPerRate(
+ AbstractItem $item,
+ $rate,
+ &$taxGroups,
+ $taxId = null,
+ $recalculateRowTotalInclTax = false
+ ) {
$inclTax = $item->getIsPriceInclTax();
- $rateKey = (string)$rate;
+ $rateKey = ($taxId == null) ? (string)$rate : $taxId;
$taxSubtotal = $subtotal = $item->getTaxableAmount() + $item->getExtraRowTaxableAmount();
$baseTaxSubtotal = $baseSubtotal = $item->getBaseTaxableAmount() + $item->getBaseExtraRowTaxableAmount();
$item->setTaxPercent($rate);
@@ -626,14 +942,27 @@ protected function _aggregateTaxPerRate($item, $rate, &$taxGroups)
$hiddenTax = null;
$baseHiddenTax = null;
+ $discount = 0;
+ $rowTaxBeforeDiscount = 0;
+ $baseRowTaxBeforeDiscount = 0;
switch ($this->_taxData->getCalculationSequence($this->_store)) {
- case \Magento\Tax\Model\Calculation::CALC_TAX_BEFORE_DISCOUNT_ON_EXCL:
- case \Magento\Tax\Model\Calculation::CALC_TAX_BEFORE_DISCOUNT_ON_INCL:
- $rowTax = $this->_calculator->calcTaxAmount($subtotal, $rate, $inclTax, false);
- $baseRowTax = $this->_calculator->calcTaxAmount($baseSubtotal, $rate, $inclTax, false);
+ case Calculation::CALC_TAX_BEFORE_DISCOUNT_ON_EXCL:
+ case Calculation::CALC_TAX_BEFORE_DISCOUNT_ON_INCL:
+ $rowTaxBeforeDiscount = $this->_calculator->calcTaxAmount($subtotal, $rate, $inclTax, false);
+ $baseRowTaxBeforeDiscount = $this->_calculator->calcTaxAmount($baseSubtotal, $rate, $inclTax, false);
+
+ $taxBeforeDiscountRounded = $rowTax = $this->_deltaRound($rowTaxBeforeDiscount, $rateKey, $inclTax);
+ $baseTaxBeforeDiscountRounded = $baseRowTax = $this->_deltaRound(
+ $baseRowTaxBeforeDiscount,
+ $rateKey,
+ $inclTax,
+ 'base'
+ );
+ $item->setTaxAmount($item->getTaxAmount() + max(0, $rowTax));
+ $item->setBaseTaxAmount($item->getBaseTaxAmount() + max(0, $baseRowTax));
break;
- case \Magento\Tax\Model\Calculation::CALC_TAX_AFTER_DISCOUNT_ON_EXCL:
- case \Magento\Tax\Model\Calculation::CALC_TAX_AFTER_DISCOUNT_ON_INCL:
+ case Calculation::CALC_TAX_AFTER_DISCOUNT_ON_EXCL:
+ case Calculation::CALC_TAX_AFTER_DISCOUNT_ON_INCL:
if ($this->_taxData->applyTaxOnOriginalPrice($this->_store)) {
$discount = $item->getOriginalDiscountAmount();
$baseDiscount = $item->getBaseOriginalDiscountAmount();
@@ -644,21 +973,43 @@ protected function _aggregateTaxPerRate($item, $rate, &$taxGroups)
$taxSubtotal = max($subtotal - $discount, 0);
$baseTaxSubtotal = max($baseSubtotal - $baseDiscount, 0);
+
$rowTax = $this->_calculator->calcTaxAmount($taxSubtotal, $rate, $inclTax, false);
$baseRowTax = $this->_calculator->calcTaxAmount($baseTaxSubtotal, $rate, $inclTax, false);
- if (!$item->getNoDiscount() && $item->getWeeeTaxApplied()) {
- $rowTaxBeforeDiscount = $this->_calculator->calcTaxAmount($subtotal, $rate, $inclTax, false);
- $baseRowTaxBeforeDiscount = $this->_calculator->calcTaxAmount(
- $baseSubtotal,
- $rate,
- $inclTax,
- false
- );
- }
+
+ $rowTax = $this->_deltaRound($rowTax, $rateKey, $inclTax);
+ $baseRowTax = $this->_deltaRound($baseRowTax, $rateKey, $inclTax, 'base');
+
+ $item->setTaxAmount($item->getTaxAmount() + max(0, $rowTax));
+ $item->setBaseTaxAmount($item->getBaseTaxAmount() + max(0, $baseRowTax));
+
+ //Calculate the Row taxes before discount
+ $rowTaxBeforeDiscount = $this->_calculator->calcTaxAmount(
+ $subtotal,
+ $rate,
+ $inclTax,
+ false
+ );
+ $baseRowTaxBeforeDiscount = $this->_calculator->calcTaxAmount(
+ $baseSubtotal,
+ $rate,
+ $inclTax,
+ false
+ );
+
+ $taxBeforeDiscountRounded = max(
+ 0,
+ $this->_deltaRound($rowTaxBeforeDiscount, $rateKey, $inclTax, 'tax_before_discount')
+ );
+ $baseTaxBeforeDiscountRounded = max(
+ 0,
+ $this->_deltaRound($baseRowTaxBeforeDiscount, $rateKey, $inclTax, 'tax_before_discount_base')
+ );
+
if ($inclTax && $discount > 0) {
- $hiddenTax = $this->_calculator->calcTaxAmount($discount, $rate, $inclTax, false);
- $baseHiddenTax = $this->_calculator->calcTaxAmount($baseDiscount, $rate, $inclTax, false);
+ $hiddenTax = $taxBeforeDiscountRounded - max(0, $rowTax);
+ $baseHiddenTax = $baseTaxBeforeDiscountRounded - max(0, $baseRowTax);
$this->_hiddenTaxes[] = array(
'rate_key' => $rateKey,
'qty' => 1,
@@ -671,21 +1022,22 @@ protected function _aggregateTaxPerRate($item, $rate, &$taxGroups)
break;
}
- $rowTax = $this->_deltaRound($rowTax, $rateKey, $inclTax);
- $baseRowTax = $this->_deltaRound($baseRowTax, $rateKey, $inclTax, 'base');
- $item->setTaxAmount(max(0, $rowTax));
- $item->setBaseTaxAmount(max(0, $baseRowTax));
-
- if (isset($rowTaxBeforeDiscount) && isset($baseRowTaxBeforeDiscount)) {
- $taxBeforeDiscount = max(0, $this->_deltaRound($rowTaxBeforeDiscount, $rateKey, $inclTax));
- $baseTaxBeforeDiscount = max(0, $this->_deltaRound($baseRowTaxBeforeDiscount, $rateKey, $inclTax, 'base'));
-
- $item->setDiscountTaxCompensation($taxBeforeDiscount - max(0, $rowTax));
- $item->setBaseDiscountTaxCompensation($baseTaxBeforeDiscount - max(0, $baseRowTax));
+ $rowTotalInclTax = $item->getRowTotalInclTax();
+ if (!isset($rowTotalInclTax) || $recalculateRowTotalInclTax) {
+ if ($this->_config->priceIncludesTax($this->_store)) {
+ $item->setRowTotalInclTax($subtotal);
+ $item->setBaseRowTotalInclTax($baseSubtotal);
+ } else {
+ $item->setRowTotalInclTax($item->getRowTotalInclTax() + $taxBeforeDiscountRounded);
+ $item->setBaseRowTotalInclTax($item->getBaseRowTotalInclTax() + $baseTaxBeforeDiscountRounded);
+ }
}
$taxGroups[$rateKey]['totals'][] = max(0, $taxSubtotal);
$taxGroups[$rateKey]['base_totals'][] = max(0, $baseTaxSubtotal);
+ $taxGroups[$rateKey]['tax'][] = max(0, $rowTax);
+ $taxGroups[$rateKey]['base_tax'][] = max(0, $baseRowTax);
+
return $this;
}
@@ -703,7 +1055,8 @@ protected function _deltaRound($price, $rate, $direction, $type = 'regular')
if ($price) {
$rate = (string)$rate;
$type = $type . $direction;
- $delta = isset($this->_roundingDeltas[$type][$rate]) ? $this->_roundingDeltas[$type][$rate] : 0;
+ // initialize the delta to a small number to avoid non-deterministic behavior with rounding of 0.5
+ $delta = isset($this->_roundingDeltas[$type][$rate]) ? $this->_roundingDeltas[$type][$rate] : 0.000001;
$price += $delta;
$this->_roundingDeltas[$type][$rate] = $price - $this->_calculator->round($price);
$price = $this->_calculator->round($price);
@@ -740,8 +1093,13 @@ protected function _recalculateParent(AbstractItem $item)
* @param float $rate
* @return void
*/
- protected function _saveAppliedTaxes(Address $address, $applied, $amount, $baseAmount, $rate)
- {
+ protected function _saveAppliedTaxes(
+ Address $address,
+ $applied,
+ $amount,
+ $baseAmount,
+ $rate
+ ) {
$previouslyAppliedTaxes = $address->getAppliedTaxes();
$process = count($previouslyAppliedTaxes);
@@ -771,7 +1129,6 @@ protected function _saveAppliedTaxes(Address $address, $applied, $amount, $baseA
}
}
-
if ($appliedAmount || $previouslyAppliedTaxes[$row['id']]['amount']) {
$previouslyAppliedTaxes[$row['id']]['amount'] += $appliedAmount;
$previouslyAppliedTaxes[$row['id']]['base_amount'] += $baseAppliedAmount;
@@ -855,7 +1212,7 @@ public function processConfigArray($config, $store)
{
$calculationSequence = $this->_taxData->getCalculationSequence($store);
switch ($calculationSequence) {
- case \Magento\Tax\Model\Calculation::CALC_TAX_BEFORE_DISCOUNT_ON_INCL:
+ case Calculation::CALC_TAX_BEFORE_DISCOUNT_ON_INCL:
$config['before'][] = 'discount';
break;
default:
diff --git a/app/code/Magento/Tax/Model/System/Config/Source/Tax/Region.php b/app/code/Magento/Tax/Model/System/Config/Source/Tax/Region.php
index bd1eef6366858..721588daf9a9d 100644
--- a/app/code/Magento/Tax/Model/System/Config/Source/Tax/Region.php
+++ b/app/code/Magento/Tax/Model/System/Config/Source/Tax/Region.php
@@ -25,11 +25,6 @@
class Region implements \Magento\Framework\Option\ArrayInterface
{
- /**
- * @var array
- */
- protected $_options;
-
/**
* @var \Magento\Directory\Model\Resource\Region\CollectionFactory
*/
@@ -44,6 +39,8 @@ public function __construct(\Magento\Directory\Model\Resource\Region\CollectionF
}
/**
+ * Return list of country's regions as array
+ *
* @param bool $noEmpty
* @param string|array|null $country
* @return array
@@ -58,9 +55,9 @@ public function toOptionArray($noEmpty = false, $country = null)
unset($options[0]);
} else {
if ($options) {
- $options[0]['label'] = '*';
+ $options[0] = array('value' => '0', 'label' => '*');
} else {
- $options = array(array('value' => '', 'label' => '*'));
+ $options = array(array('value' => '0', 'label' => '*'));
}
}
diff --git a/app/code/Magento/Tax/etc/module.xml b/app/code/Magento/Tax/etc/module.xml
index 099b9fe5254ed..11fadea58db84 100644
--- a/app/code/Magento/Tax/etc/module.xml
+++ b/app/code/Magento/Tax/etc/module.xml
@@ -24,7 +24,7 @@
*/
-->
-
+
diff --git a/app/code/Magento/Tax/sql/tax_setup/upgrade-1.6.0.5-1.6.0.6.php b/app/code/Magento/Tax/sql/tax_setup/upgrade-1.6.0.5-1.6.0.6.php
new file mode 100644
index 0000000000000..580eecb848ce1
--- /dev/null
+++ b/app/code/Magento/Tax/sql/tax_setup/upgrade-1.6.0.5-1.6.0.6.php
@@ -0,0 +1,62 @@
+getConnection();
+$adminRuleTable = $installer->getTable('admin_rule');
+$aclRulesDelete = array(
+ 'Magento_Tax::classes_customer',
+ 'Magento_Tax::classes_product',
+ 'Magento_Tax::import_export',
+ 'Magento_Tax::tax_rates',
+ 'Magento_Tax::rules'
+);
+
+/**
+ * Remove unneeded ACL rules
+ */
+$connection->delete($adminRuleTable, $connection->quoteInto('resource_id IN (?)', $aclRulesDelete));
+
+$connection->update(
+ $adminRuleTable,
+ array('resource_id' => 'Magento_Tax::manage_tax'),
+ array('resource_id = ?' => 'Magento_Tax::sales_tax')
+);
+
+/**
+ * Add new field to 'tax_calculation_rule'
+ */
+$connection->addColumn(
+ $this->getTable('tax_calculation_rule'),
+ 'calculate_subtotal',
+ [
+ 'TYPE' => \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
+ 'NULLABLE' => false,
+ 'COMMENT' => 'Calculate off subtotal option',
+ ]
+);
diff --git a/app/code/Magento/Theme/Block/Adminhtml/System/Design/Theme/Edit/Tab/General.php b/app/code/Magento/Theme/Block/Adminhtml/System/Design/Theme/Edit/Tab/General.php
index dd605251675fa..c9863c226b453 100644
--- a/app/code/Magento/Theme/Block/Adminhtml/System/Design/Theme/Edit/Tab/General.php
+++ b/app/code/Magento/Theme/Block/Adminhtml/System/Design/Theme/Edit/Tab/General.php
@@ -220,9 +220,12 @@ protected function _addThemeFieldset($form, $formData)
'label' => __('Theme Preview Image'),
'title' => __('Theme Preview Image'),
'name' => 'preview',
- 'after_element_html' => ' _themeImagePath->getPreviewImageDirectoryUrl()
- . $formData['preview_image'] . '" />'
+ . $formData['preview_image'] . '" onclick="imagePreview(\'theme_preview_image\'); return false;">'
+ . ' '
)
);
}
diff --git a/app/code/Magento/Theme/view/frontend/1column.phtml b/app/code/Magento/Theme/view/frontend/1column.phtml
index f4c8a575b92ae..0b2ac06ad088a 100644
--- a/app/code/Magento/Theme/view/frontend/1column.phtml
+++ b/app/code/Magento/Theme/view/frontend/1column.phtml
@@ -43,7 +43,7 @@ $bodyCss = $this->getBodyClass() ? $this->getBodyClass() : '';
getChildHtml('after_body_start') ?>
getChildHtml('global_notices') ?>
- getChildHtml('header-container') ?>
+ getChildHtml('header_container') ?>
getChildHtml('page_top') ?>
getChildHtml('columns_top') ?>
diff --git a/app/code/Magento/Theme/view/frontend/2columns-left.phtml b/app/code/Magento/Theme/view/frontend/2columns-left.phtml
index ce0f4a8be6942..fa8ea0aa9cbfc 100644
--- a/app/code/Magento/Theme/view/frontend/2columns-left.phtml
+++ b/app/code/Magento/Theme/view/frontend/2columns-left.phtml
@@ -43,7 +43,7 @@ $bodyCss = $this->getBodyClass() ? $this->getBodyClass() : '';
getChildHtml('after_body_start') ?>
getChildHtml('global_notices') ?>
- getChildHtml('header-container') ?>
+ getChildHtml('header_container') ?>
getChildHtml('page_top') ?>
getChildHtml('columns_top') ?>
diff --git a/app/code/Magento/Theme/view/frontend/2columns-right.phtml b/app/code/Magento/Theme/view/frontend/2columns-right.phtml
index fe652d2a1c8e4..f227734024ea5 100644
--- a/app/code/Magento/Theme/view/frontend/2columns-right.phtml
+++ b/app/code/Magento/Theme/view/frontend/2columns-right.phtml
@@ -43,7 +43,7 @@ $bodyCss = $this->getBodyClass() ? $this->getBodyClass() : '';
getChildHtml('after_body_start') ?>
getChildHtml('global_notices') ?>
- getChildHtml('header-container') ?>
+ getChildHtml('header_container') ?>
getChildHtml('page_top') ?>
getChildHtml('columns_top') ?>
diff --git a/app/code/Magento/Theme/view/frontend/3columns.phtml b/app/code/Magento/Theme/view/frontend/3columns.phtml
index 60b3a6886b71f..320b9240ba8d8 100644
--- a/app/code/Magento/Theme/view/frontend/3columns.phtml
+++ b/app/code/Magento/Theme/view/frontend/3columns.phtml
@@ -43,7 +43,7 @@ $bodyCss = $this->getBodyClass() ? $this->getBodyClass() : '';
getChildHtml('after_body_start') ?>
getChildHtml('global_notices') ?>
- getChildHtml('header-container') ?>
+ getChildHtml('header_container') ?>
getChildHtml('page_top') ?>
getChildHtml('columns_top') ?>
diff --git a/app/code/Magento/Theme/view/frontend/layout/default.xml b/app/code/Magento/Theme/view/frontend/layout/default.xml
index 91912a5ae7ea7..059f50dcc4bfa 100644
--- a/app/code/Magento/Theme/view/frontend/layout/default.xml
+++ b/app/code/Magento/Theme/view/frontend/layout/default.xml
@@ -31,7 +31,7 @@
-
+
@@ -66,16 +66,6 @@
-
diff --git a/app/code/Magento/Theme/view/frontend/page.phtml b/app/code/Magento/Theme/view/frontend/page.phtml
deleted file mode 100644
index f07b7e363a6e7..0000000000000
--- a/app/code/Magento/Theme/view/frontend/page.phtml
+++ /dev/null
@@ -1,59 +0,0 @@
-
-getBodyClass() ? $this->getBodyClass() : '';
-?>
-
-
-
-
-
-
- getChildHtml('head') ?>
-
-getAddAttribute() ?>
- data-mage-init='{"loaderAjax": {}, "loader": {}}'>
- getChildHtml('after_body_start') ?>
-
- getChildHtml('global_notices') ?>
- getChildHtml('header-container') ?>
- getChildHtml('page_top') ?>
-
- getChildHtml('page_main') ?>
-
- getChildHtml('page_bottom') ?>
-
- getChildHtml('footer') ?>
-
- getChildHtml('before_body_end') ?>
-
- getAbsoluteFooter() ?>
-
-
diff --git a/app/code/Magento/Theme/view/frontend/print.phtml b/app/code/Magento/Theme/view/frontend/print.phtml
index 88525aad918c7..d303d183fd264 100644
--- a/app/code/Magento/Theme/view/frontend/print.phtml
+++ b/app/code/Magento/Theme/view/frontend/print.phtml
@@ -36,23 +36,24 @@
data-container="body"
getAddAttribute() ?>
data-mage-init='{"loaderAjax": {}, "loader": {}}'>
-getChildHtml('after_body_start') ?>
-
-
-
- getChildHtml('content') ?>
-
-