diff --git a/app/code/Magento/AdminAnalytics/etc/adminhtml/system.xml b/app/code/Magento/AdminAnalytics/etc/adminhtml/system.xml index d6867e74c4760..a79635de5d1cd 100644 --- a/app/code/Magento/AdminAnalytics/etc/adminhtml/system.xml +++ b/app/code/Magento/AdminAnalytics/etc/adminhtml/system.xml @@ -8,9 +8,9 @@
- + - + Magento\Config\Model\Config\Source\Yesno Allow Magento to track admin usage in order to improve the quality and user experience. diff --git a/app/code/Magento/AdminNotification/etc/adminhtml/system.xml b/app/code/Magento/AdminNotification/etc/adminhtml/system.xml index 71f25741ca851..1df75be1316db 100644 --- a/app/code/Magento/AdminNotification/etc/adminhtml/system.xml +++ b/app/code/Magento/AdminNotification/etc/adminhtml/system.xml @@ -8,17 +8,17 @@
- + - + Magento\Config\Model\Config\Source\Yesno - + Magento\AdminNotification\Model\Config\Source\Frequency - + Magento\Config\Block\System\Config\Form\Field\Notification diff --git a/app/code/Magento/AdvancedSearch/etc/adminhtml/system.xml b/app/code/Magento/AdvancedSearch/etc/adminhtml/system.xml index 905dd3e7d1819..ecf8e328f51cb 100644 --- a/app/code/Magento/AdvancedSearch/etc/adminhtml/system.xml +++ b/app/code/Magento/AdvancedSearch/etc/adminhtml/system.xml @@ -10,37 +10,19 @@
- + When you enable this option your site may slow down. Magento\Config\Model\Config\Source\Yesno - + validate-digits 1 - + Magento\Config\Model\Config\Source\Yesno @@ -48,19 +30,19 @@ - + When you enable this option your site may slow down. Magento\Config\Model\Config\Source\Yesno - + validate-digits validate-zero-or-greater 1 - + Magento\Config\Model\Config\Source\Yesno When you enable this option your site may slow down. diff --git a/app/code/Magento/Analytics/etc/adminhtml/system.xml b/app/code/Magento/Analytics/etc/adminhtml/system.xml index 2a04128099345..999d565353329 100644 --- a/app/code/Magento/Analytics/etc/adminhtml/system.xml +++ b/app/code/Magento/Analytics/etc/adminhtml/system.xml @@ -7,29 +7,29 @@ --> -
+
general Magento_Analytics::analytics_settings - + For more information, see our terms and conditions.]]> - + Magento\Config\Model\Config\Source\Enabledisable Magento\Analytics\Model\Config\Backend\Enabled Magento\Analytics\Block\Adminhtml\System\Config\SubscriptionStatusLabel analytics/subscription/enabled - + Magento\Analytics\Block\Adminhtml\System\Config\CollectionTimeLabel Magento\Analytics\Model\Config\Backend\CollectionTime - + Industry Data In order to personalize your Advanced Reporting experience, please select your industry. @@ -40,7 +40,7 @@ 1 - + Learn more about
advanced - + - + validate-zero-or-greater validate-digits diff --git a/app/code/Magento/Authorizenet/etc/adminhtml/system.xml b/app/code/Magento/Authorizenet/etc/adminhtml/system.xml index 3f2037f70b2df..fe91967ed4a62 100644 --- a/app/code/Magento/Authorizenet/etc/adminhtml/system.xml +++ b/app/code/Magento/Authorizenet/etc/adminhtml/system.xml @@ -10,88 +10,88 @@
- + Magento\Config\Model\Config\Source\Yesno - + Magento\Authorizenet\Model\Source\PaymentAction - + Magento\Config\Model\Config\Backend\Encrypted - + Magento\Config\Model\Config\Backend\Encrypted - + Magento\Config\Model\Config\Backend\Encrypted - + Magento\Config\Model\Config\Backend\Encrypted - + Magento\Sales\Model\Config\Source\Order\Status\Processing - + Magento\Config\Model\Config\Source\Yesno - + - + - + Magento\Config\Model\Config\Source\Locale\Currency - + Magento\Config\Model\Config\Source\Yesno - + Magento\Config\Model\Config\Source\Yesno - + validate-email - + Magento\Authorizenet\Model\Source\Cctype - + Magento\Config\Model\Config\Source\Yesno - + Magento\Payment\Model\Config\Source\Allspecificcountries - + Magento\Directory\Model\Config\Source\Country - + validate-number validate-zero-or-greater - + validate-number validate-zero-or-greater - + validate-number diff --git a/app/code/Magento/AuthorizenetAcceptjs/etc/adminhtml/system.xml b/app/code/Magento/AuthorizenetAcceptjs/etc/adminhtml/system.xml index 7cd00959d9772..86b6d3a198d81 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/etc/adminhtml/system.xml +++ b/app/code/Magento/AuthorizenetAcceptjs/etc/adminhtml/system.xml @@ -10,7 +10,7 @@
- + Magento\Config\Model\Config\Source\Yesno @@ -25,17 +25,17 @@ payment/authorizenet_acceptjs/title - + Magento\AuthorizenetAcceptjs\Model\Adminhtml\Source\Environment payment/authorizenet_acceptjs/environment - + Magento\AuthorizenetAcceptjs\Model\Adminhtml\Source\PaymentAction payment/authorizenet_acceptjs/payment_action - + Magento\Config\Model\Config\Backend\Encrypted payment/authorizenet_acceptjs/login @@ -44,7 +44,7 @@ 1 - + Magento\Config\Model\Config\Backend\Encrypted payment/authorizenet_acceptjs/trans_key @@ -53,7 +53,7 @@ 1 - + payment/authorizenet_acceptjs/public_client_key required-entry @@ -61,7 +61,7 @@ 1 - + Magento\Config\Model\Config\Backend\Encrypted payment/authorizenet_acceptjs/trans_signature_key @@ -70,7 +70,7 @@ 1 - + Magento\Config\Model\Config\Backend\Encrypted payment/authorizenet_acceptjs/trans_md5 @@ -79,55 +79,55 @@ - + 0 - + Magento\Config\Model\Config\Source\Locale\Currency payment/authorizenet_acceptjs/currency - + Magento\Config\Model\Config\Source\Yesno payment/authorizenet_acceptjs/debug - + Magento\Config\Model\Config\Source\Yesno payment/authorizenet_acceptjs/email_customer - + Magento\Config\Model\Config\Source\Yesno payment/authorizenet_acceptjs/cvv_enabled - + Magento\AuthorizenetAcceptjs\Model\Adminhtml\Source\Cctype payment/authorizenet_acceptjs/cctypes - + Magento\Payment\Model\Config\Source\Allspecificcountries payment/authorizenet_acceptjs/allowspecific - + Magento\Directory\Model\Config\Source\Country payment/authorizenet_acceptjs/specificcountry - + payment/authorizenet_acceptjs/min_order_total validate-number validate-zero-or-greater - + payment/authorizenet_acceptjs/max_order_total validate-number validate-zero-or-greater - + validate-number payment/authorizenet_acceptjs/sort_order diff --git a/app/code/Magento/AuthorizenetCardinal/etc/adminhtml/system.xml b/app/code/Magento/AuthorizenetCardinal/etc/adminhtml/system.xml index 2be287a5e8743..cf8ad28d26d0e 100644 --- a/app/code/Magento/AuthorizenetCardinal/etc/adminhtml/system.xml +++ b/app/code/Magento/AuthorizenetCardinal/etc/adminhtml/system.xml @@ -10,7 +10,7 @@
- + Magento\Config\Model\Config\Source\Yesno three_d_secure/cardinal/enabled_authorizenet diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Denied/Index.php b/app/code/Magento/Backend/Controller/Adminhtml/Denied/Index.php new file mode 100644 index 0000000000000..c06c0a0f01650 --- /dev/null +++ b/app/code/Magento/Backend/Controller/Adminhtml/Denied/Index.php @@ -0,0 +1,27 @@ +setHasAvailableResources(false); } - $action = '*/*/denied'; + $action = '*/denied'; } return $action; } diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginWithRestrictPermissionTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginWithRestrictPermissionTest.xml new file mode 100644 index 0000000000000..e664a4a5f3e2f --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginWithRestrictPermissionTest.xml @@ -0,0 +1,61 @@ + + + + + + + + + <stories value="Login on the Admin Login page" /> + <testCaseId value="MC-29321" /> + <severity value="MAJOR" /> + <description value="Check login with restrict role."/> + <group value="login"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="logIn"/> + <!--Create user role--> + <actionGroup ref="AdminFillUserRoleRequiredDataActionGroup" stepKey="fillUserRoleRequiredData"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Media Gallery"/> + </actionGroup> + <actionGroup ref="AdminUserClickRoleResourceTabActionGroup" stepKey="switchToRoleResourceTab"/> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="addRestrictedRoleStores"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Media Gallery"/> + </actionGroup> + <actionGroup ref="AdminUserSaveRoleActionGroup" stepKey="saveRole"/> + <!--Create user and assign role to it--> + <actionGroup ref="AdminCreateUserActionGroup" stepKey="createAdminUser"> + <argument name="role" value="adminRole"/> + <argument name="User" value="admin2"/> + </actionGroup> + </before> + <after> + <actionGroup ref="logout" stepKey="logoutAsSaleRoleUser"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--Delete created data--> + <actionGroup ref="AdminUserOpenAdminRolesPageActionGroup" stepKey="navigateToUserRoleGrid"/> + <actionGroup ref="AdminDeleteRoleActionGroup" stepKey="deleteUserRole"> + <argument name="role" value="adminRole"/> + </actionGroup> + <actionGroup ref="AdminOpenAdminUsersPageActionGroup" stepKey="goToAllUsersPage"/> + <actionGroup ref="AdminDeleteNewUserActionGroup" stepKey="deleteUser"> + <argument name="userName" value="{{admin2.username}}"/> + </actionGroup> + </after> + <!--Log out of admin and login with newly created user--> + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsNewUser"> + <argument name="adminUser" value="admin2"/> + </actionGroup> + <actionGroup ref="AssertUserRoleRestrictedAccessActionGroup" stepKey="assertRestrictPage"/> + </test> +</tests> diff --git a/app/code/Magento/Backend/Test/Unit/Model/UrlTest.php b/app/code/Magento/Backend/Test/Unit/Model/UrlTest.php index ad42108cb5eea..1ef3b3980441e 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/UrlTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/UrlTest.php @@ -190,7 +190,7 @@ public function testFindFirstAvailableMenuDenied() $this->_menuMock->expects($this->any())->method('getFirstAvailableChild')->will($this->returnValue(null)); - $this->assertEquals('*/*/denied', $this->_model->findFirstAvailableMenu()); + $this->assertEquals('*/denied', $this->_model->findFirstAvailableMenu()); } public function testFindFirstAvailableMenu() diff --git a/app/code/Magento/Backend/etc/adminhtml/system.xml b/app/code/Magento/Backend/etc/adminhtml/system.xml index 4a92ed8124bf8..3a2b3554cc4a0 100644 --- a/app/code/Magento/Backend/etc/adminhtml/system.xml +++ b/app/code/Magento/Backend/etc/adminhtml/system.xml @@ -20,7 +20,7 @@ @deprecated Magento does not support custom disabling/enabling modules output since 2.2.0 version. Section 'Advanced' was disabled. This section will be removed from code in one release. --> - <section id="advanced" translate="label" type="text" sortOrder="910" showInDefault="0" showInWebsite="0" showInStore="0"> + <section id="advanced" translate="label" type="text" sortOrder="910"> <label>Advanced</label> <tab>advanced</tab> <resource>Magento_Config::advanced</resource> @@ -131,7 +131,7 @@ </depends> <comment>Add the following parameter to the URL to show template hints ?templatehints=[parameter_value]</comment> </field> - <field id="template_hints_admin" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="template_hints_admin" translate="label" type="select" sortOrder="20" showInDefault="1"> <label>Enable Template Path Hints for Admin</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> @@ -162,7 +162,7 @@ <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\Config\Model\Config\Backend\Translate</backend_model> </field> - <field id="active_admin" translate="label comment" type="select" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="active_admin" translate="label comment" type="select" sortOrder="20" showInDefault="1"> <label>Enabled for Admin</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\Config\Model\Config\Backend\Translate</backend_model> @@ -197,18 +197,18 @@ <comment>Minification is not applied in developer mode.</comment> </field> </group> - <group id="image" translate="label" type="text" sortOrder="120" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="image" translate="label" type="text" sortOrder="120" showInDefault="1"> <label>Image Processing Settings</label> - <field id="default_adapter" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="default_adapter" translate="label comment" type="select" sortOrder="10" showInDefault="1" canRestore="1"> <label>Image Adapter</label> <source_model>Magento\Config\Model\Config\Source\Image\Adapter</source_model> <backend_model>Magento\Config\Model\Config\Backend\Image\Adapter</backend_model> <comment>When the adapter was changed, please flush Catalog Images Cache.</comment> </field> </group> - <group id="static" translate="label" type="text" sortOrder="130" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="static" translate="label" type="text" sortOrder="130" showInDefault="1"> <label>Static Files Settings</label> - <field id="sign" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="sign" translate="label" type="select" sortOrder="10" showInDefault="1" canRestore="1"> <label>Sign Static Files</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> @@ -220,7 +220,7 @@ <resource>Magento_Config::config_general</resource> <group id="country" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Country Options</label> - <field id="allow" translate="label" type="multiselect" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allow" translate="label" type="multiselect" sortOrder="2" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Allow Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> @@ -229,7 +229,7 @@ <label>Default Country</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> </field> - <field id="eu_countries" translate="label" type="multiselect" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="eu_countries" translate="label" type="multiselect" sortOrder="30" showInDefault="1" canRestore="1"> <label>European Union Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> </field> @@ -241,7 +241,7 @@ </group> <group id="locale" translate="label" type="text" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Locale Options</label> - <field id="timezone" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="timezone" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1"> <label>Timezone</label> <source_model>Magento\Config\Model\Config\Source\Locale\Timezone</source_model> <backend_model>Magento\Config\Model\Config\Backend\Locale\Timezone</backend_model> @@ -271,35 +271,35 @@ <field id="hours" translate="label" type="text" sortOrder="22" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Store Hours of Operation</label> </field> - <field id="country_id" translate="label" type="select" sortOrder="25" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="country_id" translate="label" type="select" sortOrder="25" showInDefault="1" showInWebsite="1"> <label>Country</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <frontend_class>countries</frontend_class> <can_be_empty>1</can_be_empty> </field> - <field id="region_id" translate="label" type="text" sortOrder="27" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="region_id" translate="label" type="text" sortOrder="27" showInDefault="1" showInWebsite="1"> <label>Region/State</label> </field> - <field id="postcode" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="postcode" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1"> <label>ZIP/Postal Code</label> </field> - <field id="city" translate="label" type="text" sortOrder="45" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="city" translate="label" type="text" sortOrder="45" showInDefault="1" showInWebsite="1"> <label>City</label> </field> - <field id="street_line1" translate="label" type="text" sortOrder="55" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="street_line1" translate="label" type="text" sortOrder="55" showInDefault="1" showInWebsite="1"> <label>Street Address</label> </field> - <field id="street_line2" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="street_line2" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="1"> <label>Street Address Line 2</label> </field> - <field id="merchant_vat_number" translate="label" type="text" sortOrder="61" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="merchant_vat_number" translate="label" type="text" sortOrder="61" showInDefault="1" showInWebsite="1"> <label>VAT Number</label> <can_be_empty>1</can_be_empty> </field> </group> - <group id="single_store_mode" translate="label" type="text" sortOrder="150" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="single_store_mode" translate="label" type="text" sortOrder="150" showInDefault="1"> <label>Single-Store Mode</label> - <field id="enabled" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="enabled" translate="label comment" type="select" sortOrder="10" showInDefault="1"> <label>Enable Single-Store Mode</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>This setting will not be taken into account if system has more than one store view.</comment> @@ -326,7 +326,7 @@ <validate>validate-digits validate-digits-range digits-range-0-65535</validate> <comment>Please enter at least 0 and at most 65535 (For Windows server only).</comment> </field> - <field id="set_return_path" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="set_return_path" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Set Return-Path</label> <source_model>Magento\Config\Model\Config\Source\Yesnocustom</source_model> </field> @@ -341,12 +341,12 @@ </group> <group id="upload_configuration" translate="label" type="text" sortOrder="1000" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Images Upload Configuration</label> - <field id="enable_resize" translate="label" type="select" sortOrder="200" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="enable_resize" translate="label" type="select" sortOrder="200" showInDefault="1" canRestore="1"> <label>Enable Frontend Resize</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Resize performed via javascript before file upload.</comment> </field> - <field id="max_width" translate="label comment" type="text" sortOrder="300" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="max_width" translate="label comment" type="text" sortOrder="300" showInDefault="1" canRestore="1"> <label>Maximum Width</label> <validate>validate-greater-than-zero validate-number required-entry</validate> <comment>Maximum allowed width for uploaded image.</comment> @@ -354,7 +354,7 @@ <field id="enable_resize">1</field> </depends> </field> - <field id="max_height" translate="label comment" type="text" sortOrder="400" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="max_height" translate="label comment" type="text" sortOrder="400" showInDefault="1" canRestore="1"> <label>Maximum Height</label> <validate>validate-greater-than-zero validate-number required-entry</validate> <comment>Maximum allowed height for uploaded image.</comment> @@ -364,37 +364,37 @@ </field> </group> </section> - <section id="admin" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0"> + <section id="admin" translate="label" type="text" sortOrder="20" showInDefault="1"> <label>Admin</label> <tab>advanced</tab> <resource>Magento_Config::config_admin</resource> - <group id="emails" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="emails" translate="label" type="text" sortOrder="10" showInDefault="1"> <label>Admin User Emails</label> - <field id="forgot_email_template" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="forgot_email_template" translate="label comment" type="select" sortOrder="10" showInDefault="1" canRestore="1"> <label>Forgot Password Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> </field> - <field id="forgot_email_identity" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="forgot_email_identity" translate="label" type="select" sortOrder="20" showInDefault="1" canRestore="1"> <label>Forgot and Reset Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> </field> </group> - <group id="startup" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="startup" translate="label" type="text" sortOrder="20" showInDefault="1"> <label>Startup Page</label> - <field id="menu_item_id" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="menu_item_id" translate="label" type="select" sortOrder="1" showInDefault="1" canRestore="1"> <label>Startup Page</label> <source_model>Magento\Config\Model\Config\Source\Admin\Page</source_model> </field> </group> - <group id="url" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="url" translate="label" type="text" sortOrder="30" showInDefault="1"> <label>Admin Base URL</label> - <field id="use_custom" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="use_custom" translate="label" type="select" sortOrder="1" showInDefault="1" canRestore="1"> <label>Use Custom Admin URL</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\Config\Model\Config\Backend\Admin\Usecustom</backend_model> </field> - <field id="custom" translate="label comment" type="text" sortOrder="2" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="custom" translate="label comment" type="text" sortOrder="2" showInDefault="1" canRestore="1"> <label>Custom Admin URL</label> <backend_model>Magento\Config\Model\Config\Backend\Admin\Custom</backend_model> <depends> @@ -402,12 +402,12 @@ </depends> <comment>Make sure that base URL ends with '/' (slash), e.g. http://yourdomain/magento/</comment> </field> - <field id="use_custom_path" translate="label" type="select" sortOrder="3" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="use_custom_path" translate="label" type="select" sortOrder="3" showInDefault="1" canRestore="1"> <label>Use Custom Admin Path</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\Config\Model\Config\Backend\Admin\Custompath</backend_model> </field> - <field id="custom_path" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="custom_path" translate="label comment" type="text" sortOrder="4" showInDefault="1" canRestore="1"> <label>Custom Admin Path</label> <validate>required-entry validate-alphanum</validate> <backend_model>Magento\Config\Model\Config\Backend\Admin\Custompath</backend_model> @@ -417,33 +417,33 @@ <comment>You will have to sign in after you save your custom admin path.</comment> </field> </group> - <group id="security" translate="label" type="text" sortOrder="35" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="security" translate="label" type="text" sortOrder="35" showInDefault="1"> <label>Security</label> - <field id="password_reset_link_expiration_period" translate="label comment" type="text" sortOrder="7" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="password_reset_link_expiration_period" translate="label comment" type="text" sortOrder="7" showInDefault="1" canRestore="1"> <label>Recovery Link Expiration Period (hours)</label> <comment>Please enter a number 1 or greater in this field.</comment> <validate>required-entry integer validate-greater-than-zero</validate> <backend_model>Magento\Config\Model\Config\Backend\Admin\Password\Link\Expirationperiod</backend_model> </field> - <field id="use_form_key" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="use_form_key" translate="label" type="select" sortOrder="10" showInDefault="1" canRestore="1"> <label>Add Secret Key to URLs</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\Config\Model\Config\Backend\Admin\Usesecretkey</backend_model> </field> - <field id="use_case_sensitive_login" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="use_case_sensitive_login" translate="label" type="select" sortOrder="20" showInDefault="1" canRestore="1"> <label>Login is Case Sensitive</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="session_lifetime" translate="label comment" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="session_lifetime" translate="label comment" sortOrder="30" showInDefault="1" canRestore="1"> <label>Admin Session Lifetime (seconds)</label> <comment>Please enter at least 60 and at most 31536000 (one year).</comment> <backend_model>Magento\Backend\Model\Config\SessionLifetime\BackendModel</backend_model> <validate>validate-digits validate-digits-range digits-range-60-31536000</validate> </field> </group> - <group id="dashboard" translate="label" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="dashboard" translate="label" sortOrder="40" showInDefault="1"> <label>Dashboard</label> - <field id="enable_charts" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="enable_charts" translate="label" type="select" sortOrder="1" showInDefault="1" canRestore="1"> <label>Enable Charts</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> @@ -455,7 +455,7 @@ <resource>Magento_Config::web</resource> <group id="url" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Url Options</label> - <field id="use_store" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="use_store" translate="label comment" type="select" sortOrder="10" showInDefault="1" canRestore="1"> <label>Add Store Code to Urls</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\Config\Model\Config\Backend\Store</backend_model> @@ -529,7 +529,7 @@ <backend_model>Magento\Config\Model\Config\Backend\Secure</backend_model> <comment>Enter https protocol to use Secure URLs on Storefront.</comment> </field> - <field id="use_in_adminhtml" translate="label comment" type="select" sortOrder="60" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="use_in_adminhtml" translate="label comment" type="select" sortOrder="60" showInDefault="1" canRestore="1"> <label>Use Secure URLs in Admin</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\Config\Model\Config\Backend\Secure</backend_model> @@ -555,7 +555,7 @@ <field id="use_in_adminhtml">1</field> </depends> </field> - <field id="offloader_header" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="offloader_header" translate="label" type="text" sortOrder="90" showInDefault="1" canRestore="1"> <label>Offloader header</label> </field> </group> @@ -568,21 +568,21 @@ <label>Default No-route URL</label> </field> </group> - <group id="session" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="session" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="1"> <label>Session Validation Settings</label> - <field id="use_remote_addr" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="use_remote_addr" translate="label" type="select" sortOrder="1" showInDefault="1" canRestore="1"> <label>Validate REMOTE_ADDR</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="use_http_via" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="use_http_via" translate="label" type="select" sortOrder="20" showInDefault="1" canRestore="1"> <label>Validate HTTP_VIA</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="use_http_x_forwarded_for" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="use_http_x_forwarded_for" translate="label" type="select" sortOrder="30" showInDefault="1" canRestore="1"> <label>Validate HTTP_X_FORWARDED_FOR</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="use_http_user_agent" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="use_http_user_agent" translate="label" type="select" sortOrder="40" showInDefault="1" canRestore="1"> <label>Validate HTTP_USER_AGENT</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> diff --git a/app/code/Magento/Backend/etc/config.xml b/app/code/Magento/Backend/etc/config.xml index 8283fa18dd370..6c568370d9610 100644 --- a/app/code/Magento/Backend/etc/config.xml +++ b/app/code/Magento/Backend/etc/config.xml @@ -11,9 +11,6 @@ <template> <minify_html>0</minify_html> </template> - <static> - <sign>1</sign> - </static> </dev> <system> <media_storage_configuration> diff --git a/app/code/Magento/Backup/etc/adminhtml/system.xml b/app/code/Magento/Backup/etc/adminhtml/system.xml index 78e0aae1dd4c2..577762037ceb4 100644 --- a/app/code/Magento/Backup/etc/adminhtml/system.xml +++ b/app/code/Magento/Backup/etc/adminhtml/system.xml @@ -8,21 +8,21 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="system"> - <group id="backup" translate="label" type="text" sortOrder="500" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="backup" translate="label" type="text" sortOrder="500" showInDefault="1"> <label>Backup Settings</label> - <field id="functionality_enabled" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="functionality_enabled" translate="label" type="select" sortOrder="5" showInDefault="1"> <label>Enable Backup</label> <comment>Disabled by default for security reasons.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="enabled" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="enabled" translate="label" type="select" sortOrder="10" showInDefault="1"> <label>Enable Scheduled Backup</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <depends> <field id="functionality_enabled">1</field> </depends> </field> - <field id="type" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="type" translate="label" type="select" sortOrder="20" showInDefault="1"> <label>Scheduled Backup Type</label> <depends> <field id="enabled">1</field> @@ -30,14 +30,14 @@ </depends> <source_model>Magento\Backup\Model\Config\Source\Type</source_model> </field> - <field id="time" translate="label" type="time" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="time" translate="label" type="time" sortOrder="30" showInDefault="1"> <label>Start Time</label> <depends> <field id="enabled">1</field> <field id="functionality_enabled">1</field> </depends> </field> - <field id="frequency" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="frequency" translate="label" type="select" sortOrder="40" showInDefault="1"> <label>Frequency</label> <depends> <field id="enabled">1</field> @@ -46,7 +46,7 @@ <source_model>Magento\Cron\Model\Config\Source\Frequency</source_model> <backend_model>Magento\Backup\Model\Config\Backend\Cron</backend_model> </field> - <field id="maintenance" translate="label comment" type="select" sortOrder="50" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="maintenance" translate="label comment" type="select" sortOrder="50" showInDefault="1"> <label>Maintenance Mode</label> <comment>Please put your store into maintenance mode during backup.</comment> <depends> diff --git a/app/code/Magento/Braintree/etc/adminhtml/system.xml b/app/code/Magento/Braintree/etc/adminhtml/system.xml index bd4346e095c6d..06268536e880e 100644 --- a/app/code/Magento/Braintree/etc/adminhtml/system.xml +++ b/app/code/Magento/Braintree/etc/adminhtml/system.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="payment"> - <group id="braintree_section" sortOrder="6" showInDefault="0" showInWebsite="0" showInStore="0"> + <group id="braintree_section" sortOrder="6"> <group id="braintree" translate="label comment" type="text" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Braintree</label> <comment><![CDATA[Accept credit/debit cards and PayPal in your Magento store.<br/>No setup or monthly fees and your customers never leave your store to complete the purchase.]]></comment> @@ -16,7 +16,7 @@ <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment</frontend_model> <attribute type="activity_path">payment/braintree/active</attribute> <attribute type="displayIn">recommended_solutions</attribute> - <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1"> <label>Enable this Solution</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree/active</config_path> @@ -24,7 +24,7 @@ <group id="braintree_required"/> </requires> </field> - <field id="active_braintree_paypal" translate="label" type="select" sortOrder="11" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="active_braintree_paypal" translate="label" type="select" sortOrder="11" showInDefault="1" showInWebsite="1"> <label>Enable PayPal through Braintree</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree_paypal/active</config_path> @@ -32,7 +32,7 @@ <group id="braintree_required"/> </requires> </field> - <field id="braintree_cc_vault_active" translate="label" type="select" sortOrder="12" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="braintree_cc_vault_active" translate="label" type="select" sortOrder="12" showInDefault="1" showInWebsite="1"> <label>Vault Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree_cc_vault/active</config_path> @@ -52,86 +52,86 @@ <label>Title</label> <config_path>payment/braintree/title</config_path> </field> - <field id="environment" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="environment" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> <label>Environment</label> <source_model>Magento\Braintree\Model\Adminhtml\Source\Environment</source_model> <config_path>payment/braintree/environment</config_path> </field> - <field id="payment_action" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="payment_action" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1"> <label>Payment Action</label> <source_model>Magento\Braintree\Model\Adminhtml\Source\PaymentAction</source_model> <config_path>payment/braintree/payment_action</config_path> </field> - <field id="merchant_id" translate="label" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="merchant_id" translate="label" sortOrder="90" showInDefault="1" showInWebsite="1"> <label>Merchant ID</label> <config_path>payment/braintree/merchant_id</config_path> </field> - <field id="public_key" translate="label" type="obscure" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="public_key" translate="label" type="obscure" sortOrder="100" showInDefault="1" showInWebsite="1"> <label>Public Key</label> <config_path>payment/braintree/public_key</config_path> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> - <field id="private_key" translate="label" type="obscure" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="private_key" translate="label" type="obscure" sortOrder="110" showInDefault="1" showInWebsite="1"> <label>Private Key</label> <config_path>payment/braintree/private_key</config_path> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> </group> - <group id="braintree_advanced" translate="label" showInDefault="1" showInWebsite="1" showInStore="0" sortOrder="20"> + <group id="braintree_advanced" translate="label" showInDefault="1" showInWebsite="1" sortOrder="20"> <label>Advanced Braintree Settings</label> <frontend_model>Magento\Config\Block\System\Config\Form\Fieldset</frontend_model> - <field id="braintree_cc_vault_title" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="braintree_cc_vault_title" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1"> <label>Vault Title</label> <config_path>payment/braintree_cc_vault/title</config_path> </field> - <field id="merchant_account_id" translate="label comment" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="merchant_account_id" translate="label comment" sortOrder="30" showInDefault="1" showInWebsite="1"> <label>Merchant Account ID</label> <comment>If you don't specify the merchant account to use to process a transaction, Braintree will process it using your default merchant account.</comment> <config_path>payment/braintree/merchant_account_id</config_path> </field> - <field id="fraudprotection" translate="label comment" type="select" sortOrder="34" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="fraudprotection" translate="label comment" type="select" sortOrder="34" showInDefault="1" showInWebsite="1"> <label>Advanced Fraud Protection</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Be sure to Enable Advanced Fraud Protection in Your Braintree Account in Settings/Processing Section</comment> <config_path>payment/braintree/fraudprotection</config_path> </field> - <field id="debug" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="debug" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1"> <label>Debug</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree/debug</config_path> </field> - <field id="useccv" translate="label comment" type="select" sortOrder="150" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="useccv" translate="label comment" type="select" sortOrder="150" showInDefault="1" showInWebsite="1"> <label>CVV Verification</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Be sure to Enable AVS and/or CVV in Your Braintree Account in Settings/Processing Section.</comment> <config_path>payment/braintree/useccv</config_path> </field> - <field id="cctypes" translate="label" type="multiselect" sortOrder="160" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="cctypes" translate="label" type="multiselect" sortOrder="160" showInDefault="1" showInWebsite="1"> <label>Credit Card Types</label> <source_model>Magento\Braintree\Model\Adminhtml\Source\CcType</source_model> <config_path>payment/braintree/cctypes</config_path> </field> - <field id="sort_order" translate="label" type="text" sortOrder="230" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="230" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <frontend_class>validate-number</frontend_class> <config_path>payment/braintree/sort_order</config_path> </field> </group> - <group id="braintree_country_specific" translate="label" showInDefault="1" showInWebsite="1" showInStore="0" sortOrder="30"> + <group id="braintree_country_specific" translate="label" showInDefault="1" showInWebsite="1" sortOrder="30"> <label>Country Specific Settings</label> <frontend_model>Magento\Config\Block\System\Config\Form\Fieldset</frontend_model> - <field id="allowspecific" translate="label" type="allowspecific" sortOrder="200" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="allowspecific" translate="label" type="allowspecific" sortOrder="200" showInDefault="1" showInWebsite="1"> <label>Payment from Applicable Countries</label> <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> <config_path>payment/braintree/allowspecific</config_path> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="210" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="210" showInDefault="1" showInWebsite="1"> <label>Payment from Specific Countries</label> <source_model>Magento\Braintree\Model\Adminhtml\System\Config\Country</source_model> <can_be_empty>1</can_be_empty> <config_path>payment/braintree/specificcountry</config_path> </field> - <field id="countrycreditcard" translate="label" sortOrder="220" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="countrycreditcard" translate="label" sortOrder="220" showInDefault="1" showInWebsite="1"> <label>Country Specific Credit Card Types</label> <frontend_model>Magento\Braintree\Block\Adminhtml\Form\Field\CountryCreditCard</frontend_model> <backend_model>Magento\Braintree\Model\Adminhtml\System\Config\CountryCreditCard</backend_model> @@ -146,7 +146,7 @@ <config_path>payment/braintree_paypal/title</config_path> <comment>It is recommended to set this value to "PayPal" per store views.</comment> </field> - <field id="braintree_paypal_vault_active" translate="label" type="select" sortOrder="21" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="braintree_paypal_vault_active" translate="label" type="select" sortOrder="21" showInDefault="1" showInWebsite="1"> <label>Vault Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree_paypal_vault/active</config_path> @@ -154,7 +154,7 @@ <group id="braintree_required"/> </requires> </field> - <field id="sort_order" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <frontend_class>validate-number</frontend_class> <config_path>payment/braintree_paypal/sort_order</config_path> @@ -163,68 +163,68 @@ <label>Override Merchant Name</label> <config_path>payment/braintree_paypal/merchant_name_override</config_path> </field> - <field id="payment_action" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="payment_action" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1"> <label>Payment Action</label> <source_model>Magento\Braintree\Model\Adminhtml\Source\PaymentAction</source_model> <config_path>payment/braintree_paypal/payment_action</config_path> </field> - <field id="allowspecific" translate="label" type="allowspecific" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="allowspecific" translate="label" type="allowspecific" sortOrder="70" showInDefault="1" showInWebsite="1"> <label>Payment from Applicable Countries</label> <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> <config_path>payment/braintree_paypal/allowspecific</config_path> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="80" showInDefault="1" showInWebsite="1"> <label>Payment from Specific Countries</label> <source_model>Magento\Braintree\Model\Adminhtml\System\Config\Country</source_model> <can_be_empty>1</can_be_empty> <config_path>payment/braintree_paypal/specificcountry</config_path> </field> - <field id="require_billing_address" translate="label comment" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="require_billing_address" translate="label comment" type="select" sortOrder="90" showInDefault="1" showInWebsite="1"> <label>Require Customer's Billing Address</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree_paypal/require_billing_address</config_path> <comment>This feature needs be enabled first for the merchant account through PayPal technical support.</comment> </field> - <field id="allow_shipping_address_override" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="allow_shipping_address_override" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="1"> <label>Allow to Edit Shipping Address Entered During Checkout on PayPal Side</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree_paypal/allow_shipping_address_override</config_path> </field> - <field id="debug" translate="label" type="select" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="debug" translate="label" type="select" sortOrder="110" showInDefault="1" showInWebsite="1"> <label>Debug</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree_paypal/debug</config_path> </field> - <field id="display_on_shopping_cart" translate="label comment" type="select" sortOrder="120" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="display_on_shopping_cart" translate="label comment" type="select" sortOrder="120" showInDefault="1" showInWebsite="1"> <label>Display on Shopping Cart</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree_paypal/display_on_shopping_cart</config_path> <comment>Also affects mini-shopping cart.</comment> </field> - <field id="skip_order_review" translate="label" type="select" sortOrder="120" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="skip_order_review" translate="label" type="select" sortOrder="120" showInDefault="1" showInWebsite="1"> <label>Skip Order Review</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree_paypal/skip_order_review</config_path> </field> </group> - <group id="braintree_3dsecure" translate="label" showInDefault="1" showInWebsite="1" showInStore="0" sortOrder="41"> + <group id="braintree_3dsecure" translate="label" showInDefault="1" showInWebsite="1" sortOrder="41"> <label>3D Secure Verification Settings</label> <frontend_model>Magento\Config\Block\System\Config\Form\Fieldset</frontend_model> - <field id="verify_3dsecure" translate="label" type="select" sortOrder="150" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="verify_3dsecure" translate="label" type="select" sortOrder="150" showInDefault="1" showInWebsite="1"> <label>3D Secure Verification</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>payment/braintree/verify_3dsecure</config_path> </field> - <field id="threshold_amount" translate="label" type="text" sortOrder="151" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="threshold_amount" translate="label" type="text" sortOrder="151" showInDefault="1" showInWebsite="1"> <label>Threshold Amount</label> <config_path>payment/braintree/threshold_amount</config_path> </field> - <field id="allowspecific" translate="label" type="allowspecific" sortOrder="152" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="allowspecific" translate="label" type="allowspecific" sortOrder="152" showInDefault="1" showInWebsite="1"> <label>Verify for Applicable Countries</label> <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> <config_path>payment/braintree/verify_all_countries</config_path> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="153" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="153" showInDefault="1" showInWebsite="1"> <label>Verify for Specific Countries</label> <source_model>Magento\Braintree\Model\Adminhtml\System\Config\Country</source_model> <can_be_empty>1</can_be_empty> diff --git a/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php b/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php index 077ebd4422aab..a2fff5739f2f9 100644 --- a/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php +++ b/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php @@ -7,6 +7,7 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BasePriceModifier; +use Magento\Framework\DB\Select; use Magento\Framework\Indexer\DimensionalIndexerInterface; use Magento\Framework\EntityManager\MetadataPool; use Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer; @@ -394,8 +395,8 @@ private function calculateBundleOptionPrice($priceTable, $dimensions) $connection = $this->getConnection(); $this->prepareBundleSelectionTable(); - $this->calculateBundleSelectionPrice($dimensions, \Magento\Bundle\Model\Product\Price::PRICE_TYPE_FIXED); - $this->calculateBundleSelectionPrice($dimensions, \Magento\Bundle\Model\Product\Price::PRICE_TYPE_DYNAMIC); + $this->calculateFixedBundleSelectionPrice(); + $this->calculateDynamicBundleSelectionPrice($dimensions); $this->prepareBundleOptionTable(); @@ -426,84 +427,17 @@ private function calculateBundleOptionPrice($priceTable, $dimensions) } /** - * Calculate bundle product selections price by product type + * Get base select for bundle selection price * - * @param array $dimensions - * @param int $priceType - * @return void + * @return Select * @throws \Exception - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - private function calculateBundleSelectionPrice($dimensions, $priceType) + private function getBaseBundleSelectionPriceSelect(): Select { - $connection = $this->getConnection(); - - if ($priceType == \Magento\Bundle\Model\Product\Price::PRICE_TYPE_FIXED) { - $selectionPriceValue = $connection->getCheckSql( - 'bsp.selection_price_value IS NULL', - 'bs.selection_price_value', - 'bsp.selection_price_value' - ); - $selectionPriceType = $connection->getCheckSql( - 'bsp.selection_price_type IS NULL', - 'bs.selection_price_type', - 'bsp.selection_price_type' - ); - $priceExpr = new \Zend_Db_Expr( - $connection->getCheckSql( - $selectionPriceType . ' = 1', - 'ROUND(i.price * (' . $selectionPriceValue . ' / 100),4)', - $connection->getCheckSql( - 'i.special_price > 0 AND i.special_price < 100', - 'ROUND(' . $selectionPriceValue . ' * (i.special_price / 100),4)', - $selectionPriceValue - ) - ) . '* bs.selection_qty' - ); - - $tierExpr = $connection->getCheckSql( - 'i.base_tier IS NOT NULL', - $connection->getCheckSql( - $selectionPriceType . ' = 1', - 'ROUND(i.base_tier - (i.base_tier * (' . $selectionPriceValue . ' / 100)),4)', - $connection->getCheckSql( - 'i.tier_percent > 0', - 'ROUND((1 - i.tier_percent / 100) * ' . $selectionPriceValue . ',4)', - $selectionPriceValue - ) - ) . ' * bs.selection_qty', - 'NULL' - ); - - $priceExpr = $connection->getLeastSql( - [ - $priceExpr, - $connection->getIfNullSql($tierExpr, $priceExpr), - ] - ); - } else { - $price = 'idx.min_price * bs.selection_qty'; - $specialExpr = $connection->getCheckSql( - 'i.special_price > 0 AND i.special_price < 100', - 'ROUND(' . $price . ' * (i.special_price / 100), 4)', - $price - ); - $tierExpr = $connection->getCheckSql( - 'i.tier_percent IS NOT NULL', - 'ROUND((1 - i.tier_percent / 100) * ' . $price . ', 4)', - 'NULL' - ); - $priceExpr = $connection->getLeastSql( - [ - $specialExpr, - $connection->getIfNullSql($tierExpr, $price), - ] - ); - } - $metadata = $this->metadataPool->getMetadata(ProductInterface::class); $linkField = $metadata->getLinkField(); - $select = $connection->select()->from( + + $select = $this->getConnection()->select()->from( ['i' => $this->getBundlePriceTable()], ['entity_id', 'customer_group_id', 'website_id'] )->join( @@ -518,22 +452,173 @@ private function calculateBundleSelectionPrice($dimensions, $priceType) ['bs' => $this->getTable('catalog_product_bundle_selection')], 'bs.option_id = bo.option_id', ['selection_id'] - )->joinLeft( + ); + + return $select; + } + + /** + * Apply selections price for fixed bundles + * + * @return void + * @throws \Exception + */ + private function applyFixedBundleSelectionPrice() + { + $connection = $this->getConnection(); + + $selectionPriceValue = 'bsp.selection_price_value'; + $selectionPriceType = 'bsp.selection_price_type'; + $priceExpr = new \Zend_Db_Expr( + $connection->getCheckSql( + $selectionPriceType . ' = 1', + 'ROUND(i.price * (' . $selectionPriceValue . ' / 100),4)', + $connection->getCheckSql( + 'i.special_price > 0 AND i.special_price < 100', + 'ROUND(' . $selectionPriceValue . ' * (i.special_price / 100),4)', + $selectionPriceValue + ) + ) . '* bs.selection_qty' + ); + $tierExpr = $connection->getCheckSql( + 'i.base_tier IS NOT NULL', + $connection->getCheckSql( + $selectionPriceType . ' = 1', + 'ROUND(i.base_tier - (i.base_tier * (' . $selectionPriceValue . ' / 100)),4)', + $connection->getCheckSql( + 'i.tier_percent > 0', + 'ROUND((1 - i.tier_percent / 100) * ' . $selectionPriceValue . ',4)', + $selectionPriceValue + ) + ) . ' * bs.selection_qty', + 'NULL' + ); + $priceExpr = $connection->getLeastSql( + [ + $priceExpr, + $connection->getIfNullSql($tierExpr, $priceExpr), + ] + ); + + $select = $this->getBaseBundleSelectionPriceSelect(); + $select->joinInner( ['bsp' => $this->getTable('catalog_product_bundle_selection_price')], 'bs.selection_id = bsp.selection_id AND bsp.website_id = i.website_id', - [''] - )->join( + [] + )->where( + 'i.price_type=?', + \Magento\Bundle\Model\Product\Price::PRICE_TYPE_FIXED + )->columns( + [ + 'group_type' => $connection->getCheckSql("bo.type = 'select' OR bo.type = 'radio'", '0', '1'), + 'is_required' => 'bo.required', + 'price' => $priceExpr, + 'tier_price' => $tierExpr, + ] + ); + $query = $select->crossUpdateFromSelect($this->getBundleSelectionTable()); + $connection->query($query); + } + + /** + * Calculate selections price for fixed bundles + * + * @return void + * @throws \Exception + */ + private function calculateFixedBundleSelectionPrice() + { + $connection = $this->getConnection(); + + $selectionPriceValue = 'bs.selection_price_value'; + $selectionPriceType = 'bs.selection_price_type'; + $priceExpr = new \Zend_Db_Expr( + $connection->getCheckSql( + $selectionPriceType . ' = 1', + 'ROUND(i.price * (' . $selectionPriceValue . ' / 100),4)', + $connection->getCheckSql( + 'i.special_price > 0 AND i.special_price < 100', + 'ROUND(' . $selectionPriceValue . ' * (i.special_price / 100),4)', + $selectionPriceValue + ) + ) . '* bs.selection_qty' + ); + $tierExpr = $connection->getCheckSql( + 'i.base_tier IS NOT NULL', + $connection->getCheckSql( + $selectionPriceType . ' = 1', + 'ROUND(i.base_tier - (i.base_tier * (' . $selectionPriceValue . ' / 100)),4)', + $connection->getCheckSql( + 'i.tier_percent > 0', + 'ROUND((1 - i.tier_percent / 100) * ' . $selectionPriceValue . ',4)', + $selectionPriceValue + ) + ) . ' * bs.selection_qty', + 'NULL' + ); + $priceExpr = $connection->getLeastSql( + [ + $priceExpr, + $connection->getIfNullSql($tierExpr, $priceExpr), + ] + ); + + $select = $this->getBaseBundleSelectionPriceSelect(); + $select->where( + 'i.price_type=?', + \Magento\Bundle\Model\Product\Price::PRICE_TYPE_FIXED + )->columns( + [ + 'group_type' => $connection->getCheckSql("bo.type = 'select' OR bo.type = 'radio'", '0', '1'), + 'is_required' => 'bo.required', + 'price' => $priceExpr, + 'tier_price' => $tierExpr, + ] + ); + $query = $select->insertFromSelect($this->getBundleSelectionTable()); + $connection->query($query); + + $this->applyFixedBundleSelectionPrice(); + } + + /** + * Calculate selections price for dynamic bundles + * + * @param array $dimensions + * @return void + * @throws \Exception + */ + private function calculateDynamicBundleSelectionPrice($dimensions) + { + $connection = $this->getConnection(); + + $price = 'idx.min_price * bs.selection_qty'; + $specialExpr = $connection->getCheckSql( + 'i.special_price > 0 AND i.special_price < 100', + 'ROUND(' . $price . ' * (i.special_price / 100), 4)', + $price + ); + $tierExpr = $connection->getCheckSql( + 'i.tier_percent IS NOT NULL', + 'ROUND((1 - i.tier_percent / 100) * ' . $price . ', 4)', + 'NULL' + ); + $priceExpr = $connection->getLeastSql( + [ + $specialExpr, + $connection->getIfNullSql($tierExpr, $price), + ] + ); + + $select = $this->getBaseBundleSelectionPriceSelect(); + $select->join( ['idx' => $this->getMainTable($dimensions)], 'bs.product_id = idx.entity_id AND i.customer_group_id = idx.customer_group_id' . ' AND i.website_id = idx.website_id', [] - )->join( - ['e' => $this->getTable('catalog_product_entity')], - 'bs.product_id = e.entity_id AND e.required_options=0', - [] )->where( 'i.price_type=?', - $priceType + \Magento\Bundle\Model\Product\Price::PRICE_TYPE_DYNAMIC )->columns( [ 'group_type' => $connection->getCheckSql("bo.type = 'select' OR bo.type = 'radio'", '0', '1'), @@ -542,7 +627,6 @@ private function calculateBundleSelectionPrice($dimensions, $priceType) 'tier_price' => $tierExpr, ] ); - $query = $select->insertFromSelect($this->getBundleSelectionTable()); $connection->query($query); } diff --git a/app/code/Magento/Captcha/etc/adminhtml/system.xml b/app/code/Magento/Captcha/etc/adminhtml/system.xml index a05430989418d..ac4197c976ea0 100644 --- a/app/code/Magento/Captcha/etc/adminhtml/system.xml +++ b/app/code/Magento/Captcha/etc/adminhtml/system.xml @@ -8,34 +8,34 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="admin"> - <group id="captcha" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="captcha" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="1"> <label>CAPTCHA</label> - <field id="enable" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="enable" translate="label" type="select" sortOrder="1" showInDefault="1" canRestore="1"> <label>Enable CAPTCHA in Admin</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="font" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="font" translate="label" type="select" sortOrder="2" showInDefault="1" canRestore="1"> <label>Font</label> <source_model>Magento\Captcha\Model\Config\Font</source_model> <depends> <field id="enable">1</field> </depends> </field> - <field id="forms" translate="label" type="multiselect" sortOrder="3" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="forms" translate="label" type="multiselect" sortOrder="3" showInDefault="1" canRestore="1"> <label>Forms</label> <source_model>Magento\Captcha\Model\Config\Form\Backend</source_model> <depends> <field id="enable">1</field> </depends> </field> - <field id="mode" translate="label" type="select" sortOrder="4" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="mode" translate="label" type="select" sortOrder="4" showInDefault="1" canRestore="1"> <label>Displaying Mode</label> <source_model>Magento\Captcha\Model\Config\Mode</source_model> <depends> <field id="enable">1</field> </depends> </field> - <field id="failed_attempts_login" translate="label comment" type="text" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="failed_attempts_login" translate="label comment" type="text" sortOrder="5" showInDefault="1" canRestore="1"> <label>Number of Unsuccessful Attempts to Login</label> <comment>If 0 is specified, CAPTCHA on the Login form will be always available.</comment> <depends> @@ -44,14 +44,14 @@ </depends> <frontend_class>required-entry validate-digits</frontend_class> </field> - <field id="timeout" translate="label" type="text" sortOrder="6" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="timeout" translate="label" type="text" sortOrder="6" showInDefault="1" canRestore="1"> <label>CAPTCHA Timeout (minutes)</label> <depends> <field id="enable">1</field> </depends> <frontend_class>required-entry validate-digits</frontend_class> </field> - <field id="length" translate="label comment" type="text" sortOrder="7" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="length" translate="label comment" type="text" sortOrder="7" showInDefault="1" canRestore="1"> <label>Number of Symbols</label> <comment>Please specify 8 symbols at the most. Range allowed (e.g. 3-5)</comment> <depends> @@ -59,7 +59,7 @@ </depends> <frontend_class>required-entry</frontend_class> </field> - <field id="symbols" translate="label comment" type="text" sortOrder="8" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="symbols" translate="label comment" type="text" sortOrder="8" showInDefault="1" canRestore="1"> <label>Symbols Used in CAPTCHA</label> <comment> <![CDATA[Please use only letters (a-z or A-Z) or numbers (0-9) in this field. No spaces or other characters are allowed.<br />Similar looking characters (e.g. "i", "l", "1") decrease chance of correct recognition by customer.]]> @@ -69,7 +69,7 @@ </depends> <frontend_class>required-entry validate-alphanum</frontend_class> </field> - <field id="case_sensitive" translate="label" type="select" sortOrder="9" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="case_sensitive" translate="label" type="select" sortOrder="9" showInDefault="1" canRestore="1"> <label>Case Sensitive</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <depends> @@ -79,20 +79,20 @@ </group> </section> <section id="customer"> - <group id="captcha" translate="label" type="text" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="captcha" translate="label" type="text" sortOrder="110" showInDefault="1" showInWebsite="1"> <label>CAPTCHA</label> - <field id="enable" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="enable" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enable CAPTCHA on Storefront</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="font" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="font" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Font</label> <source_model>Magento\Captcha\Model\Config\Font</source_model> <depends> <field id="enable">1</field> </depends> </field> - <field id="forms" translate="label comment" type="multiselect" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="forms" translate="label comment" type="multiselect" sortOrder="3" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Forms</label> <source_model>Magento\Captcha\Model\Config\Form\Frontend</source_model> <comment>CAPTCHA for "Create user" and "Forgot password" forms is always enabled if chosen.</comment> @@ -100,14 +100,14 @@ <field id="enable">1</field> </depends> </field> - <field id="mode" translate="label" type="select" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="mode" translate="label" type="select" sortOrder="4" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Displaying Mode</label> <source_model>Magento\Captcha\Model\Config\Mode</source_model> <depends> <field id="enable">1</field> </depends> </field> - <field id="failed_attempts_login" translate="label comment" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="failed_attempts_login" translate="label comment" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Number of Unsuccessful Attempts to Login</label> <comment>If 0 is specified, CAPTCHA on the Login form will be always available.</comment> <depends> @@ -116,14 +116,14 @@ </depends> <frontend_class>required-entry validate-digits</frontend_class> </field> - <field id="timeout" translate="label" type="text" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="timeout" translate="label" type="text" sortOrder="6" showInDefault="1" showInWebsite="1" canRestore="1"> <label>CAPTCHA Timeout (minutes)</label> <depends> <field id="enable">1</field> </depends> <frontend_class>required-entry validate-digits</frontend_class> </field> - <field id="length" translate="label comment" type="text" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="length" translate="label comment" type="text" sortOrder="7" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Number of Symbols</label> <comment>Please specify 8 symbols at the most. Range allowed (e.g. 3-5)</comment> <depends> @@ -131,7 +131,7 @@ </depends> <frontend_class>required-entry validate-range range-1-8</frontend_class> </field> - <field id="symbols" translate="label comment" type="text" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="symbols" translate="label comment" type="text" sortOrder="8" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Symbols Used in CAPTCHA</label> <comment> <![CDATA[Please use only letters (a-z or A-Z) or numbers (0-9) in this field. No spaces or other characters are allowed.<br />Similar looking characters (e.g. "i", "l", "1") decrease chance of correct recognition by customer.]]> @@ -141,7 +141,7 @@ </depends> <frontend_class>required-entry validate-alphanum</frontend_class> </field> - <field id="case_sensitive" translate="label" type="select" sortOrder="9" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="case_sensitive" translate="label" type="select" sortOrder="9" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Case Sensitive</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <depends> diff --git a/app/code/Magento/CardinalCommerce/etc/adminhtml/system.xml b/app/code/Magento/CardinalCommerce/etc/adminhtml/system.xml index 046475baba676..6312696ba97e0 100644 --- a/app/code/Magento/CardinalCommerce/etc/adminhtml/system.xml +++ b/app/code/Magento/CardinalCommerce/etc/adminhtml/system.xml @@ -11,11 +11,11 @@ <label>3D Secure</label> <tab>sales</tab> <resource>Magento_Sales::three_d_secure</resource> - <group id="cardinal" type="text" sortOrder="13" showInDefault="1" showInWebsite="1" showInStore="0"> - <group id="config" translate="label comment" sortOrder="15" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="cardinal" type="text" sortOrder="13" showInDefault="1" showInWebsite="1"> + <group id="config" translate="label comment" sortOrder="15" showInDefault="1" showInWebsite="1"> <label>CardinalCommerce</label> <comment><![CDATA[Please visit <a href="https://www.cardinalcommerce.com/" target="_blank">www.cardinalcommerce.com</a> to get the CardinalCommerce credentials and find out more details about PSD2 SCA requirements. For support contact <a href="mailto:support@cardinalcommerce.com">support@cardinalcommerce.com</a>.]]></comment> - <field id="environment" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="environment" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1"> <label>Environment</label> <source_model>Magento\CardinalCommerce\Model\Adminhtml\Source\Environment</source_model> <config_path>three_d_secure/cardinal/environment</config_path> @@ -23,7 +23,7 @@ <field id="enabled_authorize">1</field> </depends> </field> - <field id="org_unit_id" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="org_unit_id" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1"> <label>Org Unit Id</label> <config_path>three_d_secure/cardinal/org_unit_id</config_path> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> @@ -31,7 +31,7 @@ <field id="enabled_authorize">1</field> </depends> </field> - <field id="api_key" translate="label" type="obscure" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="api_key" translate="label" type="obscure" sortOrder="40" showInDefault="1" showInWebsite="1"> <label>API Key</label> <config_path>three_d_secure/cardinal/api_key</config_path> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> @@ -39,7 +39,7 @@ <field id="enabled_authorize">1</field> </depends> </field> - <field id="api_identifier" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="api_identifier" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1"> <label>API Identifier</label> <config_path>three_d_secure/cardinal/api_identifier</config_path> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> @@ -47,7 +47,7 @@ <field id="enabled_authorize">1</field> </depends> </field> - <field id="debug" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="debug" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1"> <label>Debug</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>three_d_secure/cardinal/debug</config_path> diff --git a/app/code/Magento/Catalog/Controller/Category/View.php b/app/code/Magento/Catalog/Controller/Category/View.php index eea448e0fdb0b..552af244f0097 100644 --- a/app/code/Magento/Catalog/Controller/Category/View.php +++ b/app/code/Magento/Catalog/Controller/Category/View.php @@ -9,6 +9,7 @@ use Magento\Catalog\Api\CategoryRepositoryInterface; use Magento\Catalog\Helper\Category as CategoryHelper; use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Category\Attribute\LayoutUpdateManager; use Magento\Catalog\Model\Design; use Magento\Catalog\Model\Layer\Resolver; use Magento\Catalog\Model\Product\ProductList\ToolbarMemorizer; @@ -30,7 +31,6 @@ use Magento\Framework\View\Result\PageFactory; use Magento\Store\Model\StoreManagerInterface; use Psr\Log\LoggerInterface; -use Magento\Catalog\Model\Category\Attribute\LayoutUpdateManager; /** * View a category on storefront. Needs to be accessible by POST because of the store switching. @@ -208,8 +208,10 @@ protected function _initCategory() * @return ResultInterface * @throws NoSuchEntityException */ - public function execute() + public function execute(): ?ResultInterface { + $result = null; + if ($this->_request->getParam(ActionInterface::PARAM_NAME_URL_ENCODED)) { return $this->resultRedirectFactory->create()->setUrl($this->_redirect->getRedirectUrl()); } @@ -239,6 +241,7 @@ public function execute() $page->addPageLayoutHandles(['type' => $parentPageType], null, false); } $page->addPageLayoutHandles(['type' => $pageType], null, false); + $page->addPageLayoutHandles(['displaymode' => strtolower($category->getDisplayMode())], null, false); $page->addPageLayoutHandles(['id' => $category->getId()]); // apply custom layout update once layout is loaded @@ -250,8 +253,9 @@ public function execute() return $page; } elseif (!$this->getResponse()->isRedirect()) { - return $this->resultForwardFactory->create()->forward('noroute'); + $result = $this->resultForwardFactory->create()->forward('noroute'); } + return $result; } /** diff --git a/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php b/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php index c0f4e83ef3de4..ecdb3b2829b90 100644 --- a/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php +++ b/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php @@ -132,9 +132,10 @@ private function getWatermark(string $type, int $scopeId = null): array if ($file) { $size = explode( 'x', - $this->scopeConfig->getValue( + (string) $this->scopeConfig->getValue( "design/watermark/{$type}_size", - ScopeInterface::SCOPE_STORE + ScopeInterface::SCOPE_STORE, + $scopeId ) ); $opacity = $this->scopeConfig->getValue( diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php b/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php index f3984b4a35f1a..e1c90017327cd 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php @@ -395,7 +395,7 @@ public function getApplyTo() /** * Retrieve source model * - * @return \Magento\Eav\Model\Entity\Attribute\Source\AbstractSource + * @return \Magento\Eav\Model\Entity\Attribute\Source\AbstractSource|string|null */ public function getSourceModel() { diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml index 1c9cdad681d98..44b4e60973907 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml @@ -33,8 +33,9 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> - <deleteData createDataKey="attribute" stepKey="deleteAttribute"/> <deleteData createDataKey="product" stepKey="deleteProduct"/> + <deleteData createDataKey="attribute" stepKey="deleteAttribute"/> + <magentoCLI command="cron:run --group=index" stepKey="runCron"/> <actionGroup ref="logout" stepKey="logout"/> </after> <!-- Assert attribute presence in storefront product additional information --> diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Category/ViewTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Category/ViewTest.php index b826f25d7c591..595f81cc4ecd3 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Category/ViewTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Category/ViewTest.php @@ -178,7 +178,13 @@ protected function setUp() ); } - public function testApplyCustomLayoutUpdate() + /** + * Apply custom layout update is correct + * + * @dataProvider getInvocationData + * @return void + */ + public function testApplyCustomLayoutUpdate(array $expectedData): void { $categoryId = 123; $pageLayout = 'page_layout'; @@ -199,11 +205,70 @@ public function testApplyCustomLayoutUpdate() \Magento\Framework\DataObject::class, ['getPageLayout', 'getLayoutUpdates'] ); + $this->category->expects($this->at(1)) + ->method('hasChildren') + ->willReturn(true); + $this->category->expects($this->at(2)) + ->method('hasChildren') + ->willReturn($expectedData[1][0]['type'] === 'default' ? true : false); + $this->category->expects($this->once()) + ->method('getDisplayMode') + ->willReturn($expectedData[2][0]['displaymode']); + $this->expectationForPageLayoutHandles($expectedData); $settings->expects($this->atLeastOnce())->method('getPageLayout')->will($this->returnValue($pageLayout)); $settings->expects($this->once())->method('getLayoutUpdates')->willReturn(['update1', 'update2']); - $this->catalogDesign->expects($this->any())->method('getDesignSettings')->will($this->returnValue($settings)); $this->action->execute(); } + + /** + * Expected invocation for Layout Handles + * + * @param array $data + * @return void + */ + private function expectationForPageLayoutHandles($data): void + { + $index = 1; + + foreach ($data as $expectedData) { + $this->page->expects($this->at($index)) + ->method('addPageLayoutHandles') + ->with($expectedData[0], $expectedData[1], $expectedData[2]); + $index++; + } + } + + /** + * Data provider for execute method. + * + * @return array + */ + public function getInvocationData(): array + { + return [ + [ + 'layoutHandles' => [ + [['type' => 'default'], null, false], + [['type' => 'default_without_children'], null, false], + [['displaymode' => 'products'], null, false] + ] + ], + [ + 'layoutHandles' => [ + [['type' => 'default'], null, false], + [['type' => 'default_without_children'], null, false], + [['displaymode' => 'page'], null, false] + ] + ], + [ + 'layoutHandles' => [ + [['type' => 'default'], null, false], + [['type' => 'default'], null, false], + [['displaymode' => 'poducts_and_page'], null, false] + ] + ] + ]; + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Image/ParamsBuilderTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Image/ParamsBuilderTest.php index 22e3a88574e03..68239c8fa8aed 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Image/ParamsBuilderTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Image/ParamsBuilderTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Catalog\Test\Unit\Model\Product\Image; @@ -11,10 +12,15 @@ use Magento\Framework\App\Area; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Config\View; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\View\ConfigInterface; use Magento\Store\Model\ScopeInterface; +use PHPUnit\Framework\TestCase; -class ParamsBuilderTest extends \PHPUnit\Framework\TestCase +/** + * Test product image params builder + */ +class ParamsBuilderTest extends TestCase { /** * @var ScopeConfigInterface @@ -30,10 +36,17 @@ class ParamsBuilderTest extends \PHPUnit\Framework\TestCase * @var ParamsBuilder */ private $model; + /** + * @var array + */ + private $scopeConfigData = []; + /** + * @inheritDoc + */ protected function setUp() { - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManager = new ObjectManager($this); $this->scopeConfig = $this->createMock(ScopeConfigInterface::class); $this->viewConfig = $this->createMock(ConfigInterface::class); $this->model = $objectManager->getObject( @@ -43,28 +56,37 @@ protected function setUp() 'viewConfig' => $this->viewConfig, ] ); + $this->scopeConfigData = []; + $this->scopeConfig->method('getValue') + ->willReturnCallback( + function ($path, $scopeType, $scopeCode) { + return $this->scopeConfigData[$path][$scopeType][$scopeCode] ?? null; + } + ); } /** - * Test watermark location. + * Test build() with different parameters and config values + * + * @param int $scopeId + * @param array $config + * @param array $imageArguments + * @param array $expected + * @dataProvider buildDataProvider */ - public function testWatermarkLocation() + public function testBuild(int $scopeId, array $config, array $imageArguments, array $expected) { - $imageArguments = [ - 'type' => 'type', - 'height' => 'image_height', - 'width' => 'image_width', - 'angle' => 'angle', - 'background' => [1, 2, 3] + $this->scopeConfigData[Image::XML_PATH_JPEG_QUALITY][ScopeConfigInterface::SCOPE_TYPE_DEFAULT][null] = 80; + foreach ($config as $path => $value) { + $this->scopeConfigData[$path][ScopeInterface::SCOPE_STORE][$scopeId] = $value; + } + $imageArguments += [ + 'type' => 'image', + 'height' => '600', + 'width' => '400', + 'angle' => '45', + 'background' => [110, 64, 224] ]; - $scopeId = 1; - $quality = 100; - $file = 'file'; - $width = 'width'; - $height = 'height'; - $size = "{$width}x{$height}"; - $opacity = 'opacity'; - $position = 'position'; $viewMock = $this->createMock(View::class); $viewMock->expects($this->once()) @@ -77,51 +99,16 @@ public function testWatermarkLocation() ->with(['area' => Area::AREA_FRONTEND]) ->willReturn($viewMock); - $this->scopeConfig->expects($this->exactly(5))->method('getValue')->withConsecutive( - [ - Image::XML_PATH_JPEG_QUALITY - ], - [ - "design/watermark/{$imageArguments['type']}_image", - ScopeInterface::SCOPE_STORE, - $scopeId, - ], - [ - "design/watermark/{$imageArguments['type']}_size", - ScopeInterface::SCOPE_STORE], - [ - "design/watermark/{$imageArguments['type']}_imageOpacity", - ScopeInterface::SCOPE_STORE, - $scopeId - ], - [ - "design/watermark/{$imageArguments['type']}_position", - ScopeInterface::SCOPE_STORE, - $scopeId - ] - )->willReturnOnConsecutiveCalls( - $quality, - $file, - $size, - $opacity, - $position - ); - $actual = $this->model->build($imageArguments, $scopeId); - $expected = [ + $expected += [ 'image_type' => $imageArguments['type'], 'background' => $imageArguments['background'], 'angle' => $imageArguments['angle'], - 'quality' => $quality, + 'quality' => 80, 'keep_aspect_ratio' => true, 'keep_frame' => true, 'keep_transparency' => true, 'constrain_only' => true, - 'watermark_file' => $file, - 'watermark_image_opacity' => $opacity, - 'watermark_position' => $position, - 'watermark_width' => $width, - 'watermark_height' => $height, 'image_height' => $imageArguments['height'], 'image_width' => $imageArguments['width'], ]; @@ -131,4 +118,50 @@ public function testWatermarkLocation() $actual ); } + + /** + * Provides test scenarios for + * + * @return array + */ + public function buildDataProvider() + { + return [ + 'watermark config' => [ + 1, + [ + 'design/watermark/small_image_image' => 'stores/1/magento-logo.png', + 'design/watermark/small_image_size' => '60x40', + 'design/watermark/small_image_imageOpacity' => '50', + 'design/watermark/small_image_position' => 'bottom-right', + ], + [ + 'type' => 'small_image' + ], + [ + 'watermark_file' => 'stores/1/magento-logo.png', + 'watermark_image_opacity' => '50', + 'watermark_position' => 'bottom-right', + 'watermark_width' => '60', + 'watermark_height' => '40', + ] + ], + 'watermark config empty' => [ + 1, + [ + 'design/watermark/small_image_image' => 'stores/1/magento-logo.png', + ], + [ + 'type' => 'small_image' + ], + [ + 'watermark_file' => 'stores/1/magento-logo.png', + 'watermark_image_opacity' => null, + 'watermark_position' => null, + 'watermark_width' => null, + 'watermark_height' => null, + ] + ] + ]; + } } diff --git a/app/code/Magento/Catalog/etc/adminhtml/system.xml b/app/code/Magento/Catalog/etc/adminhtml/system.xml index c80363038ac60..80b323cfdb250 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/system.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/system.xml @@ -20,30 +20,30 @@ <resource>Magento_Catalog::config_catalog</resource> <group id="fields_masks" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Product Fields Auto-Generation</label> - <field id="sku" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="sku" translate="label comment" type="text" sortOrder="10" showInDefault="1" canRestore="1"> <label>Mask for SKU</label> <comment>Use {{name}} as Product Name placeholder</comment> </field> - <field id="meta_title" translate="label comment" type="text" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="meta_title" translate="label comment" type="text" sortOrder="20" showInDefault="1" canRestore="1"> <label>Mask for Meta Title</label> <comment>Use {{name}} as Product Name placeholder</comment> </field> - <field id="meta_keyword" translate="label comment" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="meta_keyword" translate="label comment" type="text" sortOrder="30" showInDefault="1" canRestore="1"> <label>Mask for Meta Keywords</label> <comment>Use {{name}} as Product Name or {{sku}} as Product SKU placeholders</comment> </field> - <field id="meta_description" translate="label comment" type="text" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="meta_description" translate="label comment" type="text" sortOrder="40" showInDefault="1" canRestore="1"> <label>Mask for Meta Description</label> <comment>Use {{name}} and {{description}} as Product Name and Product Description placeholders</comment> </field> </group> - <group id="recently_products" translate="label" type="text" sortOrder="350" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="recently_products" translate="label" type="text" sortOrder="350" showInDefault="1" showInWebsite="1"> <label>Recently Viewed/Compared Products</label> - <field id="recently_viewed_lifetime" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="recently_viewed_lifetime" translate="label" type="text" sortOrder="40" showInDefault="1" canRestore="1"> <label>Lifetime of products in Recently Viewed Widget</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="recently_compared_lifetime" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="recently_compared_lifetime" translate="label" type="text" sortOrder="40" showInDefault="1" canRestore="1"> <label>Lifetime of products in Recently Compared Widget</label> <validate>validate-number validate-zero-or-greater</validate> </field> @@ -78,12 +78,12 @@ <comment>Must be in the allowed values list.</comment> <validate>validate-per-page-value</validate> </field> - <field id="flat_catalog_category" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="flat_catalog_category" translate="label" type="select" sortOrder="100" showInDefault="1" canRestore="1"> <label>Use Flat Catalog Category</label> <backend_model>Magento\Catalog\Model\Indexer\Category\Flat\System\Config\Mode</backend_model> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="flat_catalog_product" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="flat_catalog_product" translate="label" type="select" sortOrder="100" showInDefault="1" canRestore="1"> <label>Use Flat Catalog Product</label> <backend_model>Magento\Catalog\Model\Indexer\Product\Flat\System\Config\Mode</backend_model> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> @@ -93,12 +93,12 @@ <comment>Applies to category pages.</comment> <source_model>Magento\Catalog\Model\Config\Source\ListSort</source_model> </field> - <field id="list_allow_all" translate="label comment" type="select" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="list_allow_all" translate="label comment" type="select" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Allow All Products per Page</label> <comment>Whether to show "All" option in the "Show X Per Page" dropdown.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="remember_pagination" translate="label comment" type="select" sortOrder="7" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="remember_pagination" translate="label comment" type="select" sortOrder="7" showInDefault="1" canRestore="1"> <label>Remember Category Pagination</label> <comment>Changing may affect SEO and cache storage consumption.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> @@ -128,9 +128,9 @@ <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> </group> - <group id="price" translate="label" type="text" sortOrder="400" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="price" translate="label" type="text" sortOrder="400" showInDefault="1"> <label>Price</label> - <field id="scope" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="scope" translate="label comment" type="select" sortOrder="1" showInDefault="1"> <label>Catalog Price Scope</label> <comment><![CDATA[This defines the base currency scope ("Currency Setup" > "Currency Options" > "Base Currency").]]></comment> <backend_model>Magento\Catalog\Model\Indexer\Product\Price\System\Config\PriceScope</backend_model> @@ -169,10 +169,13 @@ </section> <section id="cms"> <group id="wysiwyg"> - <field id="use_static_urls_in_catalog" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="use_static_urls_in_catalog" translate="label comment" type="select" sortOrder="10" showInDefault="1"> <label>Use Static URLs for Media Content in WYSIWYG</label> <comment>Media content will be inserted into the editor as a static URL. Media content is not updated if the system configuration base URL changes.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <depends> + <field id="enabled" negative="1">disabled</field> + </depends> </field> </group> </section> @@ -213,7 +216,7 @@ <resource>Magento_Config::config_system</resource> <group id="upload_configuration" translate="label" type="text" sortOrder="1000" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Images Upload Configuration</label> - <field id="jpeg_quality" translate="label comment" type="text" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="jpeg_quality" translate="label comment" type="text" sortOrder="100" showInDefault="1" canRestore="1"> <label>Quality</label> <validate>validate-digits validate-digits-range digits-range-1-100 required-entry</validate> <comment>Jpeg quality for resized images 1-100%.</comment> diff --git a/app/code/Magento/Catalog/etc/config.xml b/app/code/Magento/Catalog/etc/config.xml index 8506d2ae03032..59fc4b6d947d9 100644 --- a/app/code/Magento/Catalog/etc/config.xml +++ b/app/code/Magento/Catalog/etc/config.xml @@ -28,7 +28,9 @@ <grid_per_page>12</grid_per_page> <list_per_page>10</list_per_page> <flat_catalog_category>0</flat_catalog_category> + <flat_catalog_product>0</flat_catalog_product> <default_sort_by>position</default_sort_by> + <list_allow_all>0</list_allow_all> <parse_url_directives>1</parse_url_directives> <remember_pagination>0</remember_pagination> </frontend> diff --git a/app/code/Magento/Catalog/view/base/web/template/product/list/listing.html b/app/code/Magento/Catalog/view/base/web/template/product/list/listing.html index c80f591bd6590..ea0124f1c7c4d 100644 --- a/app/code/Magento/Catalog/view/base/web/template/product/list/listing.html +++ b/app/code/Magento/Catalog/view/base/web/template/product/list/listing.html @@ -25,15 +25,15 @@ <render args="$col.getBody()"/> </fastForEach> - <div if="getRegion('action-primary-area')().length || getRegion('action-secondary-area')().length" + <div if="regionHasElements('action-primary-area') || regionHasElements('action-secondary-area')" class="product-item-actions"> - <div class="actions-primary" if="getRegion('action-primary-area')().length"> + <div class="actions-primary" if="regionHasElements('action-primary-area')"> <fastForEach args="data: getRegion('action-primary-area'), as: '$col'" > <render args="$col.getBody()"/> </fastForEach> </div> - <div if="getRegion('action-secondary-area')().length" + <div if="regionHasElements('action-secondary-area')" class="actions-secondary" data-role="add-to-links"> <fastForEach args="data: getRegion('action-secondary-area'), as: '$col'" > @@ -42,7 +42,7 @@ </div> </div> - <div if="getRegion('description-area')().length" + <div if="regionHasElements('description-area')" class="product-item-description"> <fastForEach args="data: getRegion('description-area'), as: '$col'" > <render args="$col.getBody()"/> @@ -54,4 +54,4 @@ </ol> </div> </div> -</div> \ No newline at end of file +</div> diff --git a/app/code/Magento/CatalogInventory/etc/adminhtml/system.xml b/app/code/Magento/CatalogInventory/etc/adminhtml/system.xml index 546f838b9b428..a5a8476b20a02 100644 --- a/app/code/Magento/CatalogInventory/etc/adminhtml/system.xml +++ b/app/code/Magento/CatalogInventory/etc/adminhtml/system.xml @@ -13,21 +13,21 @@ <resource>Magento_CatalogInventory::cataloginventory</resource> <group id="options" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Stock Options</label> - <field id="can_subtract" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="can_subtract" translate="label" type="select" sortOrder="2" showInDefault="1" canRestore="1"> <label>Decrease Stock When Order is Placed</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="can_back_in_stock" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="can_back_in_stock" translate="label" type="select" sortOrder="2" showInDefault="1" canRestore="1"> <label>Set Items' Status to be In Stock When Order is Cancelled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="show_out_of_stock" translate="label comment" type="select" sortOrder="3" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="show_out_of_stock" translate="label comment" type="select" sortOrder="3" showInDefault="1" canRestore="1"> <label>Display Out of Stock Products</label> <comment>Products will still be shown by direct product URLs.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\CatalogInventory\Model\Config\Backend\ShowOutOfStock</backend_model> </field> - <field id="stock_threshold_qty" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="stock_threshold_qty" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Only X left Threshold</label> <validate>validate-number</validate> </field> @@ -41,45 +41,45 @@ <![CDATA[Please note that these settings apply to individual items in the cart, not to the entire cart.]]> </comment> <label>Product Stock Options</label> - <field id="manage_stock" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="manage_stock" translate="label comment" type="select" sortOrder="1" showInDefault="1" canRestore="1"> <label>Manage Stock</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\CatalogInventory\Model\Config\Backend\Managestock</backend_model> <comment>Changing can take some time due to processing whole catalog.</comment> </field> - <field id="backorders" translate="label comment" type="select" sortOrder="3" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="backorders" translate="label comment" type="select" sortOrder="3" showInDefault="1" canRestore="1"> <label>Backorders</label> <source_model>Magento\CatalogInventory\Model\Source\Backorders</source_model> <backend_model>Magento\CatalogInventory\Model\Config\Backend\Backorders</backend_model> <comment>Changing can take some time due to processing whole catalog.</comment> </field> - <field id="max_sale_qty" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="max_sale_qty" translate="label" type="text" sortOrder="4" showInDefault="1" canRestore="1"> <label>Maximum Qty Allowed in Shopping Cart</label> <validate>validate-number validate-greater-than-zero</validate> </field> - <field id="min_qty" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="min_qty" translate="label" type="text" sortOrder="5" showInDefault="1" canRestore="1"> <label>Out-of-Stock Threshold</label> <validate>validate-number</validate> <backend_model>Magento\CatalogInventory\Model\System\Config\Backend\Minqty</backend_model> </field> - <field id="min_sale_qty" translate="label" sortOrder="6" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="min_sale_qty" translate="label" sortOrder="6" showInDefault="1" canRestore="1"> <label>Minimum Qty Allowed in Shopping Cart</label> <frontend_model>Magento\CatalogInventory\Block\Adminhtml\Form\Field\Minsaleqty</frontend_model> <backend_model>Magento\CatalogInventory\Model\System\Config\Backend\Minsaleqty</backend_model> </field> - <field id="notify_stock_qty" translate="label" type="text" sortOrder="7" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="notify_stock_qty" translate="label" type="text" sortOrder="7" showInDefault="1" canRestore="1"> <label>Notify for Quantity Below</label> <validate>validate-number</validate> </field> - <field id="auto_return" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="auto_return" translate="label" type="select" sortOrder="10" showInDefault="1" canRestore="1"> <label>Automatically Return Credit Memo Item to Stock</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="enable_qty_increments" translate="label" type="select" sortOrder="8" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="enable_qty_increments" translate="label" type="select" sortOrder="8" showInDefault="1" canRestore="1"> <label>Enable Qty Increments</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="qty_increments" translate="label" type="text" sortOrder="9" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="qty_increments" translate="label" type="text" sortOrder="9" showInDefault="1" canRestore="1"> <label>Qty Increments</label> <validate>validate-number validate-greater-than-zero</validate> <backend_model>Magento\CatalogInventory\Model\System\Config\Backend\Qtyincrements</backend_model> diff --git a/app/code/Magento/CatalogInventory/etc/config.xml b/app/code/Magento/CatalogInventory/etc/config.xml index 976b3f4cad510..21641b3a4cfcd 100644 --- a/app/code/Magento/CatalogInventory/etc/config.xml +++ b/app/code/Magento/CatalogInventory/etc/config.xml @@ -22,6 +22,7 @@ <min_sale_qty>1</min_sale_qty> <min_qty>0</min_qty> <notify_stock_qty>1</notify_stock_qty> + <auto_return>0</auto_return> <enable_qty_increments>0</enable_qty_increments> <qty_increments>1</qty_increments> </item_options> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml index 514366fa45c52..1950f060790d7 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml @@ -17,6 +17,9 @@ <testCaseId value="MC-14770"/> <group value="CatalogRule"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-24172"/> + </skip> </annotations> <before> <!-- Login as Admin --> diff --git a/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml b/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml index c358062b88a41..e875a48aa29dc 100644 --- a/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml +++ b/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml @@ -38,7 +38,7 @@ <label>Autocomplete Limit</label> <validate>validate-digits</validate> </field> - <field id="enable_eav_indexer" translate="label" type="select" sortOrder="18" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="enable_eav_indexer" translate="label" type="select" sortOrder="18" showInDefault="1" canRestore="1"> <label>Enable EAV Indexer</label> <comment>Enable/Disable Product EAV indexer to improve indexation speed. Make sure that indexer is not used by 3rd party extensions.</comment> <depends> diff --git a/app/code/Magento/CatalogUrlRewrite/etc/adminhtml/system.xml b/app/code/Magento/CatalogUrlRewrite/etc/adminhtml/system.xml index ad0ff68192af4..75d395473f969 100644 --- a/app/code/Magento/CatalogUrlRewrite/etc/adminhtml/system.xml +++ b/app/code/Magento/CatalogUrlRewrite/etc/adminhtml/system.xml @@ -28,7 +28,7 @@ <label>Create Permanent Redirect for URLs if URL Key Changed</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="generate_category_product_rewrites" translate="label comment" type="select" sortOrder="6" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="generate_category_product_rewrites" translate="label comment" type="select" sortOrder="6" showInDefault="1" canRestore="1"> <label>Generate "category/product" URL Rewrites</label> <backend_model>Magento\CatalogUrlRewrite\Model\TableCleaner</backend_model> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> diff --git a/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php b/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php index a712ae91cbfa9..eca994de0892f 100644 --- a/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php +++ b/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php @@ -14,7 +14,8 @@ use Magento\Store\Model\Store; /** - * Class Product + * Rule product condition data model + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Product extends \Magento\Rule\Model\Condition\Product\AbstractProduct @@ -250,7 +251,7 @@ protected function addNotGlobalAttribute( public function getMappedSqlField() { $result = ''; - if (in_array($this->getAttribute(), ['category_ids', 'sku'])) { + if (in_array($this->getAttribute(), ['category_ids', 'sku', 'attribute_set_id'])) { $result = parent::getMappedSqlField(); } elseif (isset($this->joinedAttributes[$this->getAttribute()])) { $result = $this->joinedAttributes[$this->getAttribute()]; diff --git a/app/code/Magento/CatalogWidget/Test/Unit/Model/Rule/Condition/ProductTest.php b/app/code/Magento/CatalogWidget/Test/Unit/Model/Rule/Condition/ProductTest.php index 219cae6829299..7dceb41d263ec 100644 --- a/app/code/Magento/CatalogWidget/Test/Unit/Model/Rule/Condition/ProductTest.php +++ b/app/code/Magento/CatalogWidget/Test/Unit/Model/Rule/Condition/ProductTest.php @@ -5,53 +5,65 @@ */ namespace Magento\CatalogWidget\Test\Unit\Model\Rule\Condition; +use Magento\Catalog\Model\ProductCategoryList; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +use Magento\Catalog\Model\ResourceModel\Product; +use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\CatalogWidget\Model\Rule\Condition\Product as ProductWidget; +use Magento\Eav\Model\Config; +use Magento\Eav\Model\Entity\AbstractEntity; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Select; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\SalesRule\Model\Rule; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class ProductTest extends \PHPUnit\Framework\TestCase +class ProductTest extends TestCase { /** - * @var \Magento\CatalogWidget\Model\Rule\Condition\Product + * @var ProductWidget */ private $model; /** - * @var \Magento\Catalog\Model\ResourceModel\Product|\PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ - private $productResource; + private $attributeMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Product|MockObject */ - private $attributeMock; + private $productResource; /** * @inheritdoc - * - * @return void */ protected function setUp() { $objectManagerHelper = new ObjectManager($this); - $eavConfig = $this->createMock(\Magento\Eav\Model\Config::class); - $this->attributeMock = $this->createMock(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class); + $eavConfig = $this->createMock(Config::class); + $this->attributeMock = $this->createMock(Attribute::class); $eavConfig->expects($this->any())->method('getAttribute')->willReturn($this->attributeMock); - $ruleMock = $this->createMock(\Magento\SalesRule\Model\Rule::class); - $storeManager = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); - $storeMock = $this->createMock(\Magento\Store\Api\Data\StoreInterface::class); + $ruleMock = $this->createMock(Rule::class); + $storeManager = $this->createMock(StoreManagerInterface::class); + $storeMock = $this->createMock(StoreInterface::class); $storeManager->expects($this->any())->method('getStore')->willReturn($storeMock); - $this->productResource = $this->createMock(\Magento\Catalog\Model\ResourceModel\Product::class); + $this->productResource = $this->createMock(Product::class); $this->productResource->expects($this->once())->method('loadAllAttributes')->willReturnSelf(); $this->productResource->expects($this->once())->method('getAttributesByCode')->willReturn([]); - $productCategoryList = $this->getMockBuilder(\Magento\Catalog\Model\ProductCategoryList::class) + $productCategoryList = $this->getMockBuilder(ProductCategoryList::class) ->disableOriginalConstructor() ->getMock(); $this->model = $objectManagerHelper->getObject( - \Magento\CatalogWidget\Model\Rule\Condition\Product::class, + ProductWidget::class, [ 'config' => $eavConfig, 'storeManager' => $storeManager, @@ -72,8 +84,8 @@ protected function setUp() */ public function testAddToCollection() { - $collectionMock = $this->createMock(\Magento\Catalog\Model\ResourceModel\Product\Collection::class); - $selectMock = $this->createMock(\Magento\Framework\DB\Select::class); + $collectionMock = $this->createMock(Collection::class); + $selectMock = $this->createMock(Select::class); $collectionMock->expects($this->once())->method('getSelect')->willReturn($selectMock); $selectMock->expects($this->any())->method('join')->willReturnSelf(); $this->attributeMock->expects($this->any())->method('getAttributeCode')->willReturn('code'); @@ -83,10 +95,10 @@ public function testAddToCollection() $this->attributeMock->expects($this->once())->method('isScopeGlobal')->willReturn(true); $this->attributeMock->expects($this->once())->method('getBackendType')->willReturn('multiselect'); - $entityMock = $this->createMock(\Magento\Eav\Model\Entity\AbstractEntity::class); + $entityMock = $this->createMock(AbstractEntity::class); $entityMock->expects($this->once())->method('getLinkField')->willReturn('entitiy_id'); $this->attributeMock->expects($this->once())->method('getEntity')->willReturn($entityMock); - $connection = $this->createMock(\Magento\Framework\DB\Adapter\AdapterInterface::class); + $connection = $this->createMock(AdapterInterface::class); $this->productResource->expects($this->atLeastOnce())->method('getConnection')->willReturn($connection); @@ -102,5 +114,7 @@ public function testGetMappedSqlFieldSku() { $this->model->setAttribute('sku'); $this->assertEquals('e.sku', $this->model->getMappedSqlField()); + $this->model->setAttribute('attribute_set_id'); + $this->assertEquals('e.attribute_set_id', $this->model->getMappedSqlField()); } } diff --git a/app/code/Magento/Checkout/etc/adminhtml/system.xml b/app/code/Magento/Checkout/etc/adminhtml/system.xml index 399474a36bfc7..7454c2b6524f3 100644 --- a/app/code/Magento/Checkout/etc/adminhtml/system.xml +++ b/app/code/Magento/Checkout/etc/adminhtml/system.xml @@ -21,7 +21,7 @@ <label>Allow Guest Checkout</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="display_billing_address_on" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="display_billing_address_on" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Display Billing Address On</label> <source_model>\Magento\Checkout\Model\Adminhtml\BillingAddressDisplayOptions</source_model> </field> @@ -32,7 +32,7 @@ </group> <group id="cart" translate="label" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Shopping Cart</label> - <field id="delete_quote_after" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="delete_quote_after" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Quote Lifetime (days)</label> <validate>validate-zero-or-greater validate-digits</validate> </field> @@ -49,9 +49,9 @@ <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> </group> - <group id="cart_link" translate="label" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="cart_link" translate="label" sortOrder="3" showInDefault="1" showInWebsite="1"> <label>My Cart Link</label> - <field id="use_qty" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="use_qty" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Display Cart Summary</label> <source_model>Magento\Checkout\Model\Config\Source\Cart\Summary</source_model> </field> diff --git a/app/code/Magento/Checkout/etc/config.xml b/app/code/Magento/Checkout/etc/config.xml index f8c2e7ebcb503..c8408f6d902fa 100644 --- a/app/code/Magento/Checkout/etc/config.xml +++ b/app/code/Magento/Checkout/etc/config.xml @@ -11,6 +11,7 @@ <options> <onepage_checkout_enabled>1</onepage_checkout_enabled> <guest_checkout>1</guest_checkout> + <display_billing_address_on>0</display_billing_address_on> <max_items_display_count>10</max_items_display_count> </options> <cart> diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js b/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js index 4adc1cd88c0ae..c77e72e38107a 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js @@ -162,10 +162,11 @@ define([ /** * Get cart param by name. + * * @param {String} name * @returns {*} */ - getCartParam: function (name) { + getCartParamUnsanitizedHtml: function (name) { if (!_.isUndefined(name)) { if (!this.cart.hasOwnProperty(name)) { this.cart[name] = ko.observable(); @@ -175,12 +176,21 @@ define([ return this.cart[name](); }, + /** + * @deprecated please use getCartParamUnsanitizedHtml. + * @param {String} name + * @returns {*} + */ + getCartParam: function (name) { + return this.getCartParamUnsanitizedHtml(name); + }, + /** * Returns array of cart items, limited by 'maxItemsToDisplay' setting * @returns [] */ getCartItems: function () { - var items = this.getCartParam('items') || []; + var items = this.getCartParamUnsanitizedHtml('items') || []; items = items.slice(parseInt(-this.maxItemsToDisplay, 10)); @@ -192,7 +202,7 @@ define([ * @returns {Number} */ getCartLineItemsCount: function () { - var items = this.getCartParam('items') || []; + var items = this.getCartParamUnsanitizedHtml('items') || []; return parseInt(items.length, 10); } diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/payment/list.js b/app/code/Magento/Checkout/view/frontend/web/js/view/payment/list.js index 15ae5d68534b8..17ccf3863a875 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/payment/list.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/payment/list.js @@ -203,7 +203,7 @@ define([ */ isPaymentMethodsAvailable: function () { return _.some(this.paymentGroupsList(), function (group) { - return this.getRegion(group.displayArea)().length; + return this.regionHasElements(group.displayArea); }, this); }, diff --git a/app/code/Magento/Checkout/view/frontend/web/template/minicart/content.html b/app/code/Magento/Checkout/view/frontend/web/template/minicart/content.html index 0719a7d01ec70..c5fd6d545702b 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/minicart/content.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/minicart/content.html @@ -56,7 +56,7 @@ " translate="'Proceed to Checkout'" /> - <div data-bind="html: getCartParam('extra_actions')"></div> + <div data-bind="html: getCartParamUnsanitizedHtml('extra_actions')"></div> </div> </div> </if> @@ -97,7 +97,7 @@ </div> </div> - <div id="minicart-widgets" class="minicart-widgets" if="getRegion('promotion').length"> + <div id="minicart-widgets" class="minicart-widgets" if="regionHasElements('promotion')"> <each args="getRegion('promotion')" render=""/> </div> </div> diff --git a/app/code/Magento/Checkout/view/frontend/web/template/payment-methods/list.html b/app/code/Magento/Checkout/view/frontend/web/template/payment-methods/list.html index b7be5efee383e..77b801ec0e826 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/payment-methods/list.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/payment-methods/list.html @@ -8,7 +8,7 @@ class="items payment-methods"> <div repeat="foreach: paymentGroupsList, item: '$group'" class="payment-group"> - <div if="getRegion($group().displayArea)().length" + <div if="regionHasElements($group().displayArea)" translate="getGroupTitle($group)" class="step-title" data-role="title"> diff --git a/app/code/Magento/CheckoutAgreements/etc/adminhtml/system.xml b/app/code/Magento/CheckoutAgreements/etc/adminhtml/system.xml index 64ffb2b5dc21a..496f3fec70e5e 100644 --- a/app/code/Magento/CheckoutAgreements/etc/adminhtml/system.xml +++ b/app/code/Magento/CheckoutAgreements/etc/adminhtml/system.xml @@ -9,7 +9,7 @@ <system> <section id="checkout"> <group id="options"> - <field id="enable_agreements" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="enable_agreements" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Enable Terms and Conditions</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> diff --git a/app/code/Magento/CheckoutAgreements/etc/config.xml b/app/code/Magento/CheckoutAgreements/etc/config.xml new file mode 100644 index 0000000000000..e2932af0c25cb --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/etc/config.xml @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> + <default> + <checkout> + <options> + <enable_agreements>0</enable_agreements> + </options> + </checkout> + </default> +</config> diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFiles.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFiles.php index 6f57efad41e75..fa873930aaade 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFiles.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFiles.php @@ -5,8 +5,8 @@ */ namespace Magento\Cms\Controller\Adminhtml\Wysiwyg\Images; -use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\App\Filesystem\DirectoryList; /** * Delete image files. @@ -60,10 +60,15 @@ public function __construct( */ public function execute() { + $resultJson = $this->resultJsonFactory->create(); + + if (!$this->getRequest()->isPost()) { + $result = ['error' => true, 'message' => __('Wrong request.')]; + /** @var \Magento\Framework\Controller\Result\Json $resultJson */ + return $resultJson->setData($result); + } + try { - if (!$this->getRequest()->isPost()) { - throw new \Exception('Wrong request.'); - } $files = $this->getRequest()->getParam('files'); /** @var $helper \Magento\Cms\Helper\Wysiwyg\Images */ @@ -79,17 +84,16 @@ public function execute() /** @var \Magento\Framework\Filesystem $filesystem */ $filesystem = $this->_objectManager->get(\Magento\Framework\Filesystem::class); $dir = $filesystem->getDirectoryRead(DirectoryList::MEDIA); - $filePath = $path . '/' . \Magento\Framework\File\Uploader::getCorrectFileName($file); + $filePath = $path . '/' . $file; if ($dir->isFile($dir->getRelativePath($filePath)) && !preg_match('#.htaccess#', $file)) { $this->getStorage()->deleteFile($filePath); } } - + return $this->resultRawFactory->create(); + // phpcs:ignore Magento2.Exceptions.ThrowCatch } catch (\Exception $e) { $result = ['error' => true, 'message' => $e->getMessage()]; - /** @var \Magento\Framework\Controller\Result\Json $resultJson */ - $resultJson = $this->resultJsonFactory->create(); return $resultJson->setData($result); } diff --git a/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php b/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php index 21f3b232e783c..f0a232bdccccc 100644 --- a/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php +++ b/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php @@ -570,7 +570,11 @@ public function getThumbnailPath($filePath, $checkFile = false) $mediaRootDir = $this->_cmsWysiwygImages->getStorageRoot(); if (strpos($filePath, (string) $mediaRootDir) === 0) { - $thumbPath = $this->getThumbnailRoot() . substr($filePath, strlen($mediaRootDir)); + $relativeFilePath = substr($filePath, strlen($mediaRootDir)); + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $thumbPath = $relativeFilePath === basename($filePath) + ? $this->getThumbnailRoot() . DIRECTORY_SEPARATOR . $relativeFilePath + : $this->getThumbnailRoot() . $relativeFilePath; if (!$checkFile || $this->_directory->isExist($this->_directory->getRelativePath($thumbPath))) { return $thumbPath; @@ -589,21 +593,12 @@ public function getThumbnailPath($filePath, $checkFile = false) */ public function getThumbnailUrl($filePath, $checkFile = false) { - $mediaRootDir = $this->_cmsWysiwygImages->getStorageRoot(); - - if (strpos($filePath, (string) $mediaRootDir) === 0) { - $thumbSuffix = self::THUMBS_DIRECTORY_NAME . substr($filePath, strlen($mediaRootDir)); - if (!$checkFile || $this->_directory->isExist( - $this->_directory->getRelativePath($mediaRootDir . '/' . $thumbSuffix) - ) - ) { - $thumbSuffix = substr( - $mediaRootDir, - strlen($this->_directory->getAbsolutePath()) - ) . '/' . $thumbSuffix; - $randomIndex = '?rand=' . time(); - return str_replace('\\', '/', $this->_cmsWysiwygImages->getBaseUrl() . $thumbSuffix) . $randomIndex; - } + $thumbPath = $this->getThumbnailPath($filePath, $checkFile); + if ($thumbPath) { + $thumbRelativePath = ltrim($this->_directory->getRelativePath($thumbPath), '/\\'); + $baseUrl = rtrim($this->_cmsWysiwygImages->getBaseUrl(), '/'); + $randomIndex = '?rand=' . time(); + return str_replace('\\', '/', $baseUrl . '/' . $thumbRelativePath) . $randomIndex; } return false; @@ -666,11 +661,13 @@ public function resizeOnTheFly($filename) */ public function getThumbsPath($filePath = false) { - $mediaRootDir = $this->_cmsWysiwygImages->getStorageRoot(); $thumbnailDir = $this->getThumbnailRoot(); - if ($filePath && strpos($filePath, (string) $mediaRootDir) === 0) { - $thumbnailDir .= $this->file->getParentDirectory(substr($filePath, strlen($mediaRootDir))); + if ($filePath) { + $thumbPath = $this->getThumbnailPath($filePath, false); + if ($thumbPath) { + $thumbnailDir = $this->file->getParentDirectory($thumbPath); + } } return $thumbnailDir; diff --git a/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php b/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php index 9a9c63195051c..f87eee62e1095 100644 --- a/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php +++ b/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php @@ -131,6 +131,7 @@ class StorageTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { + $this->objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->filesystemMock = $this->createMock(\Magento\Framework\Filesystem::class); $this->driverMock = $this->getMockBuilder(\Magento\Framework\Filesystem\DriverInterface::class) ->setMethods(['getRealPathSafety']) @@ -159,10 +160,7 @@ protected function setUp() $this->returnValue($this->directoryMock) ); - $this->fileMock = $this->createPartialMock( - \Magento\Framework\Filesystem\Driver\File::class, - ['getParentDirectory'] - ); + $this->fileMock = $this->objectManagerHelper->getObject(\Magento\Framework\Filesystem\Driver\File::class); $this->ioFileMock = $this->createPartialMock(\Magento\Framework\Filesystem\Io\File::class, ['getPathInfo']); $this->ioFileMock->expects( $this->any() @@ -233,8 +231,6 @@ function ($path) { 'image_allowed' => $this->allowedImageExtensions, ]; - $this->objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->imagesStorage = $this->objectManagerHelper->getObject( \Magento\Cms\Model\Wysiwyg\Images\Storage::class, [ @@ -525,8 +521,6 @@ public function testUploadFile() ] ); - $this->fileMock->expects($this->any())->method('getParentDirectory')->willReturn($path); - $image = $this->getMockBuilder(\Magento\Catalog\Model\Product\Image::class) ->disableOriginalConstructor() ->setMethods(['open', 'keepAspectRatio', 'resize', 'save']) diff --git a/app/code/Magento/Cms/etc/adminhtml/system.xml b/app/code/Magento/Cms/etc/adminhtml/system.xml index 20d543440565b..785d0e3710f4f 100644 --- a/app/code/Magento/Cms/etc/adminhtml/system.xml +++ b/app/code/Magento/Cms/etc/adminhtml/system.xml @@ -58,9 +58,12 @@ <label>Enable WYSIWYG Editor</label> <source_model>Magento\Cms\Model\Config\Source\Wysiwyg\Enabled</source_model> </field> - <field id="editor" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="editor" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" canRestore="1"> <label>WYSIWYG Editor</label> <source_model>Magento\Cms\Model\Config\Source\Wysiwyg\Editor</source_model> + <depends> + <field id="enabled" negative="1">disabled</field> + </depends> </field> </group> </section> diff --git a/app/code/Magento/Cms/etc/di.xml b/app/code/Magento/Cms/etc/di.xml index 35fb7d82651f0..869bd22e20548 100644 --- a/app/code/Magento/Cms/etc/di.xml +++ b/app/code/Magento/Cms/etc/di.xml @@ -116,14 +116,6 @@ <argument name="resourceModel" xsi:type="string">Magento\Cms\Model\ResourceModel\Block</argument> </arguments> </type> - <virtualType name="CmsGirdFilterPool" type="Magento\Framework\View\Element\UiComponent\DataProvider\FilterPool"> - <arguments> - <argument name="appliers" xsi:type="array"> - <item name="regular" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\RegularFilter</item> - <item name="fulltext" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\FulltextFilter</item> - </argument> - </arguments> - </virtualType> <type name="Magento\Framework\Model\Entity\RepositoryFactory"> <arguments> <argument name="entities" xsi:type="array"> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminCheckUseSystemValueActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminCheckUseSystemValueActionGroup.xml new file mode 100644 index 0000000000000..10c36ced4cee3 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminCheckUseSystemValueActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCheckUseSystemValueActionGroup"> + <arguments> + <argument name="rowId" type="string"/> + </arguments> + + <checkOption selector="{{AdminConfigSection.useSystemValue(rowId)}}" stepKey="checkUseSystemValue"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminToggleEnabledActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminToggleEnabledActionGroup.xml new file mode 100644 index 0000000000000..1fe36cf9ae390 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminToggleEnabledActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminToggleEnabledActionGroup"> + <arguments> + <argument name="element" type="string"/> + <argument name="state" type="string" defaultValue="Yes"/> + </arguments> + <selectOption selector="{{element}}" userInput="{{state}}" stepKey="switchActiveState"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminUncheckUseSystemValueActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminUncheckUseSystemValueActionGroup.xml new file mode 100644 index 0000000000000..25ecd6fe09a27 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminUncheckUseSystemValueActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminUncheckUseSystemValueActionGroup"> + <arguments> + <argument name="rowId" type="string"/> + </arguments> + + <uncheckOption selector="{{AdminConfigSection.useSystemValue(rowId)}}" stepKey="uncheckUseSystemValue"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/AssertAdminValidationErrorActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/AssertAdminValidationErrorActionGroup.xml new file mode 100644 index 0000000000000..768eefd26a4f9 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/AssertAdminValidationErrorActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminValidationErrorActionGroup"> + <arguments> + <argument name="inputId" type="string"/> + <argument name="errorMessage" type="string" defaultValue="This is a required field."/> + </arguments> + + <see selector="{{AdminConfigSection.errorElement(inputId)}}" userInput="{{errorMessage}}" + stepKey="seeElementValidationError"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml b/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml index a4fb3c7e32975..de01de3c7f4a8 100644 --- a/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml +++ b/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml @@ -22,5 +22,6 @@ <element name="useSystemValue" type="checkbox" selector="#{{configRowId}} > .use-default > input" parameterized="true"/> <element name="collapsibleSectionByTitle" type="text" selector="//form[@id='config-edit-form']//div[@class='section-config'][contains(.,'{{sectionTitle}}')]" parameterized="true" /> <element name="expandedSectionByTitle" type="text" selector="//form[@id='config-edit-form']//div[@class='section-config active'][contains(.,'{{sectionTitle}}')]" parameterized="true" /> + <element name="errorElement" type="text" selector="#{{inputId}}-error" parameterized="true" /> </section> </sections> diff --git a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php index 2767e725cc9b0..88007d0b1a880 100644 --- a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php @@ -573,7 +573,7 @@ protected function _parseVariations($rowData) $fieldAndValuePairs = []; foreach ($fieldAndValuePairsText as $nameAndValue) { - $nameAndValue = explode(ImportProduct::PAIR_NAME_VALUE_SEPARATOR, $nameAndValue); + $nameAndValue = explode(ImportProduct::PAIR_NAME_VALUE_SEPARATOR, $nameAndValue, 2); if (!empty($nameAndValue)) { $value = isset($nameAndValue[1]) ? trim($nameAndValue[1]) : ''; // Ignoring field names' case. diff --git a/app/code/Magento/ConfigurableImportExport/Test/Unit/Model/Import/Product/Type/ConfigurableTest.php b/app/code/Magento/ConfigurableImportExport/Test/Unit/Model/Import/Product/Type/ConfigurableTest.php index 8d75fd902e911..85b8dc5ec1d04 100644 --- a/app/code/Magento/ConfigurableImportExport/Test/Unit/Model/Import/Product/Type/ConfigurableTest.php +++ b/app/code/Magento/ConfigurableImportExport/Test/Unit/Model/Import/Product/Type/ConfigurableTest.php @@ -9,8 +9,8 @@ use Magento\ConfigurableImportExport; /** - * Class ConfigurableTest - * @package Magento\ConfigurableImportExport\Test\Unit\Model\Import\Product\Type + * Configurable import export tests + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ConfigurableTest extends \Magento\ImportExport\Test\Unit\Model\Import\AbstractImportTestCase @@ -78,6 +78,8 @@ class ConfigurableTest extends \Magento\ImportExport\Test\Unit\Model\Import\Abst protected $productEntityLinkField = 'entity_id'; /** + * @inheritdoc + * * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function setUp() @@ -270,10 +272,12 @@ protected function setUp() } /** + * Bunches data provider + * * @return array * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - protected function _getBunch() + protected function _getBunch(): array { return [[ 'sku' => 'configurableskuI22', @@ -393,9 +397,11 @@ protected function _getBunch() } /** + * Super attributes data provider + * * @return array */ - protected function _getSuperAttributes() + protected function _getSuperAttributes(): array { return [ 'testattr2' => [ @@ -404,7 +410,6 @@ protected function _getSuperAttributes() 'attribute_code' => 'testattr2', 'is_global' => '1', 'is_visible' => '1', - 'is_static' => '0', 'is_required' => '0', 'is_unique' => '0', 'frontend_label' => 'testattr2', @@ -426,7 +431,6 @@ protected function _getSuperAttributes() 'attribute_code' => 'testattr3', 'is_global' => '1', 'is_visible' => '1', - 'is_static' => '0', 'is_required' => '0', 'is_unique' => '0', 'frontend_label' => 'testattr3', @@ -445,6 +449,8 @@ protected function _getSuperAttributes() } /** + * Verify save mtethod + * * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function testSaveData() @@ -527,6 +533,8 @@ public function testSaveData() } /** + * Callback for is row allowed to import + * * @param $rowData * @param $rowNum * @return bool @@ -540,75 +548,31 @@ public function isRowAllowedToImport($rowData, $rowNum) return true; } - public function testIsRowValid() + /** + * Verify is row valid method + * + * @dataProvider getProductDataIsValidRow + * @param array $productData + * @return void + */ + public function testIsRowValid(array $productData): void { $bunch = $this->_getBunch(); - $badProduct = [ - 'sku' => 'configurableskuI22BadPrice', - 'store_view_code' => null, - 'attribute_set_code' => 'Default', - 'product_type' => 'configurable', - 'name' => 'Configurable Product 21 BadPrice', - 'product_websites' => 'website_1', - 'configurable_variation_labels' => 'testattr2=Select Color, testattr3=Select Size', - 'configurable_variations' => 'sku=testconf2-attr2val1-testattr3v1,' - . 'testattr2=attr2val1_DOESNT_EXIST,' - . 'testattr3=testattr3v1,' - . 'display=1|sku=testconf2-attr2val1-testattr3v2,' - . 'testattr2=attr2val1,' - . 'testattr3=testattr3v2,' - . 'display=0', - '_store' => null, - '_attribute_set' => 'Default', - '_type' => 'configurable', - '_product_websites' => 'website_1', - ]; // Checking that variations' field names are case-insensitive with this // product. $caseInsensitiveSKU = 'configurableskuI22CaseInsensitive'; - $caseInsensitiveProduct = [ - 'sku' => $caseInsensitiveSKU, - 'store_view_code' => null, - 'attribute_set_code' => 'Default', - 'product_type' => 'configurable', - 'name' => 'Configurable Product 21', - 'product_websites' => 'website_1', - 'configurable_variation_labels' => 'testattr2=Select Color, testattr3=Select Size', - 'configurable_variations' => 'SKU=testconf2-attr2val1-testattr3v1,' - . 'testattr2=attr2val1,' - . 'testattr3=testattr3v1,' - . 'display=1|sku=testconf2-attr2val1-testattr3v2,' - . 'testattr2=attr2val1,' - . 'testattr3=testattr3v2,' - . 'display=0', - '_store' => null, - '_attribute_set' => 'Default', - '_type' => 'configurable', - '_product_websites' => 'website_1', - ]; - $bunch[] = $badProduct; - $bunch[] = $caseInsensitiveProduct; + $productData['caseInsencitiveProduct']['sku'] = $caseInsensitiveSKU; + $bunch[] = $productData['bad_product']; + $bunch[] = $productData['caseInsencitiveProduct']; // Set _attributes to avoid error in Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType. $this->setPropertyValue($this->configurable, '_attributes', [ - $badProduct[\Magento\CatalogImportExport\Model\Import\Product::COL_ATTR_SET] => [], + $productData['bad_product'][\Magento\CatalogImportExport\Model\Import\Product::COL_ATTR_SET] => [], ]); // Avoiding errors about attributes not being super - $this->setPropertyValue( - $this->configurable, - '_superAttributes', - [ - 'testattr2' => ['options' => ['attr2val1' => 1]], - 'testattr3' => [ - 'options' => [ - 'testattr3v2' => 1, - 'testattr3v1' => 1, - ], - ], - ] - ); + $this->setPropertyValue($this->configurable, '_superAttributes', $productData['super_attributes']); foreach ($bunch as $rowData) { - $result = $this->configurable->isRowValid($rowData, 0, !isset($this->_oldSku[$rowData['sku']])); + $result = $this->configurable->isRowValid($rowData, 0, false); $this->assertNotNull($result); if ($rowData['sku'] === $caseInsensitiveSKU) { $this->assertTrue($result); @@ -616,7 +580,78 @@ public function testIsRowValid() } } - public function testRowValidationForNumericalSkus() + /** + * + * Data provider for isValidRows test. + * + * @return array + */ + public function getProductDataIsValidRow(): array + { + return [ + [ + [ + 'bad_product' => [ + 'sku' => 'configurableskuI22BadPrice', + 'store_view_code' => null, + 'attribute_set_code' => 'Default', + 'product_type' => 'configurable', + 'name' => 'Configurable Product 21 BadPrice', + 'product_websites' => 'website_1', + 'configurable_variation_labels' => 'testattr2=Select Color, testattr3=Select Size', + 'configurable_variations' => 'sku=testconf2-attr2val1-testattr3v1,' + . 'testattr2=attr2val1_DOESNT_EXIST,' + . 'testattr3=testattr3v1,' + . 'display=1|sku=testconf2-attr2val1-testattr3v2,' + . 'testattr2=attr2val1,' + . 'testattr3=testattr3v2,' + . 'display=0', + '_store' => null, + '_attribute_set' => 'Default', + '_type' => 'configurable', + '_product_websites' => 'website_1', + ], + 'caseInsencitiveProduct' => [ + 'sku' => '', + 'store_view_code' => null, + 'attribute_set_code' => 'Default', + 'product_type' => 'configurable', + 'name' => 'Configurable Product 21', + 'product_websites' => 'website_1', + 'configurable_variation_labels' => 'testattr2=Select Color, testattr3=Select Size', + 'configurable_variations' => 'SKU=testconf2-attr2val1-testattr3v1,' + . 'testattr2=attr2val1,' + . 'testattr3=testattr3v1=sx=sl,' + . 'display=1|sku=testconf2-attr2val1-testattr3v2,' + . 'testattr2=attr2val1,' + . 'testattr3=testattr3v2,' + . 'display=0', + '_store' => null, + '_attribute_set' => 'Default', + '_type' => 'configurable', + '_product_websites' => 'website_1', + ], + 'super_attributes' => + [ + 'testattr2' => ['options' => ['attr2val1' => 1]], + 'testattr3' => [ + 'options' => [ + 'testattr3v2' => 1, + 'testattr3v1=sx=sl' => 1, + ], + ], + ] + ] + ] + ]; + } + + /** + * Verify row validation with numeric skus + * + * @return void + */ + public function testRowValidationForNumericalSkus(): void { // Set _attributes to avoid error in Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType. $this->setPropertyValue($this->configurable, '_attributes', [ @@ -649,9 +684,11 @@ public function testRowValidationForNumericalSkus() } /** + * Row validation Data Provider + * * @return array */ - public function rowValidationDataProvider() + public function rowValidationDataProvider(): array { return [ 'duplicateProduct' => [ diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml index a5ad45048ce96..fa515c89fa460 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml @@ -148,6 +148,7 @@ <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetSearch"/> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> <deleteData stepKey="deleteAttribute" createDataKey="createConfigProductAttribute"/> + <magentoCLI command="cron:run --group=index" stepKey="runCron"/> <actionGroup ref="logout" stepKey="logout"/> </after> <!-- Create configurable product from downloadable product page--> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDontDisplayOutOfStockProductsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDontDisplayOutOfStockProductsTest.xml index 9b4cea72882eb..1b7fc2c153208 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDontDisplayOutOfStockProductsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDontDisplayOutOfStockProductsTest.xml @@ -72,6 +72,8 @@ <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <magentoCLI command="cron:run --group=index" stepKey="runCron"/> + <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> </after> diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js index faacbd03f20b0..6f3af43bf5c7a 100644 --- a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js +++ b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js @@ -139,7 +139,12 @@ define([ }); $.each(queryParams, $.proxy(function (key, value) { - this.options.values[key] = value; + if (this.options.spConfig.attributes[key] !== undefined && + _.find(this.options.spConfig.attributes[key].options, function (element) { + return element.id === value; + })) { + this.options.values[key] = value; + } }, this)); }, @@ -155,7 +160,13 @@ define([ if (element.value) { attributeId = element.id.replace(/[a-z]*/, ''); - this.options.values[attributeId] = element.value; + + if (this.options.spConfig.attributes[attributeId] !== undefined && + _.find(this.options.spConfig.attributes[attributeId].options, function (optionElement) { + return optionElement.id === element.value; + })) { + this.options.values[attributeId] = element.value; + } } }, this)); }, diff --git a/app/code/Magento/Contact/etc/adminhtml/system.xml b/app/code/Magento/Contact/etc/adminhtml/system.xml index b5974e4d42e84..c1437c7778b79 100644 --- a/app/code/Magento/Contact/etc/adminhtml/system.xml +++ b/app/code/Magento/Contact/etc/adminhtml/system.xml @@ -24,15 +24,24 @@ <field id="recipient_email" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Send Emails To</label> <validate>validate-email</validate> + <depends> + <field id="*/contact/enabled">1</field> + </depends> </field> <field id="sender_email_identity" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> + <depends> + <field id="*/contact/enabled">1</field> + </depends> </field> <field id="email_template" translate="label comment" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="*/contact/enabled">1</field> + </depends> </field> </group> </section> diff --git a/app/code/Magento/Cookie/etc/adminhtml/system.xml b/app/code/Magento/Cookie/etc/adminhtml/system.xml index 9790410969055..d1dcbf45ae5be 100644 --- a/app/code/Magento/Cookie/etc/adminhtml/system.xml +++ b/app/code/Magento/Cookie/etc/adminhtml/system.xml @@ -29,7 +29,7 @@ </comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="cookie_restriction" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="cookie_restriction" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Cookie Restriction Mode</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\Cookie\Model\Config\Backend\Cookie</backend_model> diff --git a/app/code/Magento/Cron/etc/adminhtml/system.xml b/app/code/Magento/Cron/etc/adminhtml/system.xml index cef45ba386be2..2bd6611e57473 100644 --- a/app/code/Magento/Cron/etc/adminhtml/system.xml +++ b/app/code/Magento/Cron/etc/adminhtml/system.xml @@ -8,36 +8,36 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="system"> - <group id="cron" translate="label comment" type="text" sortOrder="15" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="cron" translate="label comment" type="text" sortOrder="15" showInDefault="1"> <label>Cron (Scheduled Tasks)</label> <comment>For correct URLs generated during cron runs please make sure that Web > Secure and Unsecure Base URLs are explicitly set. All the times are in minutes.</comment> - <group id="template" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="template" translate="label" type="text" sortOrder="10" showInDefault="1"> <label>Cron configuration options for group:</label> - <field id="schedule_generate_every" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="schedule_generate_every" translate="label" type="text" sortOrder="10" showInDefault="1" canRestore="1"> <label>Generate Schedules Every</label> <validate>validate-zero-or-greater validate-digits</validate> </field> - <field id="schedule_ahead_for" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="schedule_ahead_for" translate="label" type="text" sortOrder="20" showInDefault="1" canRestore="1"> <label>Schedule Ahead for</label> <validate>validate-zero-or-greater validate-digits</validate> </field> - <field id="schedule_lifetime" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="schedule_lifetime" translate="label" type="text" sortOrder="30" showInDefault="1" canRestore="1"> <label>Missed if Not Run Within</label> <validate>validate-zero-or-greater validate-digits</validate> </field> - <field id="history_cleanup_every" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="history_cleanup_every" translate="label" type="text" sortOrder="40" showInDefault="1" canRestore="1"> <label>History Cleanup Every</label> <validate>validate-zero-or-greater validate-digits</validate> </field> - <field id="history_success_lifetime" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="history_success_lifetime" translate="label" type="text" sortOrder="50" showInDefault="1" canRestore="1"> <label>Success History Lifetime</label> <validate>validate-zero-or-greater validate-digits</validate> </field> - <field id="history_failure_lifetime" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="history_failure_lifetime" translate="label" type="text" sortOrder="60" showInDefault="1" canRestore="1"> <label>Failure History Lifetime</label> <validate>validate-zero-or-greater validate-digits</validate> </field> - <field id="use_separate_process" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="use_separate_process" translate="label" type="select" sortOrder="70" showInDefault="1" canRestore="1"> <label>Use Separate Process</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> diff --git a/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/Save.php b/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/Save.php index 07c7c553ac792..f4d69096475d5 100644 --- a/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/Save.php +++ b/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/Save.php @@ -67,6 +67,6 @@ public function execute() $this->messageManager->addErrorMessage($e->getMessage()); } - return $resultRedirect->setPath('*'); + return $resultRedirect->setPath('adminhtml/*/'); } } diff --git a/app/code/Magento/CurrencySymbol/Test/Unit/Controller/Adminhtml/System/Currencysymbol/SaveTest.php b/app/code/Magento/CurrencySymbol/Test/Unit/Controller/Adminhtml/System/Currencysymbol/SaveTest.php index b3c69c352ac7d..e4dfc7a3dc765 100644 --- a/app/code/Magento/CurrencySymbol/Test/Unit/Controller/Adminhtml/System/Currencysymbol/SaveTest.php +++ b/app/code/Magento/CurrencySymbol/Test/Unit/Controller/Adminhtml/System/Currencysymbol/SaveTest.php @@ -132,7 +132,7 @@ public function testExecute() ->with(__('You applied the custom currency symbols.')); $redirect = $this->createMock(Redirect::class); - $redirect->expects($this->once())->method('setPath')->with('*')->willReturnSelf(); + $redirect->expects($this->once())->method('setPath')->with('adminhtml/*/')->willReturnSelf(); $this->resultRedirectFactory->method('create')->willReturn($redirect); $this->assertEquals($redirect, $this->action->execute()); diff --git a/app/code/Magento/Customer/etc/adminhtml/system.xml b/app/code/Magento/Customer/etc/adminhtml/system.xml index 2bd1041214801..fca625d847a1d 100644 --- a/app/code/Magento/Customer/etc/adminhtml/system.xml +++ b/app/code/Magento/Customer/etc/adminhtml/system.xml @@ -15,10 +15,10 @@ <label>Customer Configuration</label> <tab>customer</tab> <resource>Magento_Customer::config_customer</resource> - <group id="account_share" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="account_share" translate="label" type="text" sortOrder="10" showInDefault="1"> <label>Account Sharing Options</label> <hide_in_single_store_mode>1</hide_in_single_store_mode> - <field id="scope" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="scope" translate="label" type="select" sortOrder="1" showInDefault="1" canRestore="1"> <label>Share Customer Accounts</label> <backend_model>Magento\Customer\Model\Config\Share</backend_model> <source_model>Magento\Customer\Model\Config\Share</source_model> @@ -26,7 +26,7 @@ </group> <group id="create_account" translate="label" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Create New Account Options</label> - <field id="auto_group_assign" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="auto_group_assign" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Enable Automatic Assignment to Customer Group</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> @@ -76,12 +76,12 @@ <field id="auto_group_assign">1</field> </depends> </field> - <field id="viv_disable_auto_group_assign_default" translate="label" type="select" sortOrder="57" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="viv_disable_auto_group_assign_default" translate="label" type="select" sortOrder="57" showInDefault="1" canRestore="1"> <label>Default Value for Disable Automatic Group Changes Based on VAT ID</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <backend_model>Magento\Customer\Model\Config\Backend\CreateAccount\DisableAutoGroupAssignDefault</backend_model> </field> - <field id="vat_frontend_visibility" translate="label comment" type="select" sortOrder="58" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="vat_frontend_visibility" translate="label comment" type="select" sortOrder="58" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Show VAT Number on Storefront</label> <comment>To show VAT number on Storefront, set Show VAT Number on Storefront option to Yes.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> @@ -106,7 +106,7 @@ <label>Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> </field> - <field id="confirm" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="confirm" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Require Emails Confirmation</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> @@ -123,7 +123,7 @@ ]]></comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> </field> - <field id="generate_human_friendly_id" translate="label" type="select" sortOrder="120" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="generate_human_friendly_id" translate="label" type="select" sortOrder="120" showInDefault="1" canRestore="1"> <label>Generate Human-Friendly Customer ID</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> @@ -149,33 +149,33 @@ <label>Password Template Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> </field> - <field id="reset_link_expiration_period" translate="label comment" type="text" sortOrder="60" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="reset_link_expiration_period" translate="label comment" type="text" sortOrder="60" showInDefault="1" canRestore="1"> <label>Recovery Link Expiration Period (hours)</label> <comment>Please enter a number 1 or greater in this field.</comment> <validate>required-entry validate-digits validate-digits-range digits-range-1-</validate> <backend_model>Magento\Customer\Model\Config\Backend\Password\Link\Expirationperiod</backend_model> </field> - <field id="required_character_classes_number" translate="label comment" type="text" sortOrder="70" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="required_character_classes_number" translate="label comment" type="text" sortOrder="70" showInDefault="1" canRestore="1"> <label>Number of Required Character Classes</label> <comment>Number of different character classes required in password: Lowercase, Uppercase, Digits, Special Characters.</comment> <validate>required-entry validate-digits validate-digits-range digits-range-1-4</validate> </field> - <field id="minimum_password_length" translate="label comment" type="text" sortOrder="80" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="minimum_password_length" translate="label comment" type="text" sortOrder="80" showInDefault="1" canRestore="1"> <label>Minimum Password Length</label> <comment>Please enter a number 1 or greater in this field.</comment> <validate>required-entry validate-digits validate-digits-range digits-range-1-</validate> </field> - <field id="lockout_failures" translate="label comment" sortOrder="70" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="lockout_failures" translate="label comment" sortOrder="70" showInDefault="1" canRestore="1"> <label>Maximum Login Failures to Lockout Account</label> <comment>Use 0 to disable account locking.</comment> <frontend_class>required-entry validate-digits</frontend_class> </field> - <field id="lockout_threshold" translate="label comment" sortOrder="80" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="lockout_threshold" translate="label comment" sortOrder="80" showInDefault="1" canRestore="1"> <label>Lockout Time (minutes)</label> <comment>Account will be unlocked after provided time.</comment> <frontend_class>required-entry validate-digits</frontend_class> </field> - <field id="autocomplete_on_storefront" type="select" translate="label" sortOrder="65" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="autocomplete_on_storefront" type="select" translate="label" sortOrder="65" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enable Autocomplete on login/forgot password forms</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> @@ -195,68 +195,68 @@ </group> <group id="address" translate="label" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Name and Address Options</label> - <field id="street_lines" translate="label comment" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="street_lines" translate="label comment" sortOrder="10" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Number of Lines in a Street Address</label> <backend_model>Magento\Customer\Model\Config\Backend\Address\Street</backend_model> <comment>Valid range: 1-4</comment> <validate>required-entry validate-digits validate-digits-range digits-range-1-4</validate> </field> - <field id="prefix_show" translate="label comment" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="prefix_show" translate="label comment" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Show Prefix</label> <source_model>Magento\Config\Model\Config\Source\Nooptreq</source_model> <backend_model>Magento\Customer\Model\Config\Backend\Show\Address</backend_model> <comment>The title that goes before name (Mr., Mrs., etc.)</comment> </field> - <field id="prefix_options" translate="label comment" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="prefix_options" translate="label comment" sortOrder="30" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Prefix Dropdown Options</label> <comment> <![CDATA[Semicolon (;) separated values.<br/>Leave empty for open text field.]]> </comment> </field> - <field id="middlename_show" translate="label comment" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="middlename_show" translate="label comment" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Show Middle Name (initial)</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Always optional.</comment> <backend_model>Magento\Customer\Model\Config\Backend\Show\Address</backend_model> </field> - <field id="suffix_show" translate="label comment" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="suffix_show" translate="label comment" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Show Suffix</label> <source_model>Magento\Config\Model\Config\Source\Nooptreq</source_model> <comment>The suffix that goes after name (Jr., Sr., etc.)</comment> <backend_model>Magento\Customer\Model\Config\Backend\Show\Address</backend_model> </field> - <field id="suffix_options" translate="label comment" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="suffix_options" translate="label comment" sortOrder="60" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Suffix Dropdown Options</label> <comment> <![CDATA[Semicolon (;) separated values.<br/>Leave empty for open text field.]]> </comment> </field> - <field id="dob_show" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="dob_show" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Show Date of Birth</label> <source_model>Magento\Config\Model\Config\Source\Nooptreq</source_model> <backend_model>Magento\Customer\Model\Config\Backend\Show\Customer</backend_model> </field> - <field id="taxvat_show" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="taxvat_show" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Show Tax/VAT Number</label> <source_model>Magento\Config\Model\Config\Source\Nooptreq</source_model> <backend_model>Magento\Customer\Model\Config\Backend\Show\Customer</backend_model> </field> - <field id="gender_show" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="gender_show" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Show Gender</label> <source_model>Magento\Config\Model\Config\Source\Nooptreq</source_model> <backend_model>Magento\Customer\Model\Config\Backend\Show\Customer</backend_model> </field> - <field id="telephone_show" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="telephone_show" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Show Telephone</label> <source_model>Magento\Config\Model\Config\Source\Nooptreq</source_model> <backend_model>Magento\Customer\Model\Config\Backend\Show\AddressOnly</backend_model> </field> - <field id="company_show" translate="label" type="select" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="company_show" translate="label" type="select" sortOrder="110" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Show Company</label> <source_model>Magento\Config\Model\Config\Source\Nooptreq</source_model> <backend_model>Magento\Customer\Model\Config\Backend\Show\AddressOnly</backend_model> </field> - <field id="fax_show" translate="label" type="select" sortOrder="120" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="fax_show" translate="label" type="select" sortOrder="120" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Show Fax</label> <source_model>Magento\Config\Model\Config\Source\Nooptreq</source_model> <backend_model>Magento\Customer\Model\Config\Backend\Show\AddressOnly</backend_model> @@ -264,7 +264,7 @@ </group> <group id="startup" translate="label" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Login Options</label> - <field id="redirect_dashboard" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="redirect_dashboard" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Redirect Customer to Account Dashboard after Logging in</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Customer will stay on the current page if "No" is selected.</comment> @@ -286,14 +286,14 @@ <label>PDF</label> </field> </group> - <group id="online_customers" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="online_customers" translate="label" type="text" sortOrder="10" showInDefault="1"> <label>Online Customers Options</label> - <field id="online_minutes_interval" translate="label comment" type="text" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="online_minutes_interval" translate="label comment" type="text" sortOrder="1" showInDefault="1"> <label>Online Minutes Interval</label> <validate>validate-number validate-greater-than-zero</validate> <comment>Leave empty for default (15 minutes).</comment> </field> - <field id="section_data_lifetime" translate="label comment" type="text" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="section_data_lifetime" translate="label comment" type="text" sortOrder="1" showInDefault="1"> <label>Customer Data Lifetime</label> <validate>validate-number validate-greater-than-zero</validate> <comment>Please specify value in minutes.</comment> @@ -302,7 +302,7 @@ </section> <section id="general"> <group id="store_information"> - <field id="validate_vat_number" translate="button_label" sortOrder="62" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="validate_vat_number" translate="button_label" sortOrder="62" showInDefault="1" showInWebsite="1"> <button_label>Validate VAT Number</button_label> <frontend_model>Magento\Customer\Block\Adminhtml\System\Config\Validatevat</frontend_model> </field> diff --git a/app/code/Magento/Customer/etc/config.xml b/app/code/Magento/Customer/etc/config.xml index da4b80536e631..e50e6294c924d 100644 --- a/app/code/Magento/Customer/etc/config.xml +++ b/app/code/Magento/Customer/etc/config.xml @@ -12,6 +12,7 @@ <scope>1</scope> </account_share> <create_account> + <auto_group_assign>0</auto_group_assign> <confirm>0</confirm> <default_group>1</default_group> <tax_calculation_address_type>billing</tax_calculation_address_type> @@ -21,7 +22,9 @@ <email_no_password_template>customer_create_account_email_no_password_template</email_no_password_template> <email_confirmation_template>customer_create_account_email_confirmation_template</email_confirmation_template> <email_confirmed_template>customer_create_account_email_confirmed_template</email_confirmed_template> + <viv_disable_auto_group_assign_default>0</viv_disable_auto_group_assign_default> <vat_frontend_visibility>0</vat_frontend_visibility> + <generate_human_friendly_id>0</generate_human_friendly_id> </create_account> <default> <group>1</group> @@ -50,6 +53,7 @@ <suffix_show /> <suffix_options /> <dob_show /> + <taxvat_show /> <gender_show /> <telephone_show>req</telephone_show> <company_show>opt</company_show> diff --git a/app/code/Magento/CustomerImportExport/Model/Import/Customer.php b/app/code/Magento/CustomerImportExport/Model/Import/Customer.php index f86ebaea69730..5e5f309915d99 100644 --- a/app/code/Magento/CustomerImportExport/Model/Import/Customer.php +++ b/app/code/Magento/CustomerImportExport/Model/Import/Customer.php @@ -408,7 +408,7 @@ protected function _prepareDataForUpdate(array $rowData) $createdAt = (new \DateTime())->setTimestamp(strtotime($rowData['created_at'])); } - $emailInLowercase = strtolower($rowData[self::COLUMN_EMAIL]); + $emailInLowercase = strtolower(trim($rowData[self::COLUMN_EMAIL])); $newCustomer = false; $entityId = $this->_getCustomerId($emailInLowercase, $rowData[self::COLUMN_WEBSITE]); if (!$entityId) { @@ -478,6 +478,8 @@ protected function _prepareDataForUpdate(array $rowData) $entityRow['updated_at'] = $now->format(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT); if (!empty($rowData[self::COLUMN_STORE])) { $entityRow['store_id'] = $this->_storeCodeToId[$rowData[self::COLUMN_STORE]]; + } else { + $entityRow['store_id'] = $this->getCustomerStoreId($emailInLowercase, $rowData[self::COLUMN_WEBSITE]); } $entitiesToUpdate[] = $entityRow; } @@ -666,4 +668,22 @@ public function getValidColumnNames() ) ); } + + /** + * Get customer store ID by email and website ID. + * + * @param string $email + * @param string $websiteCode + * @return bool|int + */ + private function getCustomerStoreId(string $email, string $websiteCode) + { + $websiteId = (int) $this->getWebsiteId($websiteCode); + $storeId = $this->getCustomerStorage()->getCustomerStoreId($email, $websiteId); + if ($storeId === null || $storeId === false) { + $defaultStore = $this->_storeManager->getWebsite($websiteId)->getDefaultStore(); + $storeId = $defaultStore ? $defaultStore->getId() : \Magento\Store\Model\Store::DEFAULT_STORE_ID; + } + return $storeId; + } } diff --git a/app/code/Magento/CustomerImportExport/Model/ResourceModel/Import/Customer/Storage.php b/app/code/Magento/CustomerImportExport/Model/ResourceModel/Import/Customer/Storage.php index 43623019c005e..7cff2dcc21b1e 100644 --- a/app/code/Magento/CustomerImportExport/Model/ResourceModel/Import/Customer/Storage.php +++ b/app/code/Magento/CustomerImportExport/Model/ResourceModel/Import/Customer/Storage.php @@ -5,13 +5,12 @@ */ namespace Magento\CustomerImportExport\Model\ResourceModel\Import\Customer; -use Magento\CustomerImportExport\Test\Unit\Model\Import\CustomerCompositeTest; +use Magento\Customer\Model\ResourceModel\Customer\Collection as CustomerCollection; +use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory as CustomerCollectionFactory; use Magento\Framework\DataObject; use Magento\Framework\DB\Select; -use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory as CustomerCollectionFactory; -use Magento\Customer\Model\ResourceModel\Customer\Collection as CustomerCollection; -use Magento\ImportExport\Model\ResourceModel\CollectionByPagesIteratorFactory; use Magento\ImportExport\Model\ResourceModel\CollectionByPagesIterator; +use Magento\ImportExport\Model\ResourceModel\CollectionByPagesIteratorFactory; /** * Storage to check existing customers. @@ -56,6 +55,20 @@ class Storage */ public $_customerCollection; + /** + * Existing customers store IDs. In form of: + * + * [customer email] => array( + * [website id 1] => store id 1, + * [website id 2] => store id 2, + * ... => ... , + * [website id n] => store id n, + * ) + * + * @var array + */ + private $customerStoreIds = []; + /** * @param CustomerCollectionFactory $collectionFactory * @param CollectionByPagesIteratorFactory $colIteratorFactory @@ -91,7 +104,7 @@ private function prepareCollection(array $customerIdentifiers): CustomerCollecti $select = $collection->getSelect(); $customerTableId = array_keys($select->getPart(Select::FROM))[0]; $select->where( - $customerTableId .'.email in (?)', + $customerTableId . '.email in (?)', array_map( function (array $customer) { return $customer['email']; @@ -127,11 +140,15 @@ private function loadCustomersData(array $customerIdentifiers) */ public function addCustomerByArray(array $customer): Storage { - $email = strtolower(trim($customer['email'])); + $email = mb_strtolower(trim($customer['email'])); if (!isset($this->_customerIds[$email])) { $this->_customerIds[$email] = []; } + if (!isset($this->customerStoreIds[$email])) { + $this->customerStoreIds[$email] = []; + } $this->_customerIds[$email][$customer['website_id']] = $customer['entity_id']; + $this->customerStoreIds[$email][$customer['website_id']] = $customer['store_id'] ?? null; return $this; } @@ -164,11 +181,7 @@ public function addCustomer(DataObject $customer): Storage public function getCustomerId(string $email, int $websiteId) { $email = mb_strtolower($email); - //Trying to load the customer. - if (!array_key_exists($email, $this->_customerIds) || !array_key_exists($websiteId, $this->_customerIds[$email]) - ) { - $this->loadCustomersData([['email' => $email, 'website_id' => $websiteId]]); - } + $this->loadCustomerData($email, $websiteId); if (isset($this->_customerIds[$email][$websiteId])) { return $this->_customerIds[$email][$websiteId]; @@ -177,6 +190,25 @@ public function getCustomerId(string $email, int $websiteId) return false; } + /** + * Find customer store ID for unique pair of email and website ID. + * + * @param string $email + * @param int $websiteId + * @return bool|int + */ + public function getCustomerStoreId(string $email, int $websiteId) + { + $email = mb_strtolower($email); + $this->loadCustomerData($email, $websiteId); + + if (isset($this->customerStoreIds[$email][$websiteId])) { + return $this->customerStoreIds[$email][$websiteId]; + } + + return false; + } + /** * Pre-load customers for future checks. * @@ -189,12 +221,10 @@ public function prepareCustomers(array $customersToFind): void foreach ($customersToFind as $customerToFind) { $email = mb_strtolower($customerToFind['email']); $websiteId = $customerToFind['website_id']; - if (!array_key_exists($email, $this->_customerIds) - || !array_key_exists($websiteId, $this->_customerIds[$email]) - ) { + if (!$this->isLoadedCustomerData($email, $websiteId)) { //Only looking for customers we don't already have ID for. //We need unique identifiers. - $uniqueKey = $email .'_' .$websiteId; + $uniqueKey = $email . '_' . $websiteId; $identifiers[$uniqueKey] = [ 'email' => $email, 'website_id' => $websiteId, @@ -202,8 +232,10 @@ public function prepareCustomers(array $customersToFind): void //Recording that we've searched for a customer. if (!array_key_exists($email, $this->_customerIds)) { $this->_customerIds[$email] = []; + $this->customerStoreIds[$email] = []; } $this->_customerIds[$email][$websiteId] = null; + $this->customerStoreIds[$email][$websiteId] = null; } } if (!$identifiers) { @@ -213,4 +245,31 @@ public function prepareCustomers(array $customersToFind): void //Loading customers data. $this->loadCustomersData($identifiers); } + + /** + * Load customer data if it's not loaded. + * + * @param string $email + * @param int $websiteId + * @return void + */ + private function loadCustomerData(string $email, int $websiteId): void + { + if (!$this->isLoadedCustomerData($email, $websiteId)) { + $this->loadCustomersData([['email' => $email, 'website_id' => $websiteId]]); + } + } + + /** + * Check if customer data is loaded + * + * @param string $email + * @param int $websiteId + * @return bool + */ + private function isLoadedCustomerData(string $email, int $websiteId): bool + { + return array_key_exists($email, $this->_customerIds) + && array_key_exists($websiteId, $this->_customerIds[$email]); + } } diff --git a/app/code/Magento/Developer/Model/XmlCatalog/Format/VsCode.php b/app/code/Magento/Developer/Model/XmlCatalog/Format/VsCode.php new file mode 100644 index 0000000000000..568774a112e9a --- /dev/null +++ b/app/code/Magento/Developer/Model/XmlCatalog/Format/VsCode.php @@ -0,0 +1,145 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare (strict_types = 1); + +namespace Magento\Developer\Model\XmlCatalog\Format; + +use Magento\Framework\DomDocument\DomDocumentFactory; +use Magento\Framework\Exception\FileSystemException; +use Magento\Framework\Filesystem\Directory\ReadFactory; +use Magento\Framework\Filesystem\Directory\ReadInterface; +use Magento\Framework\Filesystem\DriverPool; +use Magento\Framework\Filesystem\File\WriteFactory; + +/** + * Class VsCode generates URN catalog for VsCode + */ +class VsCode implements FormatInterface +{ + private const PROJECT_PATH_IDENTIFIER = '..'; + public const XMLNS = 'urn:oasis:names:tc:entity:xmlns:xml:catalog'; + public const FILE_MODE_READ = 'r'; + public const FILE_MODE_WRITE = 'w'; + + /** + * @var ReadInterface + */ + private $currentDirRead; + + /** + * @var WriteFactory + */ + private $fileWriteFactory; + + /** + * @var DomDocumentFactory + */ + private $domDocumentFactory; + + /** + * @param ReadFactory $readFactory + * @param WriteFactory $fileWriteFactory + * @param DomDocumentFactory $domDocumentFactory + */ + public function __construct( + ReadFactory $readFactory, + WriteFactory $fileWriteFactory, + DomDocumentFactory $domDocumentFactory + ) { + $this->currentDirRead = $readFactory->create(getcwd()); + $this->fileWriteFactory = $fileWriteFactory; + $this->domDocumentFactory = $domDocumentFactory; + } + + /** + * Generate Catalog of URNs for the VsCode + * + * @param string[] $dictionary + * @param string $configFile relative path to the VsCode catalog.xml + * @return void + */ + public function generateCatalog(array $dictionary, $configFile): void + { + $catalogNode = null; + + try { + $file = $this->fileWriteFactory->create($configFile, DriverPool::FILE, self::FILE_MODE_READ); + $dom = $this->domDocumentFactory->create(); + $fileContent = $file->readAll(); + if (!empty($fileContent)) { + $dom->loadXML($fileContent); + } else { + $this->initEmptyFile($dom); + } + $catalogNode = $dom->getElementsByTagName('catalog')->item(0); + + if ($catalogNode == null) { + $dom = $this->domDocumentFactory->create(); + $catalogNode = $this->initEmptyFile($dom); + } + $file->close(); + } catch (FileSystemException $f) { + //create file if does not exists + $dom = $this->domDocumentFactory->create(); + $catalogNode = $this->initEmptyFile($dom); + } + + $xpath = new \DOMXPath($dom); + $xpath->registerNamespace('xmlns', self::XMLNS); + + foreach ($dictionary as $urn => $xsdPath) { + // Find an existing urn + $existingNode = $xpath->query("/xmlns:catalog/xmlns:system[@systemId='" . $urn . "']")->item(0); + $node = $existingNode ?? $dom->createElement('system'); + $node->setAttribute('systemId', $urn); + $node->setAttribute('uri', $this->getFileLocationInProject($xsdPath)); + $catalogNode->appendChild($node); + } + $dom->formatOutput = true; + $dom->preserveWhiteSpace = false; + + // Reload to keep pretty format + $dom->loadXML($dom->saveXML()); + + $file = $this->fileWriteFactory->create($configFile, DriverPool::FILE, self::FILE_MODE_WRITE); + $file->write($dom->saveXML()); + $file->close(); + } + + /** + * Setup basic empty dom elements + * + * @param \DOMDocument $dom + * @return \DOMElement + */ + private function initEmptyFile(\DOMDocument $dom): \DOMElement + { + $copyrigthComment = $dom->createComment(' +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +'); + $dom->appendChild($copyrigthComment); + + $catalogNode = $dom->createElement('catalog'); + $catalogNode->setAttribute('xmlns', self::XMLNS); + $dom->appendChild($catalogNode); + + return $catalogNode; + } + + /** + * Resolve xsdpath to xml project path + * + * @param string $xsdPath + * @return string + */ + private function getFileLocationInProject(string $xsdPath): string + { + return self::PROJECT_PATH_IDENTIFIER . DIRECTORY_SEPARATOR . $this->currentDirRead->getRelativePath($xsdPath); + } +} diff --git a/app/code/Magento/Developer/Test/Unit/Console/Command/XmlCatalogGenerateCommandTest.php b/app/code/Magento/Developer/Test/Unit/Console/Command/XmlCatalogGenerateCommandTest.php index e5c6525cfeb55..04d41efb793b8 100644 --- a/app/code/Magento/Developer/Test/Unit/Console/Command/XmlCatalogGenerateCommandTest.php +++ b/app/code/Magento/Developer/Test/Unit/Console/Command/XmlCatalogGenerateCommandTest.php @@ -66,4 +66,58 @@ public function testExecuteBadType() $commandTester->execute([XmlCatalogGenerateCommand::IDE_FILE_PATH_ARGUMENT => 'test']); $this->assertEquals('', $commandTester->getDisplay()); } + + public function testExecuteVsCodeFormat() + { + $fixtureXmlFile = __DIR__ . '/_files/test.xml'; + + $filesMock = $this->createPartialMock(\Magento\Framework\App\Utility\Files::class, ['getXmlCatalogFiles']); + $filesMock->expects($this->at(0)) + ->method('getXmlCatalogFiles') + ->will($this->returnValue([[$fixtureXmlFile]])); + $filesMock->expects($this->at(1)) + ->method('getXmlCatalogFiles') + ->will($this->returnValue([])); + $urnResolverMock = $this->createMock(\Magento\Framework\Config\Dom\UrnResolver::class); + $urnResolverMock->expects($this->once()) + ->method('getRealPath') + ->with($this->equalTo('urn:magento:framework:Module/etc/module.xsd')) + ->will($this->returnValue($fixtureXmlFile)); + + $vscodeFormatMock = $this->createMock(\Magento\Developer\Model\XmlCatalog\Format\VsCode::class); + $vscodeFormatMock->expects($this->once()) + ->method('generateCatalog') + ->with( + $this->equalTo(['urn:magento:framework:Module/etc/module.xsd' => $fixtureXmlFile]), + $this->equalTo('test') + )->will($this->returnValue(null)); + + $formats = ['vscode' => $vscodeFormatMock]; + $readFactory = $this->createMock(\Magento\Framework\Filesystem\Directory\ReadFactory::class); + $readDirMock = $this->createMock(\Magento\Framework\Filesystem\Directory\ReadInterface::class); + + $content = file_get_contents($fixtureXmlFile); + + $readDirMock->expects($this->once()) + ->method('readFile') + ->with($this->equalTo('test.xml')) + ->will($this->returnValue($content)); + $readFactory->expects($this->once()) + ->method('create') + ->will($this->returnValue($readDirMock)); + + $this->command = new XmlCatalogGenerateCommand( + $filesMock, + $urnResolverMock, + $readFactory, + $formats + ); + + $commandTester = new CommandTester($this->command); + $commandTester->execute([ + '--' . XmlCatalogGenerateCommand::IDE_OPTION => 'vscode', + XmlCatalogGenerateCommand::IDE_FILE_PATH_ARGUMENT => 'test', + ]); + $this->assertEquals('', $commandTester->getDisplay()); + } } diff --git a/app/code/Magento/Developer/Test/Unit/Model/XmlCatalog/Format/VsCodeTest.php b/app/code/Magento/Developer/Test/Unit/Model/XmlCatalog/Format/VsCodeTest.php new file mode 100644 index 0000000000000..55d2a811b2eee --- /dev/null +++ b/app/code/Magento/Developer/Test/Unit/Model/XmlCatalog/Format/VsCodeTest.php @@ -0,0 +1,282 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Developer\Test\Unit\Model\XmlCatalog\Format; + +use Magento\Developer\Model\XmlCatalog\Format\VsCode; +use Magento\Framework\DomDocument\DomDocumentFactory; +use Magento\Framework\Exception\FileSystemException; +use Magento\Framework\Filesystem\Directory\ReadFactory; +use Magento\Framework\Filesystem\Directory\ReadInterface; +use Magento\Framework\Filesystem\DriverPool; +use Magento\Framework\Filesystem\File\Read; +use Magento\Framework\Filesystem\File\Write; +use Magento\Framework\Filesystem\File\WriteFactory; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class VsCodeTest extends TestCase +{ + /** + * @var VsCode + */ + private $vscodeFormat; + + /** + * @var MockObject|ReadFactory + */ + private $readFactoryMock; + + /** + * @var MockObject|WriteFactory + */ + private $fileWriteFactoryMock; + + /** + * @var DomDocumentFactory + */ + private $domFactory; + + /** + * @var ObjectManager + */ + private $objectManagerHelper; + + public function setUp() + { + $this->objectManagerHelper = new ObjectManager($this); + + $currentDirReadMock = $this->createMock(ReadInterface::class); + $currentDirReadMock->expects($this->any()) + ->method('getRelativePath') + ->willReturnCallback(function ($xsdPath) { + return $xsdPath; + }); + + $this->readFactoryMock = $this->createMock(ReadFactory::class); + $this->readFactoryMock->expects($this->once()) + ->method('create') + ->withAnyParameters() + ->willReturn($currentDirReadMock); + + $this->fileWriteFactoryMock = $this->createMock(WriteFactory::class); + $this->domFactory = $this->objectManagerHelper->getObject(DomDocumentFactory::class); + + $vscodeFormatArgs = $this->objectManagerHelper->getConstructArguments( + VsCode::class, + [ + 'readFactory' => $this->readFactoryMock, + 'fileWriteFactory' => $this->fileWriteFactoryMock, + 'domDocumentFactory' => $this->domFactory, + ] + ); + + $this->vscodeFormat = $this->objectManagerHelper->getObject(VsCode::class, $vscodeFormatArgs); + } + + /** + * Test generation of new valid catalog + * + * @param string $content + * @param array $dictionary + * @dataProvider dictionaryDataProvider + * @return void + */ + public function testGenerateNewValidCatalog($content, $dictionary) + { + $configFile = 'test'; + + $message = __("The \"%1.xml\" file doesn't exist.", $configFile); + + $this->fileWriteFactoryMock->expects($this->at(0)) + ->method('create') + ->with( + $configFile, + DriverPool::FILE, + VsCode::FILE_MODE_READ + ) + ->willThrowException(new FileSystemException($message)); + + $fileMock = $this->createMock(Write::class); + $fileMock->expects($this->once()) + ->method('write') + ->with($content); + + $this->fileWriteFactoryMock->expects($this->at(1)) + ->method('create') + ->with( + $configFile, + DriverPool::FILE, + VsCode::FILE_MODE_WRITE + ) + ->willReturn($fileMock); + + $this->vscodeFormat->generateCatalog($dictionary, $configFile); + } + + /** + * Test modify existing valid catalog + * + * @param string $content + * @param array $dictionary + * @dataProvider dictionaryDataProvider + * @return void + */ + public function testGenerateExistingValidCatalog($content, $dictionary) + { + $configFile = 'test'; + + $fileMock = $this->createMock(Read::class); + $fileMock->expects($this->once()) + ->method('readAll') + ->withAnyParameters() + ->willReturn($content); + + $this->fileWriteFactoryMock->expects($this->at(0)) + ->method('create') + ->with( + $configFile, + DriverPool::FILE, + VsCode::FILE_MODE_READ + ) + ->willReturn($fileMock); + + $fileMock = $this->createMock(Write::class); + $fileMock->expects($this->once()) + ->method('write') + ->with($content); + + $this->fileWriteFactoryMock->expects($this->at(1)) + ->method('create') + ->with( + $configFile, + DriverPool::FILE, + VsCode::FILE_MODE_WRITE + ) + ->willReturn($fileMock); + + $this->vscodeFormat->generateCatalog($dictionary, $configFile); + } + + /** + * Test modify existing empty catalog + * + * @param string $content + * @param array $dictionary + * @dataProvider dictionaryDataProvider + * @return void + */ + public function testGenerateExistingEmptyValidCatalog($content, $dictionary) + { + $configFile = 'test'; + + $fileMock = $this->createMock(Read::class); + $fileMock->expects($this->once()) + ->method('readAll') + ->withAnyParameters() + ->willReturn(''); + + $this->fileWriteFactoryMock->expects($this->at(0)) + ->method('create') + ->with( + $configFile, + DriverPool::FILE, + VsCode::FILE_MODE_READ + ) + ->willReturn($fileMock); + + $fileMock = $this->createMock(Write::class); + $fileMock->expects($this->once()) + ->method('write') + ->with($content); + + $this->fileWriteFactoryMock->expects($this->at(1)) + ->method('create') + ->with( + $configFile, + DriverPool::FILE, + VsCode::FILE_MODE_WRITE + ) + ->willReturn($fileMock); + + $this->vscodeFormat->generateCatalog($dictionary, $configFile); + } + + /** + * Test modify existing invalid catalog + * + * @param string $content + * @param array $dictionary + * @dataProvider dictionaryDataProvider + * @return void + */ + public function testGenerateExistingInvalidValidCatalog($content, $dictionary, $invalidContent) + { + $configFile = 'test'; + + $fileMock = $this->createMock(Read::class); + $fileMock->expects($this->once()) + ->method('readAll') + ->withAnyParameters() + ->willReturn($invalidContent); + + $this->fileWriteFactoryMock->expects($this->at(0)) + ->method('create') + ->with( + $configFile, + DriverPool::FILE, + VsCode::FILE_MODE_READ + ) + ->willReturn($fileMock); + + $fileMock = $this->createMock(Write::class); + $fileMock->expects($this->once()) + ->method('write') + ->with($content); + + $this->fileWriteFactoryMock->expects($this->at(1)) + ->method('create') + ->with( + $configFile, + DriverPool::FILE, + VsCode::FILE_MODE_WRITE + ) + ->willReturn($fileMock); + + $this->vscodeFormat->generateCatalog($dictionary, $configFile); + } + + /** + * Data provider for test + * + * @return array + */ + public function dictionaryDataProvider() + { + $fixtureXmlFile = __DIR__ . '/_files/valid_catalog.xml'; + $content = file_get_contents($fixtureXmlFile); + $invalidXmlFile = __DIR__ . '/_files/invalid_catalog.xml'; + $invalidContent = file_get_contents($invalidXmlFile); + + return [ + [ + $content, + [ + 'urn:magento:framework:Acl/etc/acl.xsd' => + 'vendor/magento/framework/Acl/etc/acl.xsd', + 'urn:magento:module:Magento_Store:etc/config.xsd' => + 'vendor/magento/module-store/etc/config.xsd', + 'urn:magento:module:Magento_Cron:etc/crontab.xsd' => + 'vendor/magento/module-cron/etc/crontab.xsd', + 'urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd' => + 'vendor/magento/framework/Setup/Declaration/Schema/etc/schema.xsd', + ], + $invalidContent, + ], + ]; + } +} diff --git a/app/code/Magento/Developer/Test/Unit/Model/XmlCatalog/Format/_files/invalid_catalog.xml b/app/code/Magento/Developer/Test/Unit/Model/XmlCatalog/Format/_files/invalid_catalog.xml new file mode 100644 index 0000000000000..213448bfc7a68 --- /dev/null +++ b/app/code/Magento/Developer/Test/Unit/Model/XmlCatalog/Format/_files/invalid_catalog.xml @@ -0,0 +1,8 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<root /> diff --git a/app/code/Magento/Developer/Test/Unit/Model/XmlCatalog/Format/_files/valid_catalog.xml b/app/code/Magento/Developer/Test/Unit/Model/XmlCatalog/Format/_files/valid_catalog.xml new file mode 100644 index 0000000000000..43fee113e9930 --- /dev/null +++ b/app/code/Magento/Developer/Test/Unit/Model/XmlCatalog/Format/_files/valid_catalog.xml @@ -0,0 +1,13 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog"> + <system systemId="urn:magento:framework:Acl/etc/acl.xsd" uri="../vendor/magento/framework/Acl/etc/acl.xsd"/> + <system systemId="urn:magento:module:Magento_Store:etc/config.xsd" uri="../vendor/magento/module-store/etc/config.xsd"/> + <system systemId="urn:magento:module:Magento_Cron:etc/crontab.xsd" uri="../vendor/magento/module-cron/etc/crontab.xsd"/> + <system systemId="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd" uri="../vendor/magento/framework/Setup/Declaration/Schema/etc/schema.xsd"/> +</catalog> diff --git a/app/code/Magento/Developer/etc/adminhtml/system.xml b/app/code/Magento/Developer/etc/adminhtml/system.xml index 197dc6f981acf..10449ab428726 100644 --- a/app/code/Magento/Developer/etc/adminhtml/system.xml +++ b/app/code/Magento/Developer/etc/adminhtml/system.xml @@ -9,7 +9,7 @@ <section id="dev"> <group id="front_end_development_workflow" translate="label" type="text" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Frontend Development Workflow</label> - <field id="type" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="type" translate="label comment" type="select" sortOrder="1" showInDefault="1" canRestore="1"> <label>Workflow type</label> <comment>Not available in production mode.</comment> <source_model>Magento\Developer\Model\Config\Source\WorkflowType</source_model> diff --git a/app/code/Magento/Developer/etc/di.xml b/app/code/Magento/Developer/etc/di.xml index 98adcbb3a8295..d8f8eb6c1221e 100644 --- a/app/code/Magento/Developer/etc/di.xml +++ b/app/code/Magento/Developer/etc/di.xml @@ -18,6 +18,7 @@ <arguments> <argument name="formats" xsi:type="array"> <item name="phpstorm" xsi:type="object">Magento\Developer\Model\XmlCatalog\Format\PhpStorm</item> + <item name="vscode" xsi:type="object">Magento\Developer\Model\XmlCatalog\Format\VsCode</item> </argument> </arguments> </type> diff --git a/app/code/Magento/Dhl/etc/adminhtml/system.xml b/app/code/Magento/Dhl/etc/adminhtml/system.xml index 93a16821e385d..1728219236d9f 100644 --- a/app/code/Magento/Dhl/etc/adminhtml/system.xml +++ b/app/code/Magento/Dhl/etc/adminhtml/system.xml @@ -10,39 +10,39 @@ <section id="carriers"> <group id="dhl" translate="label" type="text" sortOrder="140" showInDefault="1" showInWebsite="1" showInStore="1"> <label>DHL</label> - <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enabled for Checkout</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> <field id="title" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Title</label> </field> - <field id="id" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="id" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1"> <label>Access ID</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> - <field id="password" translate="label" type="obscure" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="password" translate="label" type="obscure" sortOrder="60" showInDefault="1" showInWebsite="1"> <label>Password</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> - <field id="account" translate="label" type="text" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="account" translate="label" type="text" sortOrder="70" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Account Number</label> </field> - <field id="content_type" translate="label comment" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="content_type" translate="label comment" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Content Type (Non Domestic)</label> <comment>Whether to use Documents or NonDocuments service for non domestic shipments. (Shipments within the EU are classed as domestic)</comment> <source_model>Magento\Dhl\Model\Source\Contenttype</source_model> </field> - <field id="handling_type" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="handling_type" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Calculate Handling Fee</label> <source_model>Magento\Shipping\Model\Source\HandlingType</source_model> </field> - <field id="handling_action" translate="label comment" type="select" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="handling_action" translate="label comment" type="select" sortOrder="110" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Handling Applied</label> <comment>"Per Order" allows a single handling fee for the entire order. "Per Package" allows an individual handling fee for each package.</comment> <source_model>Magento\Shipping\Model\Source\HandlingAction</source_model> </field> - <field id="handling_fee" translate="label" type="text" sortOrder="120" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="handling_fee" translate="label" type="text" sortOrder="120" showInDefault="1" showInWebsite="1"> <label>Handling Fee</label> <validate>validate-number validate-zero-or-greater</validate> </field> @@ -78,22 +78,22 @@ <field id="size">1</field> </depends> </field> - <field id="doc_methods" translate="label" type="multiselect" sortOrder="170" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="doc_methods" translate="label" type="multiselect" sortOrder="170" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Documents Allowed Methods</label> <source_model>Magento\Dhl\Model\Source\Method\Doc</source_model> </field> - <field id="nondoc_methods" translate="label" type="multiselect" sortOrder="170" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="nondoc_methods" translate="label" type="multiselect" sortOrder="170" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Non Documents Allowed Methods</label> <source_model>Magento\Dhl\Model\Source\Method\Nondoc</source_model> </field> - <field id="ready_time" translate="label comment" type="text" sortOrder="180" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="ready_time" translate="label comment" type="text" sortOrder="180" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Ready time</label> <comment>Package ready time after order submission (in hours).</comment> </field> <field id="specificerrmsg" translate="label" type="textarea" sortOrder="800" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Displayed Error Message</label> </field> - <field id="free_method_doc" translate="label" type="select" sortOrder="1200" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="free_method_doc" translate="label" type="select" sortOrder="1200" showInDefault="1" showInWebsite="1"> <label>Free Method</label> <frontend_class>free-method</frontend_class> <source_model>Magento\Dhl\Model\Source\Method\Freedoc</source_model> @@ -101,7 +101,7 @@ <field id="content_type">D</field> </depends> </field> - <field id="free_method_nondoc" translate="label" type="select" sortOrder="1200" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="free_method_nondoc" translate="label" type="select" sortOrder="1200" showInDefault="1" showInWebsite="1"> <label>Free Method</label> <frontend_class>free-method</frontend_class> <source_model>Magento\Dhl\Model\Source\Method\Freenondoc</source_model> @@ -109,41 +109,41 @@ <field id="content_type">N</field> </depends> </field> - <field id="free_shipping_enable" translate="label" type="select" sortOrder="1210" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="free_shipping_enable" translate="label" type="select" sortOrder="1210" showInDefault="1" showInWebsite="1"> <label>Enable Free Shipping Threshold</label> <source_model>Magento\Config\Model\Config\Source\Enabledisable</source_model> </field> - <field id="free_shipping_subtotal" translate="label" type="text" sortOrder="1220" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="free_shipping_subtotal" translate="label" type="text" sortOrder="1220" showInDefault="1" showInWebsite="1"> <label>Free Shipping Amount Threshold</label> <validate>validate-number validate-zero-or-greater</validate> <depends> <field id="free_shipping_enable">1</field> </depends> </field> - <field id="sallowspecific" translate="label" type="select" sortOrder="1900" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="sallowspecific" translate="label" type="select" sortOrder="1900" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Ship to Applicable Countries</label> <frontend_class>shipping-applicable-country</frontend_class> <source_model>Magento\Shipping\Model\Config\Source\Allspecificcountries</source_model> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="1910" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="1910" showInDefault="1" showInWebsite="1"> <label>Ship to Specific Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> </field> - <field id="showmethod" translate="label" type="select" sortOrder="1940" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="showmethod" translate="label" type="select" sortOrder="1940" showInDefault="1" showInWebsite="1"> <label>Show Method if Not Applicable</label> <frontend_class>shipping-skip-hide</frontend_class> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="sort_order" translate="label" type="text" sortOrder="2000" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="2000" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="debug" translate="label" type="select" sortOrder="1950" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="debug" translate="label" type="select" sortOrder="1950" showInDefault="1" showInWebsite="1"> <label>Debug</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="sandbox_mode" translate="label" type="select" sortOrder="1960" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sandbox_mode" translate="label" type="select" sortOrder="1960" showInDefault="1" showInWebsite="1"> <label>Sandbox Mode</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> diff --git a/app/code/Magento/Directory/etc/adminhtml/system.xml b/app/code/Magento/Directory/etc/adminhtml/system.xml index 5f97a5e8d90d6..6fa47037d2645 100644 --- a/app/code/Magento/Directory/etc/adminhtml/system.xml +++ b/app/code/Magento/Directory/etc/adminhtml/system.xml @@ -13,7 +13,7 @@ <resource>Magento_Config::currency</resource> <group id="options" translate="label" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Currency Options</label> - <field id="base" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="base" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Base Currency</label> <frontend_model>Magento\Directory\Block\Adminhtml\Frontend\Currency\Base</frontend_model> <source_model>Magento\Config\Model\Config\Source\Locale\Currency</source_model> @@ -34,31 +34,31 @@ <can_be_empty>1</can_be_empty> </field> </group> - <group id="fixerio" translate="label" sortOrder="35" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="fixerio" translate="label" sortOrder="35" showInDefault="1"> <label>Fixer.io</label> - <field id="api_key" translate="label" type="obscure" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="api_key" translate="label" type="obscure" sortOrder="5" showInDefault="1"> <label>API Key</label> <config_path>currency/fixerio/api_key</config_path> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> - <field id="timeout" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="timeout" translate="label" type="text" sortOrder="10" showInDefault="1"> <label>Connection Timeout in Seconds</label> <validate>validate-zero-or-greater validate-number</validate> </field> </group> - <group id="currencyconverterapi" translate="label" sortOrder="45" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="currencyconverterapi" translate="label" sortOrder="45" showInDefault="1"> <label>Currency Converter API</label> - <field id="api_key" translate="label" type="obscure" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="api_key" translate="label" type="obscure" sortOrder="5" showInDefault="1"> <label>API Key</label> <config_path>currency/currencyconverterapi/api_key</config_path> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> - <field id="timeout" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="timeout" translate="label" type="text" sortOrder="10" showInDefault="1"> <label>Connection Timeout in Seconds</label> <validate>validate-zero-or-greater validate-number</validate> </field> </group> - <group id="import" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="import" translate="label" type="text" sortOrder="50" showInDefault="1"> <label>Scheduled Import Settings</label> <field id="enabled" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Enabled</label> @@ -71,14 +71,14 @@ <field id="enabled">1</field> </depends> </field> - <field id="error_email_identity" translate="label" type="select" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="error_email_identity" translate="label" type="select" sortOrder="6" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Error Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> <depends> <field id="enabled">1</field> </depends> </field> - <field id="error_email_template" translate="label comment" type="select" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="error_email_template" translate="label comment" type="select" sortOrder="7" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Error Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> @@ -110,9 +110,9 @@ </group> </section> <section id="system"> - <group id="currency" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="currency" translate="label" type="text" sortOrder="50" showInDefault="1"> <label>Currency</label> - <field id="installed" translate="label" type="multiselect" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="installed" translate="label" type="multiselect" sortOrder="1" showInDefault="1" canRestore="1"> <label>Installed Currencies</label> <backend_model>Magento\Config\Model\Config\Backend\Locale</backend_model> <source_model>Magento\Config\Model\Config\Source\Locale\Currency\All</source_model> @@ -122,20 +122,20 @@ </section> <section id="general"> <group id="country"> - <field id="optional_zip_countries" translate="label" type="multiselect" sortOrder="3" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="optional_zip_countries" translate="label" type="multiselect" sortOrder="3" showInDefault="1" canRestore="1"> <label>Zip/Postal Code is Optional for</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> </field> </group> - <group id="region" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="region" translate="label" type="text" sortOrder="4" showInDefault="1"> <label>State Options</label> - <field id="state_required" translate="label" type="multiselect" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="state_required" translate="label" type="multiselect" sortOrder="1" showInDefault="1"> <label>State is Required for</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> </field> - <field id="display_all" translate="label" type="select" sortOrder="8" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="display_all" translate="label" type="select" sortOrder="8" showInDefault="1"> <label>Allow to Choose State if It is Optional for Country</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> diff --git a/app/code/Magento/Downloadable/etc/adminhtml/system.xml b/app/code/Magento/Downloadable/etc/adminhtml/system.xml index 77f2cc0503631..31b2f64ec018c 100644 --- a/app/code/Magento/Downloadable/etc/adminhtml/system.xml +++ b/app/code/Magento/Downloadable/etc/adminhtml/system.xml @@ -10,15 +10,15 @@ <section id="catalog"> <group id="downloadable" translate="label" type="text" sortOrder="600" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Downloadable Product Options</label> - <field id="order_item_status" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="order_item_status" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Order Item Status to Enable Downloads</label> <source_model>Magento\Downloadable\Model\System\Config\Source\Orderitemstatus</source_model> </field> - <field id="downloads_number" translate="label" type="text" sortOrder="200" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="downloads_number" translate="label" type="text" sortOrder="200" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Default Maximum Number of Downloads</label> <validate>validate-digits validate-zero-or-greater</validate> </field> - <field id="shareable" translate="label" type="select" sortOrder="300" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="shareable" translate="label" type="select" sortOrder="300" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Shareable</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> @@ -28,15 +28,15 @@ <field id="links_title" translate="label" type="text" sortOrder="500" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Default Link Title</label> </field> - <field id="links_target_new_window" translate="label" type="select" sortOrder="600" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="links_target_new_window" translate="label" type="select" sortOrder="600" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Open Links in New Window</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="content_disposition" translate="label" type="select" sortOrder="700" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="content_disposition" translate="label" type="select" sortOrder="700" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Use Content-Disposition</label> <source_model>Magento\Downloadable\Model\System\Config\Source\Contentdisposition</source_model> </field> - <field id="disable_guest_checkout" translate="label comment" type="select" sortOrder="800" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="disable_guest_checkout" translate="label comment" type="select" sortOrder="800" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Disable Guest Checkout if Cart Contains Downloadable Items</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Guest checkout will only work with shareable.</comment> diff --git a/app/code/Magento/Downloadable/etc/config.xml b/app/code/Magento/Downloadable/etc/config.xml index 578ff1e008660..26f2457f6bf3c 100644 --- a/app/code/Magento/Downloadable/etc/config.xml +++ b/app/code/Magento/Downloadable/etc/config.xml @@ -9,8 +9,9 @@ <default> <catalog> <downloadable> - <downloads_number>0</downloads_number> <order_item_status>9</order_item_status> + <downloads_number>0</downloads_number> + <shareable>0</shareable> <samples_title>Samples</samples_title> <links_title>Links</links_title> <links_target_new_window>1</links_target_new_window> diff --git a/app/code/Magento/Eav/etc/adminhtml/system.xml b/app/code/Magento/Eav/etc/adminhtml/system.xml index 86916abe812d9..2fe1564786997 100644 --- a/app/code/Magento/Eav/etc/adminhtml/system.xml +++ b/app/code/Magento/Eav/etc/adminhtml/system.xml @@ -8,9 +8,9 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="dev"> - <group id="caching" translate="label" type="text" sortOrder="120" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="caching" translate="label" type="text" sortOrder="120" showInDefault="1"> <label>Caching Settings</label> - <field id="cache_user_defined_attributes" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="cache_user_defined_attributes" translate="label comment" type="select" sortOrder="10" showInDefault="1" canRestore="1"> <label>Cache User Defined Attributes</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>By default only system EAV attributes are cached.</comment> diff --git a/app/code/Magento/Elasticsearch/etc/adminhtml/system.xml b/app/code/Magento/Elasticsearch/etc/adminhtml/system.xml index 7e3c83dae9847..6d87c4948a9d9 100644 --- a/app/code/Magento/Elasticsearch/etc/adminhtml/system.xml +++ b/app/code/Magento/Elasticsearch/etc/adminhtml/system.xml @@ -10,52 +10,52 @@ <section id="catalog"> <group id="search"> <!-- Elasticsearch 2.0+ --> - <field id="elasticsearch_server_hostname" translate="label" type="text" sortOrder="61" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch_server_hostname" translate="label" type="text" sortOrder="61" showInDefault="1"> <label>Elasticsearch Server Hostname</label> <depends> <field id="engine">elasticsearch</field> </depends> </field> - <field id="elasticsearch_server_port" translate="label" type="text" sortOrder="62" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch_server_port" translate="label" type="text" sortOrder="62" showInDefault="1"> <label>Elasticsearch Server Port</label> <depends> <field id="engine">elasticsearch</field> </depends> </field> - <field id="elasticsearch_index_prefix" translate="label" type="text" sortOrder="63" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch_index_prefix" translate="label" type="text" sortOrder="63" showInDefault="1"> <label>Elasticsearch Index Prefix</label> <depends> <field id="engine">elasticsearch</field> </depends> </field> - <field id="elasticsearch_enable_auth" translate="label" type="select" sortOrder="64" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch_enable_auth" translate="label" type="select" sortOrder="64" showInDefault="1"> <label>Enable Elasticsearch HTTP Auth</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <depends> <field id="engine">elasticsearch</field> </depends> </field> - <field id="elasticsearch_username" translate="label" type="text" sortOrder="65" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch_username" translate="label" type="text" sortOrder="65" showInDefault="1"> <label>Elasticsearch HTTP Username</label> <depends> <field id="engine">elasticsearch</field> <field id="elasticsearch_enable_auth">1</field> </depends> </field> - <field id="elasticsearch_password" translate="label" type="text" sortOrder="66" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch_password" translate="label" type="text" sortOrder="66" showInDefault="1"> <label>Elasticsearch HTTP Password</label> <depends> <field id="engine">elasticsearch</field> <field id="elasticsearch_enable_auth">1</field> </depends> </field> - <field id="elasticsearch_server_timeout" translate="label" type="text" sortOrder="67" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch_server_timeout" translate="label" type="text" sortOrder="67" showInDefault="1"> <label>Elasticsearch Server Timeout</label> <depends> <field id="engine">elasticsearch</field> </depends> </field> - <field id="elasticsearch_test_connect_wizard" translate="button_label" sortOrder="68" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch_test_connect_wizard" translate="button_label" sortOrder="68" showInDefault="1"> <label/> <button_label>Test Connection</button_label> <frontend_model>Magento\Elasticsearch\Block\Adminhtml\System\Config\TestConnection</frontend_model> @@ -63,7 +63,7 @@ <field id="engine">elasticsearch</field> </depends> </field> - <field id="elasticsearch_minimum_should_match" translate="label" type="text" sortOrder="93" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch_minimum_should_match" translate="label" type="text" sortOrder="93" showInDefault="1"> <label>Minimum Terms to Match</label> <depends> <field id="engine">elasticsearch</field> @@ -72,52 +72,52 @@ <backend_model>Magento\Elasticsearch\Model\Config\Backend\MinimumShouldMatch</backend_model> </field> <!-- Elasticsearch 5.x --> - <field id="elasticsearch5_server_hostname" translate="label" type="text" sortOrder="61" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch5_server_hostname" translate="label" type="text" sortOrder="61" showInDefault="1"> <label>Elasticsearch Server Hostname</label> <depends> <field id="engine">elasticsearch5</field> </depends> </field> - <field id="elasticsearch5_server_port" translate="label" type="text" sortOrder="62" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch5_server_port" translate="label" type="text" sortOrder="62" showInDefault="1"> <label>Elasticsearch Server Port</label> <depends> <field id="engine">elasticsearch5</field> </depends> </field> - <field id="elasticsearch5_index_prefix" translate="label" type="text" sortOrder="63" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch5_index_prefix" translate="label" type="text" sortOrder="63" showInDefault="1"> <label>Elasticsearch Index Prefix</label> <depends> <field id="engine">elasticsearch5</field> </depends> </field> - <field id="elasticsearch5_enable_auth" translate="label" type="select" sortOrder="64" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch5_enable_auth" translate="label" type="select" sortOrder="64" showInDefault="1"> <label>Enable Elasticsearch HTTP Auth</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <depends> <field id="engine">elasticsearch5</field> </depends> </field> - <field id="elasticsearch5_username" translate="label" type="text" sortOrder="65" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch5_username" translate="label" type="text" sortOrder="65" showInDefault="1"> <label>Elasticsearch HTTP Username</label> <depends> <field id="engine">elasticsearch5</field> <field id="elasticsearch5_enable_auth">1</field> </depends> </field> - <field id="elasticsearch5_password" translate="label" type="text" sortOrder="66" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch5_password" translate="label" type="text" sortOrder="66" showInDefault="1"> <label>Elasticsearch HTTP Password</label> <depends> <field id="engine">elasticsearch5</field> <field id="elasticsearch5_enable_auth">1</field> </depends> </field> - <field id="elasticsearch5_server_timeout" translate="label" type="text" sortOrder="67" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch5_server_timeout" translate="label" type="text" sortOrder="67" showInDefault="1"> <label>Elasticsearch Server Timeout</label> <depends> <field id="engine">elasticsearch5</field> </depends> </field> - <field id="elasticsearch5_test_connect_wizard" translate="button_label" sortOrder="68" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch5_test_connect_wizard" translate="button_label" sortOrder="68" showInDefault="1"> <label/> <button_label>Test Connection</button_label> <frontend_model>Magento\Elasticsearch\Block\Adminhtml\System\Config\Elasticsearch5\TestConnection</frontend_model> @@ -125,7 +125,7 @@ <field id="engine">elasticsearch5</field> </depends> </field> - <field id="elasticsearch5_minimum_should_match" translate="label" type="text" sortOrder="93" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch5_minimum_should_match" translate="label" type="text" sortOrder="93" showInDefault="1"> <label>Minimum Terms to Match</label> <depends> <field id="engine">elasticsearch5</field> diff --git a/app/code/Magento/Elasticsearch6/etc/adminhtml/system.xml b/app/code/Magento/Elasticsearch6/etc/adminhtml/system.xml index 5c6a9357a5f6f..fee234ada43b4 100644 --- a/app/code/Magento/Elasticsearch6/etc/adminhtml/system.xml +++ b/app/code/Magento/Elasticsearch6/etc/adminhtml/system.xml @@ -11,32 +11,28 @@ <section id="catalog"> <group id="search"> <!-- Elasticsearch 6.x --> - <field id="elasticsearch6_server_hostname" translate="label" type="text" sortOrder="71" - showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="elasticsearch6_server_hostname" translate="label" type="text" sortOrder="71" showInDefault="1"> <label>Elasticsearch Server Hostname</label> <depends> <field id="engine">elasticsearch6</field> </depends> </field> - <field id="elasticsearch6_server_port" translate="label" type="text" sortOrder="72" showInDefault="1" - showInWebsite="0" showInStore="0"> + <field id="elasticsearch6_server_port" translate="label" type="text" sortOrder="72" showInDefault="1"> <label>Elasticsearch Server Port</label> <depends> <field id="engine">elasticsearch6</field> </depends> </field> - <field id="elasticsearch6_index_prefix" translate="label" type="text" sortOrder="73" showInDefault="1" - showInWebsite="0" showInStore="0"> + <field id="elasticsearch6_index_prefix" translate="label" type="text" sortOrder="73" showInDefault="1"> <label>Elasticsearch Index Prefix</label> <depends> <field id="engine">elasticsearch6</field> </depends> </field> - <field id="elasticsearch6_enable_auth" translate="label" type="select" sortOrder="74" showInDefault="1" - showInWebsite="0" showInStore="0"> + <field id="elasticsearch6_enable_auth" translate="label" type="select" sortOrder="74" showInDefault="1"> <label>Enable Elasticsearch HTTP Auth</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <depends> @@ -44,8 +40,7 @@ </depends> </field> - <field id="elasticsearch6_username" translate="label" type="text" sortOrder="75" showInDefault="1" - showInWebsite="0" showInStore="0"> + <field id="elasticsearch6_username" translate="label" type="text" sortOrder="75" showInDefault="1"> <label>Elasticsearch HTTP Username</label> <depends> <field id="engine">elasticsearch6</field> @@ -53,8 +48,7 @@ </depends> </field> - <field id="elasticsearch6_password" translate="label" type="text" sortOrder="76" showInDefault="1" - showInWebsite="0" showInStore="0"> + <field id="elasticsearch6_password" translate="label" type="text" sortOrder="76" showInDefault="1"> <label>Elasticsearch HTTP Password</label> <depends> <field id="engine">elasticsearch6</field> @@ -62,16 +56,14 @@ </depends> </field> - <field id="elasticsearch6_server_timeout" translate="label" type="text" sortOrder="77" showInDefault="1" - showInWebsite="0" showInStore="0"> + <field id="elasticsearch6_server_timeout" translate="label" type="text" sortOrder="77" showInDefault="1"> <label>Elasticsearch Server Timeout</label> <depends> <field id="engine">elasticsearch6</field> </depends> </field> - <field id="elasticsearch6_test_connect_wizard" translate="button_label" sortOrder="78" showInDefault="1" - showInWebsite="0" showInStore="0"> + <field id="elasticsearch6_test_connect_wizard" translate="button_label" sortOrder="78" showInDefault="1"> <label/> <button_label>Test Connection</button_label> <frontend_model>Magento\Elasticsearch6\Block\Adminhtml\System\Config\TestConnection</frontend_model> @@ -79,8 +71,7 @@ <field id="engine">elasticsearch6</field> </depends> </field> - <field id="elasticsearch6_minimum_should_match" translate="label" type="text" sortOrder="93" showInDefault="1" - showInWebsite="0" showInStore="0"> + <field id="elasticsearch6_minimum_should_match" translate="label" type="text" sortOrder="93" showInDefault="1"> <label>Minimum Terms to Match</label> <depends> <field id="engine">elasticsearch6</field> diff --git a/app/code/Magento/Email/etc/config.xml b/app/code/Magento/Email/etc/config.xml index 0731fc79c15f7..a9837a4f2917c 100644 --- a/app/code/Magento/Email/etc/config.xml +++ b/app/code/Magento/Email/etc/config.xml @@ -27,6 +27,7 @@ <disable>0</disable> <host>localhost</host> <port>25</port> + <set_return_path>0</set_return_path> </smtp> </system> <trans_email> diff --git a/app/code/Magento/Fedex/etc/adminhtml/system.xml b/app/code/Magento/Fedex/etc/adminhtml/system.xml index fdbe10a1a352e..f164a8e21e0ae 100644 --- a/app/code/Magento/Fedex/etc/adminhtml/system.xml +++ b/app/code/Magento/Fedex/etc/adminhtml/system.xml @@ -10,101 +10,101 @@ <section id="carriers"> <group id="fedex" translate="label" type="text" sortOrder="120" showInDefault="1" showInWebsite="1" showInStore="1"> <label>FedEx</label> - <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enabled for Checkout</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> <field id="title" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Title</label> </field> - <field id="account" translate="label comment" type="obscure" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="account" translate="label comment" type="obscure" sortOrder="40" showInDefault="1" showInWebsite="1"> <label>Account ID</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> <comment>Please make sure to use only digits here. No dashes are allowed.</comment> </field> - <field id="meter_number" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="meter_number" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1"> <label>Meter Number</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> - <field id="key" translate="label" type="obscure" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="key" translate="label" type="obscure" sortOrder="60" showInDefault="1" showInWebsite="1"> <label>Key</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> - <field id="password" translate="label" type="obscure" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="password" translate="label" type="obscure" sortOrder="70" showInDefault="1" showInWebsite="1"> <label>Password</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> - <field id="sandbox_mode" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="sandbox_mode" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Sandbox Mode</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="production_webservices_url" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="production_webservices_url" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Web-Services URL (Production)</label> <depends> <field id="sandbox_mode">0</field> </depends> </field> - <field id="sandbox_webservices_url" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="sandbox_webservices_url" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Web-Services URL (Sandbox)</label> <depends> <field id="sandbox_mode">1</field> </depends> </field> - <field id="shipment_requesttype" translate="label" type="select" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="shipment_requesttype" translate="label" type="select" sortOrder="110" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Packages Request Type</label> <source_model>Magento\Shipping\Model\Config\Source\Online\Requesttype</source_model> </field> - <field id="packaging" translate="label" type="select" sortOrder="120" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="packaging" translate="label" type="select" sortOrder="120" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Packaging</label> <source_model>Magento\Fedex\Model\Source\Packaging</source_model> </field> - <field id="dropoff" translate="label" type="select" sortOrder="130" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="dropoff" translate="label" type="select" sortOrder="130" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Dropoff</label> <source_model>Magento\Fedex\Model\Source\Dropoff</source_model> </field> - <field id="unit_of_measure" translate="label" type="select" sortOrder="135" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="unit_of_measure" translate="label" type="select" sortOrder="135" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Weight Unit</label> <source_model>Magento\Fedex\Model\Source\Unitofmeasure</source_model> </field> - <field id="max_package_weight" translate="label" type="text" sortOrder="140" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="max_package_weight" translate="label" type="text" sortOrder="140" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Maximum Package Weight (Please consult your shipping carrier for maximum supported shipping weight)</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="handling_type" translate="label" type="select" sortOrder="150" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="handling_type" translate="label" type="select" sortOrder="150" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Calculate Handling Fee</label> <source_model>Magento\Shipping\Model\Source\HandlingType</source_model> </field> - <field id="handling_action" translate="label" type="select" sortOrder="160" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="handling_action" translate="label" type="select" sortOrder="160" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Handling Applied</label> <source_model>Magento\Shipping\Model\Source\HandlingAction</source_model> </field> - <field id="handling_fee" translate="label" type="text" sortOrder="170" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="handling_fee" translate="label" type="text" sortOrder="170" showInDefault="1" showInWebsite="1"> <label>Handling Fee</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="residence_delivery" translate="label" type="select" sortOrder="180" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="residence_delivery" translate="label" type="select" sortOrder="180" showInDefault="1" showInWebsite="1"> <label>Residential Delivery</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="allowed_methods" translate="label" type="multiselect" sortOrder="190" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allowed_methods" translate="label" type="multiselect" sortOrder="190" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Allowed Methods</label> <source_model>Magento\Fedex\Model\Source\Method</source_model> <can_be_empty>1</can_be_empty> </field> - <field id="smartpost_hubid" translate="label comment" type="text" sortOrder="200" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="smartpost_hubid" translate="label comment" type="text" sortOrder="200" showInDefault="1" showInWebsite="1"> <label>Hub ID</label> <comment>The field is applicable if the Smart Post method is selected.</comment> </field> - <field id="free_method" translate="label" type="select" sortOrder="210" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="free_method" translate="label" type="select" sortOrder="210" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Free Method</label> <frontend_class>free-method</frontend_class> <source_model>Magento\Fedex\Model\Source\Freemethod</source_model> </field> - <field id="free_shipping_enable" translate="label" type="select" sortOrder="220" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="free_shipping_enable" translate="label" type="select" sortOrder="220" showInDefault="1" showInWebsite="1"> <label>Enable Free Shipping Threshold</label> <source_model>Magento\Config\Model\Config\Source\Enabledisable</source_model> </field> - <field id="free_shipping_subtotal" translate="label" type="text" sortOrder="230" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="free_shipping_subtotal" translate="label" type="text" sortOrder="230" showInDefault="1" showInWebsite="1"> <label>Free Shipping Amount Threshold</label> <validate>validate-number validate-zero-or-greater</validate> <depends> @@ -114,26 +114,26 @@ <field id="specificerrmsg" translate="label" type="textarea" sortOrder="240" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Displayed Error Message</label> </field> - <field id="sallowspecific" translate="label" type="select" sortOrder="250" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="sallowspecific" translate="label" type="select" sortOrder="250" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Ship to Applicable Countries</label> <frontend_class>shipping-applicable-country</frontend_class> <source_model>Magento\Shipping\Model\Config\Source\Allspecificcountries</source_model> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="260" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="260" showInDefault="1" showInWebsite="1"> <label>Ship to Specific Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> </field> - <field id="debug" translate="label" type="select" sortOrder="270" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="debug" translate="label" type="select" sortOrder="270" showInDefault="1" showInWebsite="1"> <label>Debug</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="showmethod" translate="label" type="select" sortOrder="280" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="showmethod" translate="label" type="select" sortOrder="280" showInDefault="1" showInWebsite="1"> <label>Show Method if Not Applicable</label> <frontend_class>shipping-skip-hide</frontend_class> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="sort_order" translate="label" type="text" sortOrder="290" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="290" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <validate>validate-number validate-zero-or-greater</validate> </field> diff --git a/app/code/Magento/GiftMessage/etc/adminhtml/system.xml b/app/code/Magento/GiftMessage/etc/adminhtml/system.xml index 6779ea6f28174..fa437a0735cf4 100644 --- a/app/code/Magento/GiftMessage/etc/adminhtml/system.xml +++ b/app/code/Magento/GiftMessage/etc/adminhtml/system.xml @@ -8,13 +8,13 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="sales"> - <group id="gift_options" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="gift_options" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1"> <label>Gift Options</label> - <field id="allow_order" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allow_order" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Allow Gift Messages on Order Level</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="allow_items" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allow_items" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Allow Gift Messages for Order Items</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> diff --git a/app/code/Magento/GoogleAdwords/Test/Mftf/ActionGroup/AdminNavigateToGoogleAdwordsConfigurationActionGroup.xml b/app/code/Magento/GoogleAdwords/Test/Mftf/ActionGroup/AdminNavigateToGoogleAdwordsConfigurationActionGroup.xml new file mode 100644 index 0000000000000..c581862a3b34c --- /dev/null +++ b/app/code/Magento/GoogleAdwords/Test/Mftf/ActionGroup/AdminNavigateToGoogleAdwordsConfigurationActionGroup.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminNavigateToGoogleAdwordsConfigurationActionGroup"> + <amOnPage url="{{AdminGoogleAdwordsConfigPage.url}}" stepKey="navigateToConfigurationPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/GoogleAdwords/Test/Mftf/Page/AdminGoogleAdwordsConfigPage.xml b/app/code/Magento/GoogleAdwords/Test/Mftf/Page/AdminGoogleAdwordsConfigPage.xml new file mode 100644 index 0000000000000..842b867ed9407 --- /dev/null +++ b/app/code/Magento/GoogleAdwords/Test/Mftf/Page/AdminGoogleAdwordsConfigPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminGoogleAdwordsConfigPage" url="admin/system_config/edit/section/google/" area="admin" + module="Magento_Config"> + <section name="AdminGoogleAdwordsConfigSection"/> + </page> +</pages> diff --git a/app/code/Magento/GoogleAdwords/Test/Mftf/Section/AdminGoogleAdwordsConfigSection.xml b/app/code/Magento/GoogleAdwords/Test/Mftf/Section/AdminGoogleAdwordsConfigSection.xml new file mode 100644 index 0000000000000..d6ee2ced1afe1 --- /dev/null +++ b/app/code/Magento/GoogleAdwords/Test/Mftf/Section/AdminGoogleAdwordsConfigSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminGoogleAdwordsConfigSection"> + <element name="active" type="text" selector="#google_adwords_active"/> + <element name="conversionId" type="text" selector="#google_adwords_conversion_id"/> + </section> +</sections> diff --git a/app/code/Magento/GoogleAdwords/Test/Mftf/Test/AdminValidateConversionIdConfigTest.xml b/app/code/Magento/GoogleAdwords/Test/Mftf/Test/AdminValidateConversionIdConfigTest.xml new file mode 100644 index 0000000000000..7c0214a8654c8 --- /dev/null +++ b/app/code/Magento/GoogleAdwords/Test/Mftf/Test/AdminValidateConversionIdConfigTest.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminValidateConversionIdConfigTest"> + <annotations> + <stories value="Admin validates the conversion ID when configuring the Google Adwords"/> + <title value="Admin validates the conversion ID when configuring the Google Adwords"/> + <description value="Testing for a required Conversion ID when configuring the Google Adwords"/> + <severity value="MINOR"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <actionGroup ref="AdminNavigateToGoogleAdwordsConfigurationActionGroup" stepKey="goToConfigPage"/> + <actionGroup ref="AdminExpandConfigSectionActionGroup" stepKey="expandingGoogleAdwordsSection"> + <argument name="sectionName" value="Google AdWords"/> + </actionGroup> + <actionGroup ref="AdminUncheckUseSystemValueActionGroup" stepKey="uncheckUseSystemValue"> + <argument name="rowId" value="row_google_adwords_active"/> + </actionGroup> + <actionGroup ref="AdminToggleEnabledActionGroup" stepKey="enableGoogleAdwordsConfig"> + <argument name="element" value="{{AdminGoogleAdwordsConfigSection.active}}"/> + </actionGroup> + <actionGroup ref="AdminClickFormActionButtonActionGroup" stepKey="clickSaveCustomVariable"> + <argument name="buttonSelector" value="{{AdminMainActionsSection.save}}"/> + </actionGroup> + <actionGroup ref="AssertAdminValidationErrorActionGroup" stepKey="seeRequiredValidationErrorForConversionId"> + <argument name="inputId" value="google_adwords_conversion_id"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/GoogleAdwords/etc/adminhtml/system.xml b/app/code/Magento/GoogleAdwords/etc/adminhtml/system.xml index c312028cf63be..74a16ae6acc62 100644 --- a/app/code/Magento/GoogleAdwords/etc/adminhtml/system.xml +++ b/app/code/Magento/GoogleAdwords/etc/adminhtml/system.xml @@ -17,6 +17,7 @@ <field id="conversion_id" translate="label" type="text" sortOrder="11" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Conversion ID</label> <backend_model>Magento\GoogleAdwords\Model\Config\Backend\ConversionId</backend_model> + <validate>required-entry validate-number</validate> <depends> <field id="*/*/active">1</field> </depends> diff --git a/app/code/Magento/GoogleAnalytics/etc/adminhtml/system.xml b/app/code/Magento/GoogleAnalytics/etc/adminhtml/system.xml index 3491b4d456b81..dd59de40a8528 100644 --- a/app/code/Magento/GoogleAnalytics/etc/adminhtml/system.xml +++ b/app/code/Magento/GoogleAnalytics/etc/adminhtml/system.xml @@ -13,7 +13,7 @@ <resource>Magento_GoogleAnalytics::google</resource> <group id="analytics" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Google Analytics</label> - <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Enable</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> diff --git a/app/code/Magento/GoogleAnalytics/etc/config.xml b/app/code/Magento/GoogleAnalytics/etc/config.xml new file mode 100644 index 0000000000000..f3c9cc67f86ae --- /dev/null +++ b/app/code/Magento/GoogleAnalytics/etc/config.xml @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> + <default> + <google> + <analytics> + <active>0</active> + </analytics> + </google> + </default> +</config> diff --git a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php index 187fd27fa0554..8eac8d0b0e163 100644 --- a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php +++ b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php @@ -344,7 +344,7 @@ protected function getProductInfo(\Magento\Framework\DataObject $buyRequest, $pr } foreach ($associatedProducts as $subProduct) { if (!isset($productsInfo[$subProduct->getId()])) { - if ($isStrictProcessMode && !$subProduct->getQty()) { + if ($isStrictProcessMode && !$subProduct->getQty() && $subProduct->isSalable()) { return __('Please specify the quantity of product(s).')->render(); } $productsInfo[$subProduct->getId()] = $subProduct->isSalable() ? (float)$subProduct->getQty() : 0; diff --git a/app/code/Magento/GroupedProduct/Model/Wishlist/Product/Item.php b/app/code/Magento/GroupedProduct/Model/Wishlist/Product/Item.php new file mode 100644 index 0000000000000..d84df510195f3 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Model/Wishlist/Product/Item.php @@ -0,0 +1,93 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GroupedProduct\Model\Wishlist\Product; + +use Magento\Wishlist\Model\Item as WishlistItem; +use Magento\GroupedProduct\Model\Product\Type\Grouped as TypeGrouped; +use Magento\Catalog\Model\Product; + +/** + * Wishlist logic for grouped product + */ +class Item +{ + /** + * Modify Wishlist item based on associated product qty + * + * @param WishlistItem $subject + * @param Product $product + * @return array + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function beforeRepresentProduct( + WishlistItem $subject, + Product $product + ): array { + if ($product->getTypeId() === TypeGrouped::TYPE_CODE + && $product->getId() === $subject->getProduct()->getId() + ) { + $itemOptions = $subject->getOptionsByCode(); + $productOptions = $product->getCustomOptions(); + + $diff = array_diff_key($itemOptions, $productOptions); + + if (!$diff) { + $buyRequest = $subject->getBuyRequest(); + $superGroupInfo = $buyRequest->getData('super_group'); + + foreach ($itemOptions as $key => $itemOption) { + if (preg_match('/associated_product_\d+/', $key)) { + $simpleId = str_replace('associated_product_', '', $key); + $prodQty = $productOptions[$key]->getValue(); + + $itemOption->setValue($itemOption->getValue() + $prodQty); + + if (isset($superGroupInfo[$simpleId])) { + $superGroupInfo[$simpleId] = $itemOptions[$key]->getValue(); + } + } + } + + $buyRequest->setData('super_group', $superGroupInfo); + + $subject->setOptions($itemOptions); + $subject->mergeBuyRequest($buyRequest); + } + } + + return [$product]; + } + + /** + * Remove associated_product_id key. associated_product_id contains qty + * + * @param WishlistItem $subject + * @param array $options1 + * @param array $options2 + * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeCompareOptions( + WishlistItem $subject, + array $options1, + array $options2 + ): array { + $diff = array_diff_key($options1, $options2); + + if (!$diff) { + foreach (array_keys($options1) as $key) { + if (preg_match('/associated_product_\d+/', $key)) { + unset($options1[$key]); + unset($options2[$key]); + } + } + } + + return [$options1, $options2]; + } +} diff --git a/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Type/GroupedTest.php b/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Type/GroupedTest.php index e50d6491a6aca..3af5c1c726593 100644 --- a/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Type/GroupedTest.php +++ b/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Type/GroupedTest.php @@ -8,6 +8,8 @@ use Magento\GroupedProduct\Model\Product\Type\Grouped; /** + * Tests for Grouped product + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class GroupedTest extends \PHPUnit\Framework\TestCase @@ -42,6 +44,9 @@ class GroupedTest extends \PHPUnit\Framework\TestCase */ private $serializer; + /** + * @inheritdoc + */ protected function setUp() { $this->objectHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -74,12 +79,22 @@ protected function setUp() ); } - public function testHasWeightFalse() + /** + * Verify has weight is false + * + * @return void + */ + public function testHasWeightFalse(): void { $this->assertFalse($this->_model->hasWeight(), 'This product has weight, but it should not'); } - public function testGetChildrenIds() + /** + * Verify children ids. + * + * @return void + */ + public function testGetChildrenIds(): void { $parentId = 12345; $childrenIds = [100, 200, 300]; @@ -96,7 +111,12 @@ public function testGetChildrenIds() $this->assertEquals($childrenIds, $this->_model->getChildrenIds($parentId)); } - public function testGetParentIdsByChild() + /** + * Verify get parents by child products + * + * @return void + */ + public function testGetParentIdsByChild(): void { $childId = 12345; $parentIds = [100, 200, 300]; @@ -113,7 +133,12 @@ public function testGetParentIdsByChild() $this->assertEquals($parentIds, $this->_model->getParentIdsByChild($childId)); } - public function testGetAssociatedProducts() + /** + * Verify get associated products + * + * @return void + */ + public function testGetAssociatedProducts(): void { $cached = true; $associatedProducts = [5, 7, 11, 13, 17]; @@ -123,12 +148,14 @@ public function testGetAssociatedProducts() } /** + * Verify able to set status filter + * * @param int $status * @param array $filters * @param array $result * @dataProvider addStatusFilterDataProvider */ - public function testAddStatusFilter($status, $filters, $result) + public function testAddStatusFilter($status, $filters, $result): void { $this->product->expects($this->once())->method('getData')->will($this->returnValue($filters)); $this->product->expects($this->once())->method('setData')->with('_cache_instance_status_filters', $result); @@ -136,14 +163,21 @@ public function testAddStatusFilter($status, $filters, $result) } /** + * Data Provider for Status Filter + * * @return array */ - public function addStatusFilterDataProvider() + public function addStatusFilterDataProvider(): array { return [[1, [], [1]], [1, false, [1]]]; } - public function testSetSaleableStatus() + /** + * Verify able to set salable status + * + * @return void + */ + public function testSetSaleableStatus(): void { $key = '_cache_instance_status_filters'; $saleableIds = [300, 800, 500]; @@ -159,7 +193,12 @@ public function testSetSaleableStatus() $this->assertEquals($this->_model, $this->_model->setSaleableStatus($this->product)); } - public function testGetStatusFiltersNoData() + /** + * Verify status filter with no data. + * + * @return void + */ + public function testGetStatusFiltersNoData(): void { $result = [ \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED, @@ -169,7 +208,12 @@ public function testGetStatusFiltersNoData() $this->assertEquals($result, $this->_model->getStatusFilters($this->product)); } - public function testGetStatusFiltersWithData() + /** + * Verify status filter with data + * + * @return void + */ + public function testGetStatusFiltersWithData(): void { $result = [ \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED, @@ -180,7 +224,12 @@ public function testGetStatusFiltersWithData() $this->assertEquals($result, $this->_model->getStatusFilters($this->product)); } - public function testGetAssociatedProductIdsCached() + /** + * Verify AssociatedProducts Ids with cache + * + * @return void + */ + public function testGetAssociatedProductIdsCached(): void { $key = '_cache_instance_associated_product_ids'; $cachedData = [300, 303, 306]; @@ -192,7 +241,12 @@ public function testGetAssociatedProductIdsCached() $this->assertEquals($cachedData, $this->_model->getAssociatedProductIds($this->product)); } - public function testGetAssociatedProductIdsNonCached() + /** + * Verify AssociatedProducts Ids with no cached. + * + * @return void + */ + public function testGetAssociatedProductIdsNonCached(): void { $args = $this->objectHelper->getConstructArguments( \Magento\GroupedProduct\Model\Product\Type\Grouped::class, @@ -236,7 +290,12 @@ public function testGetAssociatedProductIdsNonCached() $this->assertEquals($associatedIds, $model->getAssociatedProductIds($this->product)); } - public function testGetAssociatedProductCollection() + /** + * Verify Associated Product collection + * + * @return void + */ + public function testGetAssociatedProductCollection(): void { $link = $this->createPartialMock( \Magento\Catalog\Model\Product\Link::class, @@ -261,6 +320,8 @@ public function testGetAssociatedProductCollection() } /** + * Verify Proccess buy request + * * @param array $superGroup * @param array $result * @dataProvider processBuyRequestDataProvider @@ -274,9 +335,11 @@ public function testProcessBuyRequest($superGroup, $result) } /** + * dataProvider for buy request + * * @return array */ - public function processBuyRequestDataProvider() + public function processBuyRequestDataProvider(): array { return [ 'positive' => [[1, 2, 3], ['super_group' => [1, 2, 3]]], @@ -285,9 +348,12 @@ public function processBuyRequestDataProvider() } /** + * Get Children Msrp when children product with Msrp + * + * @return void * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ - public function testGetChildrenMsrpWhenNoChildrenWithMsrp() + public function testGetChildrenMsrpWhenNoChildrenWithMsrp(): void { $key = '_cache_instance_associated_products'; @@ -298,7 +364,12 @@ public function testGetChildrenMsrpWhenNoChildrenWithMsrp() $this->assertEquals(0, $this->_model->getChildrenMsrp($this->product)); } - public function testPrepareForCartAdvancedEmpty() + /** + * Prepare for card method with advanced empty + * + * @return void + */ + public function testPrepareForCartAdvancedEmpty(): void { $this->product = $this->createMock(\Magento\Catalog\Model\Product::class); $buyRequest = new \Magento\Framework\DataObject(); @@ -381,7 +452,12 @@ public function testPrepareForCartAdvancedEmpty() ); } - public function testPrepareForCartAdvancedNoProductsStrictTrue() + /** + * Prepare for card with no products set strict option true + * + * @return void + */ + public function testPrepareForCartAdvancedNoProductsStrictTrue(): void { $buyRequest = new \Magento\Framework\DataObject(); $buyRequest->setSuperGroup([0 => 0]); @@ -404,7 +480,12 @@ public function testPrepareForCartAdvancedNoProductsStrictTrue() ); } - public function testPrepareForCartAdvancedNoProductsStrictFalse() + /** + * Prepare for card with no products and set strict to false + * + * @return void + */ + public function testPrepareForCartAdvancedNoProductsStrictFalse(): void { $buyRequest = new \Magento\Framework\DataObject(); $buyRequest->setSuperGroup([0 => 0]); @@ -429,7 +510,12 @@ public function testPrepareForCartAdvancedNoProductsStrictFalse() ); } - public function testPrepareForCartAdvancedWithProductsStrictFalseStringResult() + /** + * Verify Prepare for cart product with Product strict flase and string result + * + * @return false + */ + public function testPrepareForCartAdvancedWithProductsStrictFalseStringResult(): void { $associatedProduct = $this->createMock(\Magento\Catalog\Model\Product::class); $associatedId = 9384; @@ -463,7 +549,12 @@ public function testPrepareForCartAdvancedWithProductsStrictFalseStringResult() ); } - public function testPrepareForCartAdvancedWithProductsStrictFalseEmptyArrayResult() + /** + * Verify prepare for cart with strict option set to false and empty array + * + * @return void + */ + public function testPrepareForCartAdvancedWithProductsStrictFalseEmptyArrayResult(): void { $expectedMsg = "Cannot process the item."; $associatedProduct = $this->createMock(\Magento\Catalog\Model\Product::class); @@ -498,7 +589,12 @@ public function testPrepareForCartAdvancedWithProductsStrictFalseEmptyArrayResul ); } - public function testPrepareForCartAdvancedWithProductsStrictFalse() + /** + * Prepare for cart product with Product strict option st to false. + * + * @return void + */ + public function testPrepareForCartAdvancedWithProductsStrictFalse(): void { $associatedProduct = $this->createMock(\Magento\Catalog\Model\Product::class); $associatedId = 9384; @@ -541,7 +637,12 @@ public function testPrepareForCartAdvancedWithProductsStrictFalse() ); } - public function testPrepareForCartAdvancedWithProductsStrictTrue() + /** + * Verify prepare for cart with Product strict option true + * + * @return void + */ + public function testPrepareForCartAdvancedWithProductsStrictTrue(): void { $associatedProduct = $this->createMock(\Magento\Catalog\Model\Product::class); $associatedId = 9384; @@ -587,15 +688,20 @@ public function testPrepareForCartAdvancedWithProductsStrictTrue() ); } - public function testPrepareForCartAdvancedZeroQty() + /** + * Verify prepare for card with sold out option + * + * @return void + */ + public function testPrepareForCartAdvancedZeroQtyAndSoldOutOption(): void { $expectedMsg = "Please specify the quantity of product(s)."; - $associatedId = 9384; + $associatedId = 91; $associatedProduct = $this->createMock(\Magento\Catalog\Model\Product::class); - $associatedProduct->expects($this->atLeastOnce())->method('getId')->will($this->returnValue($associatedId)); - + $associatedProduct->expects($this->atLeastOnce())->method('getId')->will($this->returnValue(90)); + $associatedProduct->expects($this->once())->method('isSalable')->willReturn(true); $buyRequest = new \Magento\Framework\DataObject(); - $buyRequest->setSuperGroup([$associatedId => 0]); + $buyRequest->setSuperGroup([$associatedId => 90]); $cached = true; $this->product @@ -609,7 +715,12 @@ public function testPrepareForCartAdvancedZeroQty() $this->assertEquals($expectedMsg, $this->_model->prepareForCartAdvanced($buyRequest, $this->product)); } - public function testFlushAssociatedProductsCache() + /** + * Verify flush cache for associated products + * + * @return void + */ + public function testFlushAssociatedProductsCache(): void { $productMock = $this->createPartialMock(\Magento\Catalog\Model\Product::class, ['unsetData']); $productMock->expects($this->once()) diff --git a/app/code/Magento/GroupedProduct/Test/Unit/Model/Wishlist/Product/ItemTest.php b/app/code/Magento/GroupedProduct/Test/Unit/Model/Wishlist/Product/ItemTest.php new file mode 100644 index 0000000000000..1edf5e8ce2d95 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Unit/Model/Wishlist/Product/ItemTest.php @@ -0,0 +1,183 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\GroupedProduct\Test\Unit\Model\Wishlist\Product; + +use Magento\GroupedProduct\Model\Product\Type\Grouped as TypeGrouped; + +/** + * Unit test for Wishlist Item Plugin. + */ +class ItemTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\GroupedProduct\Model\Wishlist\Product\Item + */ + protected $model; + + /** + * @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject + */ + protected $productMock; + + /** + * @var \Magento\Wishlist\Model\Item|\PHPUnit_Framework_MockObject_MockObject + */ + protected $subjectMock; + + /** + * Init Mock Objects + */ + protected function setUp() + { + $this->subjectMock = $this->createPartialMock( + \Magento\Wishlist\Model\Item::class, + [ + 'getOptionsByCode', + 'getBuyRequest', + 'setOptions', + 'mergeBuyRequest', + 'getProduct' + ] + ); + + $this->productMock = $this->createPartialMock( + \Magento\Catalog\Model\Product::class, + [ + 'getId', + 'getTypeId', + 'getCustomOptions' + ] + ); + + $this->model = new \Magento\GroupedProduct\Model\Wishlist\Product\Item(); + } + + /** + * Test Before Represent Product method + */ + public function testBeforeRepresentProduct() + { + $testSimpleProdId = 34; + $prodInitQty = 2; + $prodQtyInWishlist = 3; + $resWishlistQty = $prodInitQty + $prodQtyInWishlist; + $superGroup = [ + 'super_group' => [ + 33 => "0", + 34 => 3, + 35 => "0" + ] + ]; + + $superGroupObj = new \Magento\Framework\DataObject($superGroup); + + $this->productMock->expects($this->once())->method('getId')->willReturn($testSimpleProdId); + $this->productMock->expects($this->once())->method('getTypeId') + ->willReturn(TypeGrouped::TYPE_CODE); + $this->productMock->expects($this->once())->method('getCustomOptions') + ->willReturn( + $this->getProductAssocOption($prodInitQty, $testSimpleProdId) + ); + + $wishlistItemProductMock = $this->createPartialMock( + \Magento\Catalog\Model\Product::class, + [ + 'getId', + ] + ); + $wishlistItemProductMock->expects($this->once())->method('getId')->willReturn($testSimpleProdId); + + $this->subjectMock->expects($this->once())->method('getProduct') + ->willReturn($wishlistItemProductMock); + $this->subjectMock->expects($this->once())->method('getOptionsByCode') + ->willReturn( + $this->getWishlistAssocOption($prodQtyInWishlist, $resWishlistQty, $testSimpleProdId) + ); + $this->subjectMock->expects($this->once())->method('getBuyRequest')->willReturn($superGroupObj); + + $this->model->beforeRepresentProduct($this->subjectMock, $this->productMock); + } + + /** + * Test Before Compare Options method with same keys + */ + public function testBeforeCompareOptionsSameKeys() + { + $options1 = ['associated_product_34' => 3]; + $options2 = ['associated_product_34' => 2]; + + $res = $this->model->beforeCompareOptions($this->subjectMock, $options1, $options2); + + $this->assertEquals([], $res[0]); + $this->assertEquals([], $res[1]); + } + + /** + * Test Before Compare Options method with diff keys + */ + public function testBeforeCompareOptionsDiffKeys() + { + $options1 = ['associated_product_1' => 3]; + $options2 = ['associated_product_34' => 2]; + + $res = $this->model->beforeCompareOptions($this->subjectMock, $options1, $options2); + + $this->assertEquals($options1, $res[0]); + $this->assertEquals($options2, $res[1]); + } + + /** + * Return mock array with wishlist options + * + * @param int $initVal + * @param int $resVal + * @param int $prodId + * @return array + */ + private function getWishlistAssocOption($initVal, $resVal, $prodId) + { + $items = []; + + $optionMock = $this->createPartialMock( + \Magento\Wishlist\Model\Item\Option::class, + [ + 'getValue', + ] + ); + $optionMock->expects($this->at(0))->method('getValue')->willReturn($initVal); + $optionMock->expects($this->at(1))->method('getValue')->willReturn($resVal); + + $items['associated_product_' . $prodId] = $optionMock; + + return $items; + } + + /** + * Return mock array with product options + * + * @param int $initVal + * @param int $prodId + * @return array + */ + private function getProductAssocOption($initVal, $prodId) + { + $items = []; + + $optionMock = $this->createPartialMock( + \Magento\Catalog\Model\Product\Configuration\Item\Option::class, + [ + 'getValue', + ] + ); + + $optionMock->expects($this->once())->method('getValue')->willReturn($initVal); + + $items['associated_product_' . $prodId] = $optionMock; + + return $items; + } +} diff --git a/app/code/Magento/GroupedProduct/composer.json b/app/code/Magento/GroupedProduct/composer.json index 68063c05ddf7b..3cb41387d2c6d 100644 --- a/app/code/Magento/GroupedProduct/composer.json +++ b/app/code/Magento/GroupedProduct/composer.json @@ -18,7 +18,8 @@ "magento/module-quote": "*", "magento/module-sales": "*", "magento/module-store": "*", - "magento/module-ui": "*" + "magento/module-ui": "*", + "magento/module-wishlist": "*" }, "suggest": { "magento/module-grouped-product-sample-data": "*" diff --git a/app/code/Magento/GroupedProduct/etc/frontend/di.xml b/app/code/Magento/GroupedProduct/etc/frontend/di.xml new file mode 100644 index 0000000000000..21ed87e91cb6c --- /dev/null +++ b/app/code/Magento/GroupedProduct/etc/frontend/di.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\Wishlist\Model\Item"> + <plugin name="groupedProductWishlistProcessor" type="Magento\GroupedProduct\Model\Wishlist\Product\Item" /> + </type> +</config> diff --git a/app/code/Magento/Indexer/Model/ResourceModel/Indexer/State.php b/app/code/Magento/Indexer/Model/ResourceModel/Indexer/State.php index d1e27be21ae41..d0ab887b2e335 100644 --- a/app/code/Magento/Indexer/Model/ResourceModel/Indexer/State.php +++ b/app/code/Magento/Indexer/Model/ResourceModel/Indexer/State.php @@ -5,6 +5,11 @@ */ namespace Magento\Indexer\Model\ResourceModel\Indexer; +use Magento\Framework\Indexer\StateInterface; + +/** + * Resource model for indexer state + */ class State extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb { /** @@ -17,4 +22,22 @@ protected function _construct() $this->_init('indexer_state', 'state_id'); $this->addUniqueField(['field' => ['indexer_id'], 'title' => __('State for the same indexer')]); } + + /** + * @inheritDoc + */ + protected function prepareDataForUpdate($object) + { + $data = parent::prepareDataForUpdate($object); + + if (isset($data['status']) && StateInterface::STATUS_VALID === $data['status']) { + $data['status'] = $this->getConnection()->getCheckSql( + $this->getConnection()->quoteInto('status = ?', StateInterface::STATUS_WORKING), + $this->getConnection()->quote($data['status']), + 'status' + ); + } + + return $data; + } } diff --git a/app/code/Magento/Indexer/Test/Mftf/Section/AdminIndexManagementSection.xml b/app/code/Magento/Indexer/Test/Mftf/Section/AdminIndexManagementSection.xml index 8e7df86d01329..020cd9654e36b 100644 --- a/app/code/Magento/Indexer/Test/Mftf/Section/AdminIndexManagementSection.xml +++ b/app/code/Magento/Indexer/Test/Mftf/Section/AdminIndexManagementSection.xml @@ -15,7 +15,7 @@ <element name="massActionSubmit" type="button" selector="#gridIndexer_massaction-form button"/> <element name="indexerSelect" type="select" selector="//select[contains(@class,'action-select-multiselect')]"/> <element name="indexerStatus" type="text" selector="//tr[descendant::td[contains(., '{{status}}')]]//*[contains(@class, 'col-indexer_status')]/span" parameterized="true"/> - <element name="successMessage" type="text" selector="//*[@data-ui-id='messages-message-success']"/> + <element name="successMessage" type="text" selector="//*[@data-ui-id='messages-message-success']" timeout="120"/> <element name="selectMassAction" type="select" selector="#gridIndexer_massaction-mass-select"/> </section> </sections> diff --git a/app/code/Magento/InstantPurchase/etc/adminhtml/system.xml b/app/code/Magento/InstantPurchase/etc/adminhtml/system.xml index 76785c023ed0b..d87c91b293717 100644 --- a/app/code/Magento/InstantPurchase/etc/adminhtml/system.xml +++ b/app/code/Magento/InstantPurchase/etc/adminhtml/system.xml @@ -17,6 +17,9 @@ </field> <field id="button_text" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Button Text</label> + <depends> + <field id="active">1</field> + </depends> </field> </group> </section> diff --git a/app/code/Magento/Integration/etc/adminhtml/system.xml b/app/code/Magento/Integration/etc/adminhtml/system.xml index ddaae76700255..6ef569a1d8a2f 100644 --- a/app/code/Magento/Integration/etc/adminhtml/system.xml +++ b/app/code/Magento/Integration/etc/adminhtml/system.xml @@ -11,58 +11,58 @@ <label>OAuth</label> <tab>service</tab> <resource>Magento_Integration::config_oauth</resource> - <group id="access_token_lifetime" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="access_token_lifetime" translate="label" type="text" sortOrder="100" showInDefault="1"> <label>Access Token Expiration</label> - <field id="customer" translate="label comment" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="customer" translate="label comment" type="text" sortOrder="30" showInDefault="1" canRestore="1"> <label>Customer Token Lifetime (hours)</label> <comment>We will disable this feature if the value is empty.</comment> <validate>required-entry validate-zero-or-greater validate-number</validate> </field> - <field id="admin" translate="label comment" type="text" sortOrder="60" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="admin" translate="label comment" type="text" sortOrder="60" showInDefault="1" canRestore="1"> <label>Admin Token Lifetime (hours)</label> <comment>We will disable this feature if the value is empty.</comment> <validate>required-entry validate-zero-or-greater validate-number</validate> </field> </group> - <group id="cleanup" translate="label" type="text" sortOrder="300" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="cleanup" translate="label" type="text" sortOrder="300" showInDefault="1"> <label>Cleanup Settings</label> - <field id="cleanup_probability" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="cleanup_probability" translate="label comment" type="text" sortOrder="10" showInDefault="1" canRestore="1"> <label>Cleanup Probability</label> <comment>Integer. Launch cleanup in X OAuth requests. 0 (not recommended) - to disable cleanup</comment> <validate>required-entry validate-zero-or-greater validate-digits</validate> </field> - <field id="expiration_period" translate="label comment" type="text" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="expiration_period" translate="label comment" type="text" sortOrder="20" showInDefault="1" canRestore="1"> <label>Expiration Period</label> <comment>Cleanup entries older than X minutes.</comment> <validate>required-entry validate-zero-or-greater validate-number</validate> </field> </group> - <group id="consumer" translate="label" type="text" sortOrder="400" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="consumer" translate="label" type="text" sortOrder="400" showInDefault="1"> <label>Consumer Settings</label> - <field id="expiration_period" translate="label comment" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="expiration_period" translate="label comment" type="text" sortOrder="30" showInDefault="1" canRestore="1"> <label>Expiration Period</label> <comment>Consumer key/secret will expire if not used within X seconds after Oauth token exchange starts.</comment> <validate>required-entry validate-zero-or-greater validate-number</validate> </field> - <field id="post_maxredirects" translate="label comment" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="post_maxredirects" translate="label comment" type="text" sortOrder="30" showInDefault="1" canRestore="1"> <label>OAuth consumer credentials HTTP Post maxredirects</label> <comment>Number of maximum redirects for OAuth consumer credentials Post request.</comment> <validate>required-entry validate-zero-or-greater validate-digits</validate> </field> - <field id="post_timeout" translate="label comment" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="post_timeout" translate="label comment" type="text" sortOrder="30" showInDefault="1" canRestore="1"> <label>OAuth consumer credentials HTTP Post timeout</label> <comment>Timeout for OAuth consumer credentials Post request within X seconds.</comment> <validate>required-entry validate-zero-or-greater validate-number</validate> </field> </group> - <group id="authentication_lock" translate="label" type="text" sortOrder="400" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="authentication_lock" translate="label" type="text" sortOrder="400" showInDefault="1"> <label>Authentication Locks</label> - <field id="max_failures_count" translate="label comment" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="max_failures_count" translate="label comment" type="text" sortOrder="30" showInDefault="1" canRestore="1"> <label>Maximum Login Failures to Lock Out Account</label> <comment>Maximum Number of authentication failures to lock out account.</comment> <validate>required-entry validate-zero-or-greater validate-digits</validate> </field> - <field id="timeout" translate="label comment" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="timeout" translate="label comment" type="text" sortOrder="30" showInDefault="1" canRestore="1"> <label>Lockout Time (seconds)</label> <comment>Period of time in seconds after which account will be unlocked.</comment> <validate>required-entry validate-zero-or-greater validate-number</validate> diff --git a/app/code/Magento/MediaStorage/etc/adminhtml/system.xml b/app/code/Magento/MediaStorage/etc/adminhtml/system.xml index 2c7219fe8afaa..9c8c2c5b24398 100644 --- a/app/code/Magento/MediaStorage/etc/adminhtml/system.xml +++ b/app/code/Magento/MediaStorage/etc/adminhtml/system.xml @@ -10,11 +10,11 @@ <section id="system" type="text" sortOrder="900" showInDefault="1" showInWebsite="1" showInStore="1"> <group id="media_storage_configuration" translate="label" type="text" sortOrder="900" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Storage Configuration for Media</label> - <field id="media_storage" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="media_storage" translate="label" type="select" sortOrder="100" showInDefault="1"> <label>Media Storage</label> <source_model>Magento\MediaStorage\Model\Config\Source\Storage\Media\Storage</source_model> </field> - <field id="media_database" translate="label" type="select" sortOrder="200" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="media_database" translate="label" type="select" sortOrder="200" showInDefault="1"> <label>Select Media Database</label> <source_model>Magento\MediaStorage\Model\Config\Source\Storage\Media\Database</source_model> <backend_model>Magento\MediaStorage\Model\Config\Backend\Storage\Media\Database</backend_model> @@ -22,11 +22,11 @@ <field id="media_storage">1</field> </depends> </field> - <field id="synchronize" translate="label comment" type="button" sortOrder="300" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="synchronize" translate="label comment" type="button" sortOrder="300" showInDefault="1"> <frontend_model>Magento\MediaStorage\Block\System\Config\System\Storage\Media\Synchronize</frontend_model> <comment>After selecting a new media storage location, press the Synchronize button to transfer all media to that location and then "Save Config". Media will not be available in the new location until the synchronization process is complete.</comment> </field> - <field id="configuration_update_time" translate="label" type="text" sortOrder="400" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="configuration_update_time" translate="label" type="text" sortOrder="400" showInDefault="1" canRestore="1"> <label>Environment Update Time</label> <validate>validate-zero-or-greater validate-digits</validate> </field> diff --git a/app/code/Magento/Msrp/etc/adminhtml/system.xml b/app/code/Magento/Msrp/etc/adminhtml/system.xml index c20d753a2e794..8f6c3750c3835 100644 --- a/app/code/Magento/Msrp/etc/adminhtml/system.xml +++ b/app/code/Magento/Msrp/etc/adminhtml/system.xml @@ -8,16 +8,16 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="sales"> - <group id="msrp" translate="label" type="text" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="msrp" translate="label" type="text" sortOrder="110" showInDefault="1" showInWebsite="1"> <label>Minimum Advertised Price</label> - <field id="enabled" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="enabled" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enable MAP</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment> <![CDATA[<strong style="color:red">Warning!</strong> Enabling MAP by default will hide all product prices on Storefront.]]> </comment> </field> - <field id="display_price_type" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="display_price_type" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Display Actual Price</label> <source_model>Magento\Msrp\Model\Product\Attribute\Source\Type</source_model> </field> diff --git a/app/code/Magento/Multishipping/etc/adminhtml/system.xml b/app/code/Magento/Multishipping/etc/adminhtml/system.xml index f30782b357c73..909db7b883904 100644 --- a/app/code/Magento/Multishipping/etc/adminhtml/system.xml +++ b/app/code/Magento/Multishipping/etc/adminhtml/system.xml @@ -7,19 +7,22 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> - <section id="multishipping" translate="label" type="text" sortOrder="311" showInDefault="1" showInWebsite="1" showInStore="0"> + <section id="multishipping" translate="label" type="text" sortOrder="311" showInDefault="1" showInWebsite="1"> <label>Multishipping Settings</label> <tab>sales</tab> <resource>Magento_Multishipping::config_multishipping</resource> - <group id="options" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="options" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1"> <label>Options</label> - <field id="checkout_multiple" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="checkout_multiple" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Allow Shipping to Multiple Addresses</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="checkout_multiple_maximum_qty" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="checkout_multiple_maximum_qty" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Maximum Qty Allowed for Shipping to Multiple Addresses</label> <validate>validate-number</validate> + <depends> + <field id="checkout_multiple">1</field> + </depends> </field> </group> </section> diff --git a/app/code/Magento/MysqlMq/etc/adminhtml/system.xml b/app/code/Magento/MysqlMq/etc/adminhtml/system.xml index 045a176a48e87..835283ee263c2 100644 --- a/app/code/Magento/MysqlMq/etc/adminhtml/system.xml +++ b/app/code/Magento/MysqlMq/etc/adminhtml/system.xml @@ -8,22 +8,22 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="system"> - <group id="mysqlmq" translate="label comment" type="text" sortOrder="15" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="mysqlmq" translate="label comment" type="text" sortOrder="15" showInDefault="1"> <label>MySQL Message Queue Cleanup</label> <comment>All the times are in minutes. Use "0" if you want to skip automatic clearance.</comment> - <field id="retry_inprogress_after" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="retry_inprogress_after" translate="label" type="text" sortOrder="60" showInDefault="1"> <label>Retry Messages In Progress After</label> <validate>validate-zero-or-greater validate-digits</validate> </field> - <field id="successful_messages_lifetime" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="successful_messages_lifetime" translate="label" type="text" sortOrder="50" showInDefault="1"> <label>Successful Messages Lifetime</label> <validate>validate-zero-or-greater validate-digits</validate> </field> - <field id="failed_messages_lifetime" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="failed_messages_lifetime" translate="label" type="text" sortOrder="60" showInDefault="1"> <label>Failed Messages Lifetime</label> <validate>validate-zero-or-greater validate-digits</validate> </field> - <field id="new_messages_lifetime" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="new_messages_lifetime" translate="label" type="text" sortOrder="60" showInDefault="1"> <label>New Messages Lifetime</label> <validate>validate-zero-or-greater validate-digits</validate> </field> diff --git a/app/code/Magento/Newsletter/etc/adminhtml/system.xml b/app/code/Magento/Newsletter/etc/adminhtml/system.xml index 16af7b2158dde..01dc4777f5d31 100644 --- a/app/code/Magento/Newsletter/etc/adminhtml/system.xml +++ b/app/code/Magento/Newsletter/etc/adminhtml/system.xml @@ -20,6 +20,9 @@ </group> <group id="subscription" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Subscription Options</label> + <depends> + <field id="*/general/active">1</field> + </depends> <field id="allow_guest_subscribe" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Allow Guest Subscription</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> diff --git a/app/code/Magento/OfflinePayments/etc/adminhtml/system.xml b/app/code/Magento/OfflinePayments/etc/adminhtml/system.xml index aedab33239f9f..23793f970a7d9 100644 --- a/app/code/Magento/OfflinePayments/etc/adminhtml/system.xml +++ b/app/code/Magento/OfflinePayments/etc/adminhtml/system.xml @@ -10,26 +10,26 @@ <section id="payment" type="text" sortOrder="400" showInDefault="1" showInWebsite="1" showInStore="1"> <group id="checkmo" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Check / Money Order</label> - <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="order_status" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="order_status" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" canRestore="1"> <label>New Order Status</label> <source_model>Magento\Sales\Model\Config\Source\Order\Status\NewStatus</source_model> </field> - <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <frontend_class>validate-number</frontend_class> </field> <field id="title" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Title</label> </field> - <field id="allowspecific" translate="label" type="allowspecific" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allowspecific" translate="label" type="allowspecific" sortOrder="50" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Payment from Applicable Countries</label> <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="51" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="51" showInDefault="1" showInWebsite="1"> <label>Payment from Specific Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> @@ -40,11 +40,11 @@ <field id="mailing_address" translate="label" type="textarea" sortOrder="62" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Check to</label> </field> - <field id="min_order_total" translate="label" type="text" sortOrder="98" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="min_order_total" translate="label" type="text" sortOrder="98" showInDefault="1" showInWebsite="1"> <label>Minimum Order Total</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="max_order_total" translate="label" type="text" sortOrder="99" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="max_order_total" translate="label" type="text" sortOrder="99" showInDefault="1" showInWebsite="1"> <label>Maximum Order Total</label> <validate>validate-number validate-zero-or-greater</validate> </field> @@ -52,35 +52,35 @@ </group> <group id="purchaseorder" translate="label" type="text" sortOrder="32" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Purchase Order</label> - <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="order_status" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="order_status" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" canRestore="1"> <label>New Order Status</label> <source_model>Magento\Sales\Model\Config\Source\Order\Status\NewStatus</source_model> </field> - <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <frontend_class>validate-number</frontend_class> </field> <field id="title" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Title</label> </field> - <field id="allowspecific" translate="label" type="allowspecific" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allowspecific" translate="label" type="allowspecific" sortOrder="50" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Payment from Applicable Countries</label> <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="51" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="51" showInDefault="1" showInWebsite="1"> <label>Payment from Specific Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> </field> - <field id="min_order_total" translate="label" type="text" sortOrder="98" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="min_order_total" translate="label" type="text" sortOrder="98" showInDefault="1" showInWebsite="1"> <label>Minimum Order Total</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="max_order_total" translate="label" type="text" sortOrder="99" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="max_order_total" translate="label" type="text" sortOrder="99" showInDefault="1" showInWebsite="1"> <label>Maximum Order Total</label> <validate>validate-number validate-zero-or-greater</validate> </field> @@ -88,22 +88,22 @@ </group> <group id="banktransfer" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Bank Transfer Payment</label> - <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> <field id="title" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Title</label> </field> - <field id="order_status" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="order_status" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" canRestore="1"> <label>New Order Status</label> <source_model>Magento\Sales\Model\Config\Source\Order\Status\NewStatus</source_model> </field> - <field id="allowspecific" translate="label" type="allowspecific" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allowspecific" translate="label" type="allowspecific" sortOrder="50" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Payment from Applicable Countries</label> <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="51" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="51" showInDefault="1" showInWebsite="1"> <label>Payment from Specific Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> @@ -111,37 +111,37 @@ <field id="instructions" translate="label" type="textarea" sortOrder="62" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Instructions</label> </field> - <field id="min_order_total" translate="label" type="text" sortOrder="98" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="min_order_total" translate="label" type="text" sortOrder="98" showInDefault="1" showInWebsite="1"> <label>Minimum Order Total</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="max_order_total" translate="label" type="text" sortOrder="99" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="max_order_total" translate="label" type="text" sortOrder="99" showInDefault="1" showInWebsite="1"> <label>Maximum Order Total</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <validate>validate-number</validate> </field> </group> <group id="cashondelivery" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Cash On Delivery Payment</label> - <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> <field id="title" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Title</label> </field> - <field id="order_status" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="order_status" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" canRestore="1"> <label>New Order Status</label> <source_model>Magento\Sales\Model\Config\Source\Order\Status\NewStatus</source_model> </field> - <field id="allowspecific" translate="label" type="allowspecific" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allowspecific" translate="label" type="allowspecific" sortOrder="50" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Payment from Applicable Countries</label> <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="51" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="51" showInDefault="1" showInWebsite="1"> <label>Payment from Specific Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> @@ -149,15 +149,15 @@ <field id="instructions" translate="label" type="textarea" sortOrder="62" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Instructions</label> </field> - <field id="min_order_total" translate="label" type="text" sortOrder="98" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="min_order_total" translate="label" type="text" sortOrder="98" showInDefault="1" showInWebsite="1"> <label>Minimum Order Total</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="max_order_total" translate="label" type="text" sortOrder="99" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="max_order_total" translate="label" type="text" sortOrder="99" showInDefault="1" showInWebsite="1"> <label>Maximum Order Total</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <validate>validate-number</validate> </field> diff --git a/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml b/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml index cb75bddf4d7bd..768aab0499046 100644 --- a/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml +++ b/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml @@ -10,47 +10,47 @@ <section id="carriers" type="text" sortOrder="320" showInDefault="1" showInWebsite="1" showInStore="1"> <group id="flatrate" translate="label" type="text" sortOrder="0" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Flat Rate</label> - <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> <field id="name" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Method Name</label> </field> - <field id="price" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="price" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Price</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="handling_type" translate="label" type="select" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="handling_type" translate="label" type="select" sortOrder="7" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Calculate Handling Fee</label> <source_model>Magento\Shipping\Model\Source\HandlingType</source_model> </field> - <field id="handling_fee" translate="label" type="text" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="0" > + <field id="handling_fee" translate="label" type="text" sortOrder="8" showInDefault="1" showInWebsite="1" > <label>Handling Fee</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <validate>validate-number validate-zero-or-greater</validate> </field> <field id="title" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Title</label> </field> - <field id="type" translate="label" type="select" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="type" translate="label" type="select" sortOrder="4" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Type</label> <source_model>Magento\OfflineShipping\Model\Config\Source\Flatrate</source_model> </field> - <field id="sallowspecific" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="sallowspecific" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Ship to Applicable Countries</label> <frontend_class>shipping-applicable-country</frontend_class> <source_model>Magento\Shipping\Model\Config\Source\Allspecificcountries</source_model> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="91" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="91" showInDefault="1" showInWebsite="1"> <label>Ship to Specific Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> </field> - <field id="showmethod" translate="label" type="select" sortOrder="92" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="showmethod" translate="label" type="select" sortOrder="92" showInDefault="1" showInWebsite="1"> <label>Show Method if Not Applicable</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <frontend_class>shipping-skip-hide</frontend_class> @@ -61,54 +61,54 @@ </group> <group id="tablerate" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Table Rates</label> - <field id="handling_type" translate="label" type="select" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="handling_type" translate="label" type="select" sortOrder="7" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Calculate Handling Fee</label> <source_model>Magento\Shipping\Model\Source\HandlingType</source_model> </field> - <field id="handling_fee" translate="label" type="text" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="handling_fee" translate="label" type="text" sortOrder="8" showInDefault="1" showInWebsite="1"> <label>Handling Fee</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="condition_name" translate="label" type="select" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="condition_name" translate="label" type="select" sortOrder="4" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Condition</label> <source_model>Magento\OfflineShipping\Model\Config\Source\Tablerate</source_model> </field> - <field id="include_virtual_price" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="include_virtual_price" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Include Virtual Products in Price Calculation</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="export" translate="label" type="Magento\OfflineShipping\Block\Adminhtml\Form\Field\Export" sortOrder="5" showInDefault="0" showInWebsite="1" showInStore="0"> + <field id="export" translate="label" type="Magento\OfflineShipping\Block\Adminhtml\Form\Field\Export" sortOrder="5" showInWebsite="1"> <label>Export</label> </field> - <field id="import" translate="label" type="Magento\OfflineShipping\Block\Adminhtml\Form\Field\Import" sortOrder="6" showInDefault="0" showInWebsite="1" showInStore="0"> + <field id="import" translate="label" type="Magento\OfflineShipping\Block\Adminhtml\Form\Field\Import" sortOrder="6" showInWebsite="1"> <label>Import</label> <backend_model>Magento\OfflineShipping\Model\Config\Backend\Tablerate</backend_model> </field> <field id="name" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Method Name</label> </field> - <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <validate>validate-number validate-zero-or-greater</validate> </field> <field id="title" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Title</label> </field> - <field id="sallowspecific" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="sallowspecific" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Ship to Applicable Countries</label> <frontend_class>shipping-applicable-country</frontend_class> <source_model>Magento\Shipping\Model\Config\Source\Allspecificcountries</source_model> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="91" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="91" showInDefault="1" showInWebsite="1"> <label>Ship to Specific Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> </field> - <field id="showmethod" translate="label" type="select" sortOrder="92" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="showmethod" translate="label" type="select" sortOrder="92" showInDefault="1" showInWebsite="1"> <label>Show Method if Not Applicable</label> <frontend_class>shipping-skip-hide</frontend_class> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> @@ -119,39 +119,39 @@ </group> <group id="freeshipping" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Free Shipping</label> - <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="free_shipping_subtotal" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="free_shipping_subtotal" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1"> <label>Minimum Order Amount</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="tax_including" translate="label" sortOrder="5" type="select" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="tax_including" translate="label" sortOrder="5" type="select" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Include Tax to Amount</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> <field id="name" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Method Name</label> </field> - <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <validate>validate-number validate-zero-or-greater</validate> </field> <field id="title" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Title</label> </field> - <field id="sallowspecific" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="sallowspecific" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Ship to Applicable Countries</label> <frontend_class>shipping-applicable-country</frontend_class> <source_model>Magento\Shipping\Model\Config\Source\Allspecificcountries</source_model> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="91" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="91" showInDefault="1" showInWebsite="1"> <label>Ship to Specific Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> </field> - <field id="showmethod" translate="label" type="select" sortOrder="92" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="showmethod" translate="label" type="select" sortOrder="92" showInDefault="1" showInWebsite="1"> <label>Show Method if Not Applicable</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <frontend_class>shipping-skip-hide</frontend_class> diff --git a/app/code/Magento/PageCache/etc/adminhtml/system.xml b/app/code/Magento/PageCache/etc/adminhtml/system.xml index 234e3e48a95d8..4ffc20958748d 100644 --- a/app/code/Magento/PageCache/etc/adminhtml/system.xml +++ b/app/code/Magento/PageCache/etc/adminhtml/system.xml @@ -8,15 +8,15 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="system"> - <group id="full_page_cache" translate="label" showInDefault="1" showInWebsite="0" showInStore="0" sortOrder="600"> + <group id="full_page_cache" translate="label" showInDefault="1" sortOrder="600"> <label>Full Page Cache</label> - <field id="caching_application" translate="label" type="select" sortOrder="0" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="caching_application" translate="label" type="select" sortOrder="0" showInDefault="1" canRestore="1"> <label>Caching Application</label> <source_model>Magento\PageCache\Model\System\Config\Source\Application</source_model> </field> - <group id="varnish" translate="label" showInDefault="1" showInWebsite="0" showInStore="0" sortOrder="605"> + <group id="varnish" translate="label" showInDefault="1" sortOrder="605"> <label>Varnish Configuration</label> - <field id="access_list" type="text" translate="label comment" sortOrder="15" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="access_list" type="text" translate="label comment" sortOrder="15" showInDefault="1"> <label>Access list</label> <comment>IPs access list separated with ',' that can purge Varnish configuration for config file generation. If field is empty default value localhost will be saved.</comment> @@ -25,7 +25,7 @@ <field id="caching_application">1</field> </depends> </field> - <field id="backend_host" type="text" translate="label comment" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="backend_host" type="text" translate="label comment" sortOrder="20" showInDefault="1"> <label>Backend host</label> <comment>Specify backend host for config file generation. If field is empty default value localhost will be saved.</comment> <backend_model>Magento\PageCache\Model\System\Config\Backend\Varnish</backend_model> @@ -33,7 +33,7 @@ <field id="caching_application">1</field> </depends> </field> - <field id="backend_port" type="text" translate="label comment" sortOrder="25" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="backend_port" type="text" translate="label comment" sortOrder="25" showInDefault="1"> <label>Backend port</label> <comment>Specify backend port for config file generation. If field is empty default value 8080 will be saved.</comment> <backend_model>Magento\PageCache\Model\System\Config\Backend\Varnish</backend_model> @@ -41,7 +41,7 @@ <field id="caching_application">1</field> </depends> </field> - <field id="grace_period" type="text" translate="label comment" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="grace_period" type="text" translate="label comment" sortOrder="30" showInDefault="1"> <label>Grace period</label> <comment>Specify grace period in seconds for config file generation. If field is empty default value 300 will be saved. This grace period will be used to serve cached content when the server is healthy. If the server is not healthy, cached content will be served for 3 days before failing.</comment> <backend_model>Magento\PageCache\Model\System\Config\Backend\Varnish</backend_model> @@ -49,20 +49,20 @@ <field id="caching_application">1</field> </depends> </field> - <field id="export_button_version4" translate="label" type="button" sortOrder="35" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="export_button_version4" translate="label" type="button" sortOrder="35" showInDefault="1"> <label>Export Configuration</label> <frontend_model>Magento\PageCache\Block\System\Config\Form\Field\Export\Varnish4</frontend_model> <depends> <field id="caching_application">1</field> </depends> </field> - <field id="export_button_version5" type="button" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="export_button_version5" type="button" sortOrder="40" showInDefault="1"> <frontend_model>Magento\PageCache\Block\System\Config\Form\Field\Export\Varnish5</frontend_model> <depends> <field id="caching_application">1</field> </depends> </field> - <field id="export_button_version6" type="button" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="export_button_version6" type="button" sortOrder="40" showInDefault="1"> <frontend_model>Magento\PageCache\Block\System\Config\Form\Field\Export\Varnish6</frontend_model> <depends> <field id="caching_application">1</field> @@ -72,7 +72,7 @@ <field id="caching_application">2</field> </depends> </group> - <field id="ttl" type="text" translate="label comment" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="ttl" type="text" translate="label comment" sortOrder="5" showInDefault="1" canRestore="1"> <label>TTL for public content</label> <validate>validate-zero-or-greater validate-digits</validate> <comment>Public content cache lifetime in seconds. If field is empty default value 86400 will be saved. </comment> diff --git a/app/code/Magento/Payment/etc/adminhtml/system.xml b/app/code/Magento/Payment/etc/adminhtml/system.xml index d168dc13a397b..1e8b617d31326 100644 --- a/app/code/Magento/Payment/etc/adminhtml/system.xml +++ b/app/code/Magento/Payment/etc/adminhtml/system.xml @@ -13,33 +13,33 @@ <resource>Magento_Payment::payment</resource> <group id="free" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Zero Subtotal Checkout</label> - <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="order_status" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="order_status" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" canRestore="1"> <label>New Order Status</label> <source_model>Magento\Sales\Model\Config\Source\Order\Status\Newprocessing</source_model> </field> - <field id="payment_action" translate="label" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="payment_action" translate="label" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Automatically Invoice All Items</label> <source_model>Magento\Payment\Model\Source\Invoice</source_model> <depends> <field id="order_status" separator=",">processing</field> </depends> </field> - <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Sort Order</label> <frontend_class>validate-number</frontend_class> </field> <field id="title" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Title</label> </field> - <field id="allowspecific" translate="label" type="allowspecific" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allowspecific" translate="label" type="allowspecific" sortOrder="50" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Payment from Applicable Countries</label> <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="51" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="51" showInDefault="1" showInWebsite="1"> <label>Payment from Specific Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> diff --git a/app/code/Magento/Paypal/etc/adminhtml/system.xml b/app/code/Magento/Paypal/etc/adminhtml/system.xml index 88bb61f2cdc99..80e9523c752e4 100644 --- a/app/code/Magento/Paypal/etc/adminhtml/system.xml +++ b/app/code/Magento/Paypal/etc/adminhtml/system.xml @@ -8,10 +8,10 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="payment"> - <group id="account" translate="label" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="account" translate="label" sortOrder="1" showInDefault="1" showInWebsite="1"> <label>Merchant Location</label> <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="merchant_country" type="select" translate="label comment" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="merchant_country" type="select" translate="label comment" sortOrder="5" showInDefault="1" showInWebsite="1"> <label>Merchant Country</label> <comment>If not specified, Default Country from General Config will be used.</comment> <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Country</frontend_model> diff --git a/app/code/Magento/PaypalCaptcha/etc/adminhtml/system.xml b/app/code/Magento/PaypalCaptcha/etc/adminhtml/system.xml index 12afd8ceda60e..aa5eb371ba1a8 100644 --- a/app/code/Magento/PaypalCaptcha/etc/adminhtml/system.xml +++ b/app/code/Magento/PaypalCaptcha/etc/adminhtml/system.xml @@ -8,8 +8,8 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="customer"> - <group id="captcha" translate="label" type="text" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="0"> - <field id="forms" translate="label comment" type="multiselect" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <group id="captcha" translate="label" type="text" sortOrder="110" showInDefault="1" showInWebsite="1"> + <field id="forms" translate="label comment" type="multiselect" sortOrder="3" showInDefault="1" showInWebsite="1" canRestore="1"> <comment>CAPTCHA for "Create user", "Forgot password", "Payflow Pro" forms is always enabled if chosen.</comment> </field> </group> diff --git a/app/code/Magento/Persistent/etc/adminhtml/system.xml b/app/code/Magento/Persistent/etc/adminhtml/system.xml index 2db7cb0df0bd1..18882c0072877 100644 --- a/app/code/Magento/Persistent/etc/adminhtml/system.xml +++ b/app/code/Magento/Persistent/etc/adminhtml/system.xml @@ -7,46 +7,46 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> - <section id="persistent" translate="label" type="text" sortOrder="500" showInDefault="1" showInWebsite="1" showInStore="0"> + <section id="persistent" translate="label" type="text" sortOrder="500" showInDefault="1" showInWebsite="1"> <class>separator-top</class> <label>Persistent Shopping Cart</label> <tab>customer</tab> <resource>Magento_Persistent::persistent</resource> - <group id="options" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="options" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1"> <label>General Options</label> - <field id="enabled" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="enabled" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enable Persistence</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="lifetime" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="lifetime" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Persistence Lifetime (seconds)</label> <validate>validate-digits validate-digits-range digits-range-0-3153600000</validate> <depends> <field id="enabled">1</field> </depends> </field> - <field id="remember_enabled" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="remember_enabled" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enable "Remember Me"</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <depends> <field id="enabled">1</field> </depends> </field> - <field id="remember_default" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="remember_default" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" canRestore="1"> <label>"Remember Me" Default Value</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <depends> <field id="enabled">1</field> </depends> </field> - <field id="logout_clear" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="logout_clear" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Clear Persistence on Sign Out</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <depends> <field id="enabled">1</field> </depends> </field> - <field id="shopping_cart" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="shopping_cart" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Persist Shopping Cart</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <depends> diff --git a/app/code/Magento/ProductAlert/etc/adminhtml/system.xml b/app/code/Magento/ProductAlert/etc/adminhtml/system.xml index edf4940ff504a..2af3b905d2faf 100644 --- a/app/code/Magento/ProductAlert/etc/adminhtml/system.xml +++ b/app/code/Magento/ProductAlert/etc/adminhtml/system.xml @@ -14,7 +14,7 @@ <label>Allow Alert When Product Price Changes</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="allow_stock" translate="label" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allow_stock" translate="label" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Allow Alert When Product Comes Back in Stock</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> @@ -33,25 +33,25 @@ <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> </field> </group> - <group id="productalert_cron" translate="label" type="text" sortOrder="260" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="productalert_cron" translate="label" type="text" sortOrder="260" showInDefault="1"> <label>Product Alerts Run Settings</label> - <field id="frequency" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="frequency" translate="label" type="select" sortOrder="1" showInDefault="1"> <label>Frequency</label> <source_model>Magento\Cron\Model\Config\Source\Frequency</source_model> <backend_model>Magento\Cron\Model\Config\Backend\Product\Alert</backend_model> </field> - <field id="time" translate="label" type="time" sortOrder="2" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="time" translate="label" type="time" sortOrder="2" showInDefault="1"> <label>Start Time</label> </field> - <field id="error_email" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="error_email" translate="label" type="text" sortOrder="3" showInDefault="1"> <label>Error Email Recipient</label> <validate>validate-email</validate> </field> - <field id="error_email_identity" translate="label" type="select" sortOrder="4" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="error_email_identity" translate="label" type="select" sortOrder="4" showInDefault="1" canRestore="1"> <label>Error Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> </field> - <field id="error_email_template" translate="label comment" type="select" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="error_email_template" translate="label comment" type="select" sortOrder="5" showInDefault="1" canRestore="1"> <label>Error Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> diff --git a/app/code/Magento/ProductVideo/etc/adminhtml/system.xml b/app/code/Magento/ProductVideo/etc/adminhtml/system.xml index aa8749f69b409..aa70f3a5096c5 100644 --- a/app/code/Magento/ProductVideo/etc/adminhtml/system.xml +++ b/app/code/Magento/ProductVideo/etc/adminhtml/system.xml @@ -8,9 +8,9 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="catalog"> - <group id="product_video" translate="label" type="text" sortOrder="350" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="product_video" translate="label" type="text" sortOrder="350" showInDefault="1" showInWebsite="1"> <label>Product Video</label> - <field id="youtube_api_key" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="youtube_api_key" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1"> <label>YouTube API Key</label> </field> <field id="play_if_base" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> diff --git a/app/code/Magento/Quote/Model/Quote/Address.php b/app/code/Magento/Quote/Model/Quote/Address.php index db8ac0d1fe179..39148f990b714 100644 --- a/app/code/Magento/Quote/Model/Quote/Address.php +++ b/app/code/Magento/Quote/Model/Quote/Address.php @@ -6,10 +6,42 @@ namespace Magento\Quote\Model\Quote; use Magento\Customer\Api\AddressMetadataInterface; +use Magento\Customer\Api\Data\AddressInterface; use Magento\Customer\Api\Data\AddressInterfaceFactory; use Magento\Customer\Api\Data\RegionInterfaceFactory; +use Magento\Customer\Model\Address\AbstractAddress; +use Magento\Customer\Model\Address\Mapper; +use Magento\Directory\Helper\Data; +use Magento\Directory\Model\CountryFactory; +use Magento\Directory\Model\RegionFactory; +use Magento\Framework\Api\AttributeValueFactory; +use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\Api\ExtensionAttributesFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Data\Collection\AbstractDb; +use Magento\Framework\DataObject\Copy; +use Magento\Framework\Model\Context; +use Magento\Framework\Model\ResourceModel\AbstractResource; +use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection; +use Magento\Framework\Registry; use Magento\Framework\Serialize\Serializer\Json; +use Magento\Quote\Api\Data\AddressExtensionInterface; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Address\Rate; +use Magento\Quote\Model\Quote\Address\RateCollectorInterfaceFactory; +use Magento\Quote\Model\Quote\Address\RateFactory; +use Magento\Quote\Model\Quote\Address\RateRequest; +use Magento\Quote\Model\Quote\Address\RateRequestFactory; +use Magento\Quote\Model\Quote\Address\Total; +use Magento\Quote\Model\Quote\Address\Total\Collector; +use Magento\Quote\Model\Quote\Address\Total\CollectorFactory; +use Magento\Quote\Model\Quote\Address\TotalFactory; +use Magento\Quote\Model\Quote\Item\AbstractItem; +use Magento\Quote\Model\ResourceModel\Quote\Address\Rate\CollectionFactory; +use Magento\Shipping\Model\CarrierFactoryInterface; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\StoreManagerInterface; /** @@ -22,8 +54,8 @@ * @method Address setCreatedAt(string $value) * @method string getUpdatedAt() * @method Address setUpdatedAt(string $value) - * @method \Magento\Customer\Api\Data\AddressInterface getCustomerAddress() - * @method Address setCustomerAddressData(\Magento\Customer\Api\Data\AddressInterface $value) + * @method AddressInterface getCustomerAddress() + * @method Address setCustomerAddressData(AddressInterface $value) * @method string getAddressType() * @method Address setAddressType(string $value) * @method int getFreeShipping() @@ -90,14 +122,14 @@ * @method int[] getAppliedRuleIds() * @method Address setBaseShippingInclTax(float $value) * - * @property $_objectCopyService \Magento\Framework\DataObject\Copy + * @property $objectCopyService \Magento\Framework\DataObject\Copy * @SuppressWarnings(PHPMD.ExcessivePublicCount) * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @since 100.0.2 */ -class Address extends \Magento\Customer\Model\Address\AbstractAddress implements +class Address extends AbstractAddress implements \Magento\Quote\Api\Data\AddressInterface { const RATES_FETCH = 1; @@ -125,28 +157,28 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress implements /** * Quote object * - * @var \Magento\Quote\Model\Quote + * @var Quote */ protected $_items; /** * Quote object * - * @var \Magento\Quote\Model\Quote + * @var Quote */ protected $_quote; /** * Sales Quote address rates * - * @var \Magento\Quote\Model\Quote\Address\Rate + * @var Rate */ protected $_rates; /** * Total models collector * - * @var \Magento\Quote\Model\Quote\Address\Total\Collector + * @var Collector */ protected $_totalCollector; @@ -170,7 +202,7 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress implements /** * Core store config * - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ScopeConfigInterface */ protected $_scopeConfig; @@ -185,33 +217,33 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress implements protected $_itemCollectionFactory; /** - * @var \Magento\Quote\Model\Quote\Address\RateCollectorInterfaceFactory + * @var RateCollectorInterfaceFactory */ protected $_rateCollector; /** - * @var \Magento\Quote\Model\ResourceModel\Quote\Address\Rate\CollectionFactory + * @var CollectionFactory */ protected $_rateCollectionFactory; /** - * @var \Magento\Quote\Model\Quote\Address\Total\CollectorFactory + * @var CollectorFactory */ protected $_totalCollectorFactory; /** - * @var \Magento\Quote\Model\Quote\Address\TotalFactory + * @var TotalFactory */ protected $_addressTotalFactory; /** - * @var \Magento\Quote\Model\Quote\Address\RateFactory + * @var RateFactory * @since 100.2.0 */ protected $_addressRateFactory; /** - * @var \Magento\Customer\Api\Data\AddressInterfaceFactory + * @var AddressInterfaceFactory */ protected $addressDataFactory; @@ -221,7 +253,7 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress implements protected $validator; /** - * @var \Magento\Customer\Model\Address\Mapper + * @var Mapper */ protected $addressMapper; @@ -241,7 +273,7 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress implements protected $totalsCollector; /** - * @var \Magento\Quote\Model\Quote\TotalsReader + * @var TotalsReader */ protected $totalsReader; @@ -256,37 +288,47 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress implements private $storeManager; /** - * @param \Magento\Framework\Model\Context $context - * @param \Magento\Framework\Registry $registry - * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory - * @param \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory - * @param \Magento\Directory\Helper\Data $directoryData + * @var Copy + */ + private $objectCopyService; + + /** + * @var /Magento\Shipping\Model\CarrierFactoryInterface + */ + private $carrierFactory; + + /** + * @param Context $context + * @param Registry $registry + * @param ExtensionAttributesFactory $extensionFactory + * @param AttributeValueFactory $customAttributeFactory + * @param Data $directoryData * @param \Magento\Eav\Model\Config $eavConfig * @param \Magento\Customer\Model\Address\Config $addressConfig - * @param \Magento\Directory\Model\RegionFactory $regionFactory - * @param \Magento\Directory\Model\CountryFactory $countryFactory + * @param RegionFactory $regionFactory + * @param CountryFactory $countryFactory * @param AddressMetadataInterface $metadataService * @param AddressInterfaceFactory $addressDataFactory * @param RegionInterfaceFactory $regionDataFactory - * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + * @param DataObjectHelper $dataObjectHelper + * @param ScopeConfigInterface $scopeConfig * @param Address\ItemFactory $addressItemFactory * @param \Magento\Quote\Model\ResourceModel\Quote\Address\Item\CollectionFactory $itemCollectionFactory - * @param \Magento\Quote\Model\Quote\Address\RateFactory $addressRateFactory + * @param RateFactory $addressRateFactory * @param Address\RateCollectorInterfaceFactory $rateCollector - * @param \Magento\Quote\Model\ResourceModel\Quote\Address\Rate\CollectionFactory $rateCollectionFactory + * @param CollectionFactory $rateCollectionFactory * @param Address\RateRequestFactory $rateRequestFactory * @param Address\Total\CollectorFactory $totalCollectorFactory * @param Address\TotalFactory $addressTotalFactory - * @param \Magento\Framework\DataObject\Copy $objectCopyService - * @param \Magento\Shipping\Model\CarrierFactoryInterface $carrierFactory + * @param Copy $objectCopyService + * @param CarrierFactoryInterface $carrierFactory * @param Address\Validator $validator - * @param \Magento\Customer\Model\Address\Mapper $addressMapper + * @param Mapper $addressMapper * @param Address\CustomAttributeListInterface $attributeList * @param TotalsCollector $totalsCollector - * @param \Magento\Quote\Model\Quote\TotalsReader $totalsReader - * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource - * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection + * @param TotalsReader $totalsReader + * @param AbstractResource|null $resource + * @param AbstractDb|null $resourceCollection * @param array $data * @param Json $serializer * @param StoreManagerInterface $storeManager @@ -294,37 +336,37 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress implements * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( - \Magento\Framework\Model\Context $context, - \Magento\Framework\Registry $registry, - \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory, - \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory, - \Magento\Directory\Helper\Data $directoryData, + Context $context, + Registry $registry, + ExtensionAttributesFactory $extensionFactory, + AttributeValueFactory $customAttributeFactory, + Data $directoryData, \Magento\Eav\Model\Config $eavConfig, \Magento\Customer\Model\Address\Config $addressConfig, - \Magento\Directory\Model\RegionFactory $regionFactory, - \Magento\Directory\Model\CountryFactory $countryFactory, + RegionFactory $regionFactory, + CountryFactory $countryFactory, AddressMetadataInterface $metadataService, AddressInterfaceFactory $addressDataFactory, RegionInterfaceFactory $regionDataFactory, - \Magento\Framework\Api\DataObjectHelper $dataObjectHelper, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, + DataObjectHelper $dataObjectHelper, + ScopeConfigInterface $scopeConfig, \Magento\Quote\Model\Quote\Address\ItemFactory $addressItemFactory, \Magento\Quote\Model\ResourceModel\Quote\Address\Item\CollectionFactory $itemCollectionFactory, - \Magento\Quote\Model\Quote\Address\RateFactory $addressRateFactory, - \Magento\Quote\Model\Quote\Address\RateCollectorInterfaceFactory $rateCollector, - \Magento\Quote\Model\ResourceModel\Quote\Address\Rate\CollectionFactory $rateCollectionFactory, - \Magento\Quote\Model\Quote\Address\RateRequestFactory $rateRequestFactory, - \Magento\Quote\Model\Quote\Address\Total\CollectorFactory $totalCollectorFactory, - \Magento\Quote\Model\Quote\Address\TotalFactory $addressTotalFactory, - \Magento\Framework\DataObject\Copy $objectCopyService, - \Magento\Shipping\Model\CarrierFactoryInterface $carrierFactory, + RateFactory $addressRateFactory, + RateCollectorInterfaceFactory $rateCollector, + CollectionFactory $rateCollectionFactory, + RateRequestFactory $rateRequestFactory, + CollectorFactory $totalCollectorFactory, + TotalFactory $addressTotalFactory, + Copy $objectCopyService, + CarrierFactoryInterface $carrierFactory, Address\Validator $validator, - \Magento\Customer\Model\Address\Mapper $addressMapper, + Mapper $addressMapper, Address\CustomAttributeListInterface $attributeList, - \Magento\Quote\Model\Quote\TotalsCollector $totalsCollector, - \Magento\Quote\Model\Quote\TotalsReader $totalsReader, - \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, - \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, + TotalsCollector $totalsCollector, + TotalsReader $totalsReader, + AbstractResource $resource = null, + AbstractDb $resourceCollection = null, array $data = [], Json $serializer = null, StoreManagerInterface $storeManager = null @@ -338,8 +380,8 @@ public function __construct( $this->_rateRequestFactory = $rateRequestFactory; $this->_totalCollectorFactory = $totalCollectorFactory; $this->_addressTotalFactory = $addressTotalFactory; - $this->_objectCopyService = $objectCopyService; - $this->_carrierFactory = $carrierFactory; + $this->objectCopyService = $objectCopyService; + $this->carrierFactory = $carrierFactory; $this->addressDataFactory = $addressDataFactory; $this->validator = $validator; $this->addressMapper = $addressMapper; @@ -412,7 +454,7 @@ protected function _populateBeforeSaveData() $this->setCustomerAddressId($this->getCustomerAddressData()->getId()); } - if (!$this->getId()) { + if (!$this->getId() || $this->getQuote()->dataHasChangedFor('customer_id')) { $this->setSameAsBilling((int)$this->_isSameAsBilling()); } } @@ -427,7 +469,7 @@ protected function _isSameAsBilling() { $quoteSameAsBilling = $this->getSameAsBilling(); - return $this->getAddressType() == \Magento\Quote\Model\Quote\Address::TYPE_SHIPPING && + return $this->getAddressType() == Address::TYPE_SHIPPING && ($this->_isNotRegisteredCustomer() || $this->_isDefaultShippingNullOrSameAsBillingAddress()) && ($quoteSameAsBilling || $quoteSameAsBilling === 0 || $quoteSameAsBilling === null); } @@ -473,10 +515,10 @@ protected function _isDefaultShippingNullOrSameAsBillingAddress() /** * Declare address quote model object * - * @param \Magento\Quote\Model\Quote $quote + * @param Quote $quote * @return $this */ - public function setQuote(\Magento\Quote\Model\Quote $quote) + public function setQuote(Quote $quote) { $this->_quote = $quote; $this->setQuoteId($quote->getId()); @@ -486,7 +528,7 @@ public function setQuote(\Magento\Quote\Model\Quote $quote) /** * Retrieve quote object * - * @return \Magento\Quote\Model\Quote + * @return Quote */ public function getQuote() { @@ -496,12 +538,12 @@ public function getQuote() /** * Import quote address data from customer address Data Object. * - * @param \Magento\Customer\Api\Data\AddressInterface $address + * @param AddressInterface $address * @return $this */ - public function importCustomerAddressData(\Magento\Customer\Api\Data\AddressInterface $address) + public function importCustomerAddressData(AddressInterface $address) { - $this->_objectCopyService->copyFieldsetToTarget( + $this->objectCopyService->copyFieldsetToTarget( 'customer_address', 'to_quote_address', $this->addressMapper->toFlatArray($address), @@ -519,11 +561,11 @@ public function importCustomerAddressData(\Magento\Customer\Api\Data\AddressInte /** * Export data to customer address Data Object. * - * @return \Magento\Customer\Api\Data\AddressInterface + * @return AddressInterface */ public function exportCustomerAddress() { - $customerAddressData = $this->_objectCopyService->getDataFromFieldset( + $customerAddressData = $this->objectCopyService->getDataFromFieldset( 'sales_convert_quote_address', 'to_customer_address', $this @@ -542,7 +584,7 @@ public function exportCustomerAddress() $this->dataObjectHelper->populateWithArray( $addressDataObject, $customerAddressData, - \Magento\Customer\Api\Data\AddressInterface::class + AddressInterface::class ); return $addressDataObject; } @@ -568,7 +610,7 @@ public function toArray(array $arrAttributes = []) /** * Retrieve address items collection * - * @return \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection + * @return AbstractCollection */ public function getItemsCollection() { @@ -765,13 +807,13 @@ public function removeItem($itemId) /** * Add item to address * - * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item + * @param AbstractItem $item * @param int $qty * @return $this */ - public function addItem(\Magento\Quote\Model\Quote\Item\AbstractItem $item, $qty = null) + public function addItem(AbstractItem $item, $qty = null) { - if ($item instanceof \Magento\Quote\Model\Quote\Item) { + if ($item instanceof Item) { if ($item->getParentItemId()) { return $this; } @@ -808,7 +850,7 @@ public function addItem(\Magento\Quote\Model\Quote\Item\AbstractItem $item, $qty /** * Retrieve collection of quote shipping rates * - * @return \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection + * @return AbstractCollection */ public function getShippingRatesCollection() { @@ -849,13 +891,13 @@ public function getGroupedAllShippingRates() { $rates = []; foreach ($this->getShippingRatesCollection() as $rate) { - if (!$rate->isDeleted() && $this->_carrierFactory->get($rate->getCarrier())) { + if (!$rate->isDeleted() && $this->carrierFactory->get($rate->getCarrier())) { if (!isset($rates[$rate->getCarrier()])) { $rates[$rate->getCarrier()] = []; } $rates[$rate->getCarrier()][] = $rate; - $rates[$rate->getCarrier()][0]->carrier_sort_order = $this->_carrierFactory->get( + $rates[$rate->getCarrier()][0]->carrier_sort_order = $this->carrierFactory->get( $rate->getCarrier() )->getSortOrder(); } @@ -881,7 +923,7 @@ protected function _sortRates($firstItem, $secondItem) * Retrieve shipping rate by identifier * * @param int $rateId - * @return \Magento\Quote\Model\Quote\Address\Rate|false + * @return Rate|false */ public function getShippingRateById($rateId) { @@ -898,7 +940,7 @@ public function getShippingRateById($rateId) * Retrieve shipping rate by code * * @param string $code - * @return \Magento\Quote\Model\Quote\Address\Rate|false + * @return Rate|false */ public function getShippingRateByCode($code) { @@ -927,10 +969,10 @@ public function removeAllShippingRates() /** * Add shipping rate * - * @param \Magento\Quote\Model\Quote\Address\Rate $rate + * @param Rate $rate * @return $this */ - public function addShippingRate(\Magento\Quote\Model\Quote\Address\Rate $rate) + public function addShippingRate(Rate $rate) { $rate->setAddress($this); $this->getShippingRatesCollection()->addItem($rate); @@ -970,14 +1012,14 @@ public function collectShippingRates() * * Returns true if current selected shipping method code corresponds to one of the found rates * - * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item + * @param AbstractItem $item * @return bool * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ - public function requestShippingRates(\Magento\Quote\Model\Quote\Item\AbstractItem $item = null) + public function requestShippingRates(AbstractItem $item = null) { - /** @var $request \Magento\Quote\Model\Quote\Address\RateRequest */ + /** @var $request RateRequest */ $request = $this->_rateRequestFactory->create(); $request->setAllItems($item ? [$item] : $this->getAllItems()); $request->setDestCountryId($this->getCountryId()); @@ -1041,7 +1083,7 @@ public function requestShippingRates(\Magento\Quote\Model\Quote\Item\AbstractIte $item->setBaseShippingAmount($rate->getPrice()); } else { - /** @var \Magento\Store\Api\Data\StoreInterface */ + /** @var StoreInterface */ $store = $this->storeManager->getStore(); $amountPrice = $store->getBaseCurrency() ->convert($rate->getPrice(), $store->getCurrentCurrencyCode()); @@ -1078,17 +1120,17 @@ public function getTotals() /** * Add total data or model * - * @param \Magento\Quote\Model\Quote\Address\Total|array $total + * @param Total|array $total * @return $this */ public function addTotal($total) { $addressTotal = null; if (is_array($total)) { - /** @var \Magento\Quote\Model\Quote\Address\Total $addressTotal */ - $addressTotal = $this->_addressTotalFactory->create(\Magento\Quote\Model\Quote\Address\Total::class); + /** @var Total $addressTotal */ + $addressTotal = $this->_addressTotalFactory->create(Total::class); $addressTotal->setData($total); - } elseif ($total instanceof \Magento\Quote\Model\Quote\Address\Total) { + } elseif ($total instanceof Total) { $addressTotal = $total; } @@ -1104,7 +1146,7 @@ public function addTotal($total) /** * Rewrite clone method * - * @return \Magento\Quote\Model\Quote\Address + * @return Address */ public function __clone() { @@ -1141,7 +1183,7 @@ public function validateMinimumAmount() $storeId = $this->getQuote()->getStoreId(); $validateEnabled = $this->_scopeConfig->isSetFlag( 'sales/minimum_order/active', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + ScopeInterface::SCOPE_STORE, $storeId ); if (!$validateEnabled) { @@ -1154,17 +1196,17 @@ public function validateMinimumAmount() $includeDiscount = $this->_scopeConfig->getValue( 'sales/minimum_order/include_discount_amount', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + ScopeInterface::SCOPE_STORE, $storeId ); $amount = $this->_scopeConfig->getValue( 'sales/minimum_order/amount', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + ScopeInterface::SCOPE_STORE, $storeId ); $taxInclude = $this->_scopeConfig->getValue( 'sales/minimum_order/tax_including', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + ScopeInterface::SCOPE_STORE, $storeId ); @@ -1701,7 +1743,7 @@ public function setSaveInAddressBook($saveInAddressBook) /** * @inheritdoc * - * @return \Magento\Quote\Api\Data\AddressExtensionInterface|null + * @return AddressExtensionInterface|null */ public function getExtensionAttributes() { @@ -1711,10 +1753,10 @@ public function getExtensionAttributes() /** * @inheritdoc * - * @param \Magento\Quote\Api\Data\AddressExtensionInterface $extensionAttributes + * @param AddressExtensionInterface $extensionAttributes * @return $this */ - public function setExtensionAttributes(\Magento\Quote\Api\Data\AddressExtensionInterface $extensionAttributes) + public function setExtensionAttributes(AddressExtensionInterface $extensionAttributes) { return $this->_setExtensionAttributes($extensionAttributes); } diff --git a/app/code/Magento/Quote/Model/ResourceModel/Quote.php b/app/code/Magento/Quote/Model/ResourceModel/Quote.php index ae26407c74522..48945dacd1738 100644 --- a/app/code/Magento/Quote/Model/ResourceModel/Quote.php +++ b/app/code/Magento/Quote/Model/ResourceModel/Quote.php @@ -102,6 +102,7 @@ public function loadByCustomerId($quote, $customerId) if ($data) { $quote->setData($data); + $quote->setOrigData(); } $this->_afterLoad($quote); @@ -124,6 +125,7 @@ public function loadActive($quote, $quoteId) $data = $connection->fetchRow($select); if ($data) { $quote->setData($data); + $quote->setOrigData(); } $this->_afterLoad($quote); @@ -148,6 +150,7 @@ public function loadByIdWithoutStore($quote, $quoteId) if ($data) { $quote->setData($data); + $quote->setOrigData(); } } @@ -303,5 +306,7 @@ public function save(\Magento\Framework\Model\AbstractModel $object) if (!$object->isPreventSaving()) { return parent::save($object); } + + return $this; } } diff --git a/app/code/Magento/Reports/etc/adminhtml/system.xml b/app/code/Magento/Reports/etc/adminhtml/system.xml index 40eba9f3a6bb2..66507afda5b3b 100644 --- a/app/code/Magento/Reports/etc/adminhtml/system.xml +++ b/app/code/Magento/Reports/etc/adminhtml/system.xml @@ -8,9 +8,9 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="catalog"> - <group id="recently_products" translate="label" type="text" sortOrder="350" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="recently_products" translate="label" type="text" sortOrder="350" showInDefault="1" showInWebsite="1"> <label>Recently Viewed/Compared Products</label> - <field id="scope" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="scope" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Show for Current</label> <source_model>Magento\Config\Model\Config\Source\Reports\Scope</source_model> <hide_in_single_store_mode>1</hide_in_single_store_mode> @@ -25,30 +25,30 @@ </field> </group> </section> - <section id="reports" translate="label" type="text" sortOrder="1000" showInDefault="1" showInWebsite="0" showInStore="0"> + <section id="reports" translate="label" type="text" sortOrder="1000" showInDefault="1"> <label>Reports</label> <tab>general</tab> <resource>Magento_Reports::reports</resource> - <group id="dashboard" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="dashboard" translate="label" type="text" sortOrder="1" showInDefault="1"> <label>Dashboard</label> - <field id="ytd_start" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="ytd_start" translate="label" type="select" sortOrder="1" showInDefault="1" canRestore="1"> <label>Year-To-Date Starts</label> <frontend_model>Magento\Reports\Block\Adminhtml\Config\Form\Field\YtdStart</frontend_model> </field> - <field id="mtd_start" translate="label comment" type="select" sortOrder="2" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="mtd_start" translate="label comment" type="select" sortOrder="2" showInDefault="1" canRestore="1"> <label>Current Month Starts</label> <frontend_model>Magento\Reports\Block\Adminhtml\Config\Form\Field\MtdStart</frontend_model> <comment>Select day of the month.</comment> </field> </group> - <group id="options" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="options" translate="label" type="text" sortOrder="1" showInDefault="1"> <label>General Options</label> - <field id="enabled" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="enabled" translate="label comment" type="select" sortOrder="1" showInDefault="1" canRestore="1"> <label>Enable Reports</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>If disabled, all report events will be disabled</comment> </field> - <field id="product_view_enabled" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="product_view_enabled" translate="label comment" type="select" sortOrder="1" showInDefault="1"> <label>Enable "Product View" Report</label> <comment>If enabled, will collect statistic of viewed product pages</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> @@ -56,7 +56,7 @@ <field id="enabled">1</field> </depends> </field> - <field id="product_send_enabled" translate="label comment" type="select" sortOrder="2" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="product_send_enabled" translate="label comment" type="select" sortOrder="2" showInDefault="1"> <label>Enable "Send Product Link To Friend" Report</label> <comment>If enabled, will collect statistic of product links sent to friend</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> @@ -64,7 +64,7 @@ <field id="enabled">1</field> </depends> </field> - <field id="product_compare_enabled" translate="label comment" type="select" sortOrder="3" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="product_compare_enabled" translate="label comment" type="select" sortOrder="3" showInDefault="1"> <label>Enable "Add Product To Compare List" Report</label> <comment>If enabled, will collect statistic of products added to Compare List</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> @@ -72,7 +72,7 @@ <field id="enabled">1</field> </depends> </field> - <field id="product_to_cart_enabled" translate="label comment" type="select" sortOrder="4" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="product_to_cart_enabled" translate="label comment" type="select" sortOrder="4" showInDefault="1"> <label>Enable "Product Added To Cart" Report</label> <comment>If enabled, will collect statistic of products added to Cart</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> @@ -80,7 +80,7 @@ <field id="enabled">1</field> </depends> </field> - <field id="product_to_wishlist_enabled" translate="label comment" type="select" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="product_to_wishlist_enabled" translate="label comment" type="select" sortOrder="5" showInDefault="1"> <label>Enable "Product Added To WishList" Report</label> <comment>If enabled, will collect statistic of products added to WishList</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> @@ -88,7 +88,7 @@ <field id="enabled">1</field> </depends> </field> - <field id="wishlist_share_enabled" translate="label comment" type="select" sortOrder="6" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="wishlist_share_enabled" translate="label comment" type="select" sortOrder="6" showInDefault="1"> <label>Enable "Share WishList" Report</label> <comment>If enabled, will collect statistic of shared WishLists</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> diff --git a/app/code/Magento/Review/Test/Unit/Observer/CatalogProductListCollectionAppendSummaryFieldsObserverTest.php b/app/code/Magento/Review/Test/Unit/Observer/CatalogProductListCollectionAppendSummaryFieldsObserverTest.php new file mode 100644 index 0000000000000..894463de93227 --- /dev/null +++ b/app/code/Magento/Review/Test/Unit/Observer/CatalogProductListCollectionAppendSummaryFieldsObserverTest.php @@ -0,0 +1,149 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types = 1); + +namespace Magento\Review\Test\Unit\Observer; + +use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection; +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Review\Model\ResourceModel\Review\Summary; +use Magento\Review\Model\ResourceModel\Review\SummaryFactory; +use Magento\Review\Observer\CatalogProductListCollectionAppendSummaryFieldsObserver; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreManagerInterface; +use MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Test class for \Magento\Review\Observer\CatalogProductListCollectionAppendSummaryFieldsObserver + */ +class CatalogProductListCollectionAppendSummaryFieldsObserverTest extends TestCase +{ + private const STORE_ID = '1'; + + /** + * @var Event|MockObject + */ + private $eventMock; + + /** + * Testable Object + * + * @var CatalogProductListCollectionAppendSummaryFieldsObserver + */ + private $observer; + + /** + * @var Observer|MockObject + */ + private $observerMock; + + /** + * @var Collection|MockObject + */ + private $productCollectionMock; + + /** + * @var StoreInterface|MockObject + */ + private $storeMock; + + /** + * @var StoreManagerInterface|MockObject + */ + private $storeManagerMock; + + /** + * @var Summary|MockObject + */ + private $sumResourceMock; + + /** + * @var SummaryFactory|MockObject + */ + private $sumResourceFactoryMock; + + /** + * @inheritdoc + */ + protected function setUp() : void + { + $this->eventMock = $this->getMockBuilder(Event::class) + ->disableOriginalConstructor() + ->setMethods(['getCollection']) + ->getMock(); + + $this->observerMock = $this->createMock(Observer::class); + + $this->productCollectionMock = $this->getMockBuilder(Collection::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getStore']) + ->getMockForAbstractClass(); + + $this->storeMock = $this->getMockBuilder(StoreInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getId']) + ->getMockForAbstractClass(); + + $this->sumResourceMock = $this->createPartialMock( + Summary::class, + ['appendSummaryFieldsToCollection'] + ); + + $this->sumResourceFactoryMock = $this->getMockBuilder(SummaryFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->observer = new CatalogProductListCollectionAppendSummaryFieldsObserver( + $this->sumResourceFactoryMock, + $this->storeManagerMock + ); + } + + /** + * Product listing test + */ + public function testAddSummaryFieldToProductsCollection() : void + { + $this->eventMock + ->expects($this->once()) + ->method('getCollection') + ->willReturn($this->productCollectionMock); + + $this->observerMock + ->expects($this->once()) + ->method('getEvent') + ->willReturn($this->eventMock); + + $this->storeManagerMock + ->expects($this->once()) + ->method('getStore') + ->willReturn($this->storeMock); + + $this->storeMock + ->expects($this->once()) + ->method('getId') + ->willReturn(self::STORE_ID); + + $this->sumResourceFactoryMock + ->expects($this->once()) + ->method('create') + ->willReturn($this->sumResourceMock); + + $this->sumResourceMock + ->expects($this->once()) + ->method('appendSummaryFieldsToCollection') + ->willReturn($this->sumResourceMock); + + $this->observer->execute($this->observerMock); + } +} diff --git a/app/code/Magento/Review/etc/adminhtml/system.xml b/app/code/Magento/Review/etc/adminhtml/system.xml index a24ed29dc2c23..1ddae2118c5a7 100644 --- a/app/code/Magento/Review/etc/adminhtml/system.xml +++ b/app/code/Magento/Review/etc/adminhtml/system.xml @@ -8,15 +8,18 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="catalog"> - <group id="review" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="review" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1"> <label>Product Reviews</label> <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="allow_guest" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allow_guest" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Allow Guests to Write Reviews</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <depends> + <field id="active">1</field> + </depends> </field> </group> </section> diff --git a/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php b/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php index e216e2ae658ba..2206dbef38640 100644 --- a/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php +++ b/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php @@ -625,6 +625,8 @@ public function getMappedSqlField() $mappedSqlField = $this->getEavAttributeTableAlias() . '.value'; } elseif ($this->getAttribute() == 'category_ids') { $mappedSqlField = 'e.entity_id'; + } elseif ($this->getAttribute() == 'attribute_set_id') { + $mappedSqlField = 'e.attribute_set_id'; } else { $mappedSqlField = parent::getMappedSqlField(); } diff --git a/app/code/Magento/Rule/Test/Unit/Model/Condition/Product/AbstractProductTest.php b/app/code/Magento/Rule/Test/Unit/Model/Condition/Product/AbstractProductTest.php index 80f07c9d6550d..62d91de3ca8e2 100644 --- a/app/code/Magento/Rule/Test/Unit/Model/Condition/Product/AbstractProductTest.php +++ b/app/code/Magento/Rule/Test/Unit/Model/Condition/Product/AbstractProductTest.php @@ -3,85 +3,103 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Rule\Test\Unit\Model\Condition\Product; -use ReflectionMethod; -use ReflectionProperty; +use Magento\Catalog\Model\ProductCategoryList; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +use Magento\Catalog\Model\ResourceModel\Product; +use Magento\Eav\Model\Config; +use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource; +use Magento\Eav\Model\Entity\Type; +use Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\Collection; +use Magento\Framework\DataObject; +use Magento\Framework\Model\AbstractModel; +use Magento\Rule\Model\Condition\Product\AbstractProduct; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class AbstractProductTest extends \PHPUnit\Framework\TestCase +/** + * Class to test Abstract Rule product condition data model + */ +class AbstractProductTest extends TestCase { /** * Tested condition * - * @var \Magento\Rule\Model\Condition\Product\AbstractProduct|\PHPUnit_Framework_MockObject_MockObject + * @var AbstractProduct|MockObject */ protected $_condition; /** * Framework object * - * @var \Magento\Framework\DataObject|\PHPUnit_Framework_MockObject_MockObject + * @var DataObject|MockObject */ protected $_object; /** - * Reflection for Magento\Rule\Model\Condition\Product\AbstractProduct::$_entityAttributeValues + * Reflection for AbstractProduct::$_entityAttributeValues * * @var \ReflectionProperty */ protected $_entityAttributeValuesProperty; /** - * Reflection for Magento\Rule\Model\Condition\Product\AbstractProduct::$_config + * Reflection for AbstractProduct::$_config * * @var \ReflectionProperty */ protected $_configProperty; /** - * Reflection for Magento\Rule\Model\Condition\Product\AbstractProduct::$productCategoryListProperty + * Reflection for AbstractProduct::$productCategoryListProperty * * @var \ReflectionProperty */ private $productCategoryListProperty; + /** + * @inheritdoc + */ protected function setUp() { $this->_condition = $this->getMockForAbstractClass( - \Magento\Rule\Model\Condition\Product\AbstractProduct::class, + AbstractProduct::class, [], '', false ); $this->productCategoryListProperty = new \ReflectionProperty( - \Magento\Rule\Model\Condition\Product\AbstractProduct::class, + AbstractProduct::class, 'productCategoryList' ); $this->productCategoryListProperty->setAccessible(true); $this->_entityAttributeValuesProperty = new \ReflectionProperty( - \Magento\Rule\Model\Condition\Product\AbstractProduct::class, + AbstractProduct::class, '_entityAttributeValues' ); $this->_entityAttributeValuesProperty->setAccessible(true); $this->_configProperty = new \ReflectionProperty( - \Magento\Rule\Model\Condition\Product\AbstractProduct::class, + AbstractProduct::class, '_config' ); $this->_configProperty->setAccessible(true); } + /** + * Test to validate equal category id condition + */ public function testValidateAttributeEqualCategoryId() { - $product = $this->createPartialMock(\Magento\Framework\Model\AbstractModel::class, ["getAttribute"]); + $product = $this->createPartialMock(AbstractModel::class, ["getAttribute"]); $this->_condition->setAttribute('category_ids'); $this->_condition->setValueParsed('1'); $this->_condition->setOperator('{}'); - $productCategoryList = $this->getMockBuilder(\Magento\Catalog\Model\ProductCategoryList::class) + $productCategoryList = $this->getMockBuilder(ProductCategoryList::class) ->disableOriginalConstructor() ->getMock(); $productCategoryList->method('getCategoryIds')->willReturn([1, 2]); @@ -91,7 +109,7 @@ public function testValidateAttributeEqualCategoryId() ); $this->_configProperty->setValue( $this->_condition, - $this->getMockBuilder(\Magento\Eav\Model\Config::class) + $this->getMockBuilder(Config::class) ->disableOriginalConstructor() ->getMock() ); @@ -99,10 +117,13 @@ public function testValidateAttributeEqualCategoryId() $this->assertTrue($this->_condition->validate($product)); } + /** + * Test to validate empty attribute condition + */ public function testValidateEmptyEntityAttributeValues() { $product = $this->createPartialMock( - \Magento\Framework\Model\AbstractModel::class, + AbstractModel::class, ["getAttribute", 'getResource'] ); $product->expects($this->once()) @@ -110,7 +131,7 @@ public function testValidateEmptyEntityAttributeValues() ->willReturn(null); $product->setId(1); $configProperty = new \ReflectionProperty( - \Magento\Rule\Model\Condition\Product\AbstractProduct::class, + AbstractProduct::class, '_entityAttributeValues' ); $configProperty->setAccessible(true); @@ -118,10 +139,13 @@ public function testValidateEmptyEntityAttributeValues() $this->assertFalse($this->_condition->validate($product)); } + /** + * Test to validate empty attribute value condition + */ public function testValidateEmptyEntityAttributeValuesWithResource() { $product = $this->createPartialMock( - \Magento\Framework\Model\AbstractModel::class, + AbstractModel::class, ["getAttribute", 'getResource'] ); $product->setId(1); @@ -132,18 +156,18 @@ public function testValidateEmptyEntityAttributeValuesWithResource() $this->_configProperty->setValue( $this->_condition, - $this->createMock(\Magento\Eav\Model\Config::class) + $this->createMock(Config::class) ); - $attribute = new \Magento\Framework\DataObject(); + $attribute = new DataObject(); $attribute->setBackendType('datetime'); - $newResource = $this->createPartialMock(\Magento\Catalog\Model\ResourceModel\Product::class, ['getAttribute']); + $newResource = $this->createPartialMock(Product::class, ['getAttribute']); $newResource->expects($this->any()) ->method('getAttribute') ->with('someAttribute') ->will($this->returnValue($attribute)); - $newResource->_config = $this->createMock(\Magento\Eav\Model\Config::class); + $newResource->_config = $this->createMock(Config::class); $product->expects($this->atLeastOnce()) ->method('getResource') ->willReturn($newResource); @@ -154,22 +178,25 @@ public function testValidateEmptyEntityAttributeValuesWithResource() $attribute->setBackendType('null'); $attribute->setFrontendInput('multiselect'); - $newResource = $this->createPartialMock(\Magento\Catalog\Model\ResourceModel\Product::class, ['getAttribute']); + $newResource = $this->createPartialMock(Product::class, ['getAttribute']); $newResource->expects($this->any()) ->method('getAttribute') ->with('someAttribute') ->will($this->returnValue($attribute)); - $newResource->_config = $this->createMock(\Magento\Eav\Model\Config::class); + $newResource->_config = $this->createMock(Config::class); $product->setResource($newResource); $this->assertFalse($this->_condition->validate($product)); } + /** + * Test to validate set entity attribute value with resource condition + */ public function testValidateSetEntityAttributeValuesWithResource() { $this->_condition->setAttribute('someAttribute'); $product = $this->createPartialMock( - \Magento\Framework\Model\AbstractModel::class, + AbstractModel::class, ['getAttribute', 'getResource'] ); $product->setAtribute('attribute'); @@ -177,22 +204,22 @@ public function testValidateSetEntityAttributeValuesWithResource() $this->_configProperty->setValue( $this->_condition, - $this->createMock(\Magento\Eav\Model\Config::class) + $this->createMock(Config::class) ); $this->_entityAttributeValuesProperty->setValue( $this->_condition, - $this->createMock(\Magento\Eav\Model\Config::class) + $this->createMock(Config::class) ); - $attribute = new \Magento\Framework\DataObject(); + $attribute = new DataObject(); $attribute->setBackendType('datetime'); - $newResource = $this->createPartialMock(\Magento\Catalog\Model\ResourceModel\Product::class, ['getAttribute']); + $newResource = $this->createPartialMock(Product::class, ['getAttribute']); $newResource->expects($this->any()) ->method('getAttribute') ->with('someAttribute') ->will($this->returnValue($attribute)); - $newResource->_config = $this->createMock(\Magento\Eav\Model\Config::class); + $newResource->_config = $this->createMock(Config::class); $product->expects($this->atLeastOnce()) ->method('getResource') @@ -209,10 +236,13 @@ public function testValidateSetEntityAttributeValuesWithResource() $this->assertFalse($this->_condition->validate($product)); } + /** + * Test to validate set entity attribute value without resource condition + */ public function testValidateSetEntityAttributeValuesWithoutResource() { $product = $this->createPartialMock( - \Magento\Framework\Model\AbstractModel::class, + AbstractModel::class, ['someMethod', 'getResource', 'load'] ); $this->_condition->setAttribute('someAttribute'); @@ -221,23 +251,23 @@ public function testValidateSetEntityAttributeValuesWithoutResource() $this->_configProperty->setValue( $this->_condition, - $this->createMock(\Magento\Eav\Model\Config::class) + $this->createMock(Config::class) ); $this->_entityAttributeValuesProperty->setValue( $this->_condition, - $this->createMock(\Magento\Eav\Model\Config::class) + $this->createMock(Config::class) ); - $attribute = new \Magento\Framework\DataObject(); + $attribute = new DataObject(); $attribute->setBackendType('multiselect'); - $newResource = $this->createPartialMock(\Magento\Catalog\Model\ResourceModel\Product::class, ['getAttribute']); + $newResource = $this->createPartialMock(Product::class, ['getAttribute']); $newResource->expects($this->any()) ->method('getAttribute') ->with('someAttribute') ->will($this->returnValue($attribute)); - $newResource->_config = $this->createMock(\Magento\Eav\Model\Config::class); + $newResource->_config = $this->createMock(Config::class); $product->expects($this->atLeastOnce()) ->method('getResource') @@ -254,16 +284,16 @@ public function testValidateSetEntityAttributeValuesWithoutResource() $this->assertFalse($this->_condition->validate($product)); - $attribute = new \Magento\Framework\DataObject(); + $attribute = new DataObject(); $attribute->setBackendType(null); $attribute->setFrontendInput('multiselect'); - $newResource = $this->createPartialMock(\Magento\Catalog\Model\ResourceModel\Product::class, ['getAttribute']); + $newResource = $this->createPartialMock(Product::class, ['getAttribute']); $newResource->expects($this->any()) ->method('getAttribute') ->with('someAttribute') ->will($this->returnValue($attribute)); - $newResource->_config = $this->createMock(\Magento\Eav\Model\Config::class); + $newResource->_config = $this->createMock(Config::class); $product->setResource($newResource); $product->setId(1); @@ -272,19 +302,29 @@ public function testValidateSetEntityAttributeValuesWithoutResource() $this->assertFalse($this->_condition->validate($product)); } + /** + * Test to get tables to join + */ public function testGetjointTables() { $this->_condition->setAttribute('category_ids'); $this->assertEquals([], $this->_condition->getTablesToJoin()); } + /** + * Test to get mapped sql field + */ public function testGetMappedSqlField() { $this->_condition->setAttribute('category_ids'); $this->assertEquals('e.entity_id', $this->_condition->getMappedSqlField()); + $this->_condition->setAttribute('attribute_set_id'); + $this->assertEquals('e.attribute_set_id', $this->_condition->getMappedSqlField()); } /** + * Test to prepare value options + * * @param array $setData * @param string $attributeObjectFrontendInput * @param array $attrObjectSourceAllOptionsValue @@ -308,7 +348,7 @@ public function testPrepareValueOptions( $this->_condition->setData($key, $value); } - $attrObjectSourceMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\Source\AbstractSource::class) + $attrObjectSourceMock = $this->getMockBuilder(AbstractSource::class) ->setMethods(['getAllOptions']) ->disableOriginalConstructor() ->getMock(); @@ -318,7 +358,7 @@ public function testPrepareValueOptions( ->with($expectedAttrObjSourceAllOptionsParam) ->willReturn($attrObjectSourceAllOptionsValue); - $attributeObjectMock = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) + $attributeObjectMock = $this->getMockBuilder(Attribute::class) ->setMethods(['usesSource', 'getFrontendInput', 'getSource', 'getAllOptions']) ->disableOriginalConstructor() ->getMock(); @@ -329,28 +369,28 @@ public function testPrepareValueOptions( ->willReturn($attributeObjectFrontendInput); $attributeObjectMock->method('getSource')->willReturn($attrObjectSourceMock); - $entityTypeMock = $this->getMockBuilder(\Magento\Framework\Model\AbstractModel\Type::class) + $entityTypeMock = $this->getMockBuilder(Type::class) ->setMethods(['getId']) ->disableOriginalConstructor() ->getMock(); $entityTypeMock->method('getId')->willReturn('SomeEntityType'); $configValueMock = $this->createPartialMock( - \Magento\Eav\Model\Config::class, + Config::class, ['getAttribute', 'getEntityType'] ); $configValueMock->method('getAttribute')->willReturn($attributeObjectMock); $configValueMock->method('getEntityType')->willReturn($entityTypeMock); - $configProperty = new ReflectionProperty( - \Magento\Rule\Model\Condition\Product\AbstractProduct::class, + $configProperty = new \ReflectionProperty( + AbstractProduct::class, '_config' ); $configProperty->setAccessible(true); $configProperty->setValue($this->_condition, $configValueMock); $attrSetCollectionValueMock = $this - ->getMockBuilder(\Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\Collection::class) + ->getMockBuilder(Collection::class) ->setMethods(['setEntityTypeFilter', 'load', 'toOptionArray']) ->disableOriginalConstructor() ->getMock(); @@ -362,15 +402,15 @@ public function testPrepareValueOptions( ->method('toOptionArray') ->willReturn($attrSetCollectionOptionsArray); - $attrSetCollectionProperty = new ReflectionProperty( - \Magento\Rule\Model\Condition\Product\AbstractProduct::class, + $attrSetCollectionProperty = new \ReflectionProperty( + AbstractProduct::class, '_attrSetCollection' ); $attrSetCollectionProperty->setAccessible(true); $attrSetCollectionProperty->setValue($this->_condition, $attrSetCollectionValueMock); - $testedMethod = new ReflectionMethod( - \Magento\Rule\Model\Condition\Product\AbstractProduct::class, + $testedMethod = new \ReflectionMethod( + AbstractProduct::class, '_prepareValueOptions' ); $testedMethod->setAccessible(true); @@ -395,7 +435,6 @@ public function prepareValueOptionsDataProvider() 'value_option' => ['k' => 'v'], ], null, null, null, null, ['key' => 'value'], ['k' => 'v'], ], - [ ['attribute' => 'attribute_set_id'], null, @@ -414,7 +453,6 @@ public function prepareValueOptionsDataProvider() 'value2' => 'Label for value 2' ] ], - [ [ 'value_select_options' => [ @@ -436,7 +474,6 @@ public function prepareValueOptionsDataProvider() 'value4' => 'Label for value 4' ] ], - [ [ 'value_select_options' => [ @@ -458,7 +495,6 @@ public function prepareValueOptionsDataProvider() 'value6' => 'Label for value 6' ] ], - [ [], 'multiselect', @@ -477,7 +513,6 @@ public function prepareValueOptionsDataProvider() 'value8' => 'Label for value 8', ], ], - [ [], 'multiselect', @@ -495,7 +530,6 @@ public function prepareValueOptionsDataProvider() 'valueA' => 'Label for value A', ], ], - [ [], 'select', diff --git a/app/code/Magento/Sales/Controller/AbstractController/Reorder.php b/app/code/Magento/Sales/Controller/AbstractController/Reorder.php index 65cb537e89fec..f53ecaa625bf5 100644 --- a/app/code/Magento/Sales/Controller/AbstractController/Reorder.php +++ b/app/code/Magento/Sales/Controller/AbstractController/Reorder.php @@ -4,16 +4,18 @@ * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sales\Controller\AbstractController; use Magento\Framework\App\Action; use Magento\Framework\Registry; use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Sales\Helper\Reorder as ReorderHelper; /** * Abstract class for controllers Reorder(Customer) and Reorder(Guest) - * - * @package Magento\Sales\Controller\AbstractController */ abstract class Reorder extends Action\Action implements HttpPostActionInterface { @@ -28,17 +30,27 @@ abstract class Reorder extends Action\Action implements HttpPostActionInterface protected $_coreRegistry; /** + * @var ReorderHelper + */ + private $reorderHelper; + + /** + * Constructor + * * @param Action\Context $context * @param OrderLoaderInterface $orderLoader * @param Registry $registry + * @param ReorderHelper|null $reorderHelper */ public function __construct( Action\Context $context, OrderLoaderInterface $orderLoader, - Registry $registry + Registry $registry, + ReorderHelper $reorderHelper = null ) { $this->orderLoader = $orderLoader; $this->_coreRegistry = $registry; + $this->reorderHelper = $reorderHelper ?: ObjectManager::getInstance()->get(ReorderHelper::class); parent::__construct($context); } @@ -57,6 +69,11 @@ public function execute() /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); + if (!$this->reorderHelper->canReorder($order->getId())) { + $this->messageManager->addErrorMessage(__("Reorder is not available.")); + return $resultRedirect->setPath('checkout/cart'); + } + /* @var $cart \Magento\Checkout\Model\Cart */ $cart = $this->_objectManager->get(\Magento\Checkout\Model\Cart::class); $items = $order->getItemsCollection(); diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderForCustomerWithTwoAddressesTaxableAndNonTaxableTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderForCustomerWithTwoAddressesTaxableAndNonTaxableTest.xml index 1c35a9e900a8a..8177bbaa6e1d7 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderForCustomerWithTwoAddressesTaxableAndNonTaxableTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderForCustomerWithTwoAddressesTaxableAndNonTaxableTest.xml @@ -45,6 +45,10 @@ </actionGroup> <!--Step 2: Select taxable address as billing address--> <selectOption selector="{{AdminOrderFormBillingAddressSection.selectAddress}}" userInput="{{US_Address_CA.state}}" stepKey="selectTaxableAddress" /> + <waitForPageLoad stepKey="waitForChangeBillingAddress"/> + <!--Step 3: Set shipping address same as billing --> + <checkOption selector="{{AdminOrderFormShippingAddressSection.SameAsBilling}}" stepKey="checkSameAsBillingAddressCheckbox"/> + <waitForPageLoad stepKey="waitForChangeShippingAddress"/> <!--Step 3: Select FlatRate shipping method--> <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="selectFlatRateShippingMethod"/> <!--Step 4: Verify that tax is applied to the order--> diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Guest/ReorderTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Guest/ReorderTest.php new file mode 100644 index 0000000000000..964a10f232daf --- /dev/null +++ b/app/code/Magento/Sales/Test/Unit/Controller/Guest/ReorderTest.php @@ -0,0 +1,147 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Sales\Test\Unit\Controller\Guest; + +use Magento\Framework\App\Action\Context; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\Result\Redirect; +use Magento\Framework\Controller\Result\RedirectFactory; +use Magento\Framework\Message\ManagerInterface as MessageManagerInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Sales\Controller\Guest\OrderLoader; +use Magento\Sales\Controller\Guest\Reorder; +use Magento\Sales\Helper\Reorder as ReorderHelper; +use Magento\Sales\Model\Order; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Test class for Reorder + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ReorderTest extends TestCase +{ + /** + * Stub Order Id + */ + private const STUB_ORDER_ID = 1; + + /** + * @var Reorder + */ + private $reorder; + + /** + * @var Registry|MockObject + */ + private $registryMock; + + /** + * @var OrderLoader|MockObject + */ + private $orderLoaderMock; + + /** + * @var RequestInterface|MockObject + */ + private $requestMock; + + /** + * @var RedirectFactory|MockObject + */ + private $resultRedirectFactoryMock; + + /** + * @var ReorderHelper|MockObject + */ + private $reorderHelperMock; + + /** + * @var MessageManagerInterface|MockObject + */ + private $messageManagerMock; + + /** + * Setup environment for test + */ + protected function setUp() + { + $contextMock = $this->createMock(Context::class); + $this->registryMock = $this->createMock(Registry::class); + $this->orderLoaderMock = $this->createMock(OrderLoader::class); + $this->requestMock = $this->createMock(RequestInterface::class); + $this->resultRedirectFactoryMock = $this->createMock(RedirectFactory::class); + $this->reorderHelperMock = $this->createMock(ReorderHelper::class); + $this->messageManagerMock = $this->createMock(MessageManagerInterface::class); + + $contextMock->expects($this->once())->method('getRequest')->willReturn($this->requestMock); + $contextMock->expects($this->once())->method('getResultRedirectFactory') + ->willReturn($this->resultRedirectFactoryMock); + $contextMock->expects($this->once())->method('getMessageManager') + ->willReturn($this->messageManagerMock); + + $objectManagerMock = $this->createMock(ObjectManagerInterface::class); + $objectManagerMock->expects($this->once())->method('get') + ->with(ReorderHelper::class) + ->willReturn($this->reorderHelperMock); + + ObjectManager::setInstance($objectManagerMock); + + $objectManager = new ObjectManagerHelper($this); + $this->reorder = $objectManager->getObject( + Reorder::class, + [ + 'context' => $contextMock, + 'orderLoader' => $this->orderLoaderMock, + 'registry' => $this->registryMock + ] + ); + } + + /** + * Test execute() with the reorder is not allowed + */ + public function testExecuteWithReorderIsNotAllowed() + { + $orderMock = $this->createMock(Order::class); + $orderMock->method('getId')->willReturn(self::STUB_ORDER_ID); + + $this->orderLoaderMock->method('load') + ->with($this->requestMock) + ->willReturn($this->resultRedirectFactoryMock); + + $this->registryMock->expects($this->once())->method('registry') + ->with('current_order') + ->willReturn($orderMock); + + $this->reorderHelperMock->method('canReorder')->with(self::STUB_ORDER_ID) + ->willReturn(false); + + $resultRedirectMock = $this->createMock(Redirect::class); + $this->resultRedirectFactoryMock->expects($this->once())->method('create')->willReturn($resultRedirectMock); + + $this->reorderHelperMock->method('canReorder')->with(self::STUB_ORDER_ID) + ->willReturn(false); + + /** Expected Error Message */ + $this->messageManagerMock->expects($this->once()) + ->method('addErrorMessage') + ->with('Reorder is not available.')->willReturnSelf(); + $resultRedirectMock->expects($this->once()) + ->method('setPath') + ->with('checkout/cart')->willReturnSelf(); + + /** Assert result */ + $this->assertEquals($resultRedirectMock, $this->reorder->execute()); + } +} diff --git a/app/code/Magento/Sales/etc/adminhtml/system.xml b/app/code/Magento/Sales/etc/adminhtml/system.xml index 473d3068d69c7..84caeca093bad 100644 --- a/app/code/Magento/Sales/etc/adminhtml/system.xml +++ b/app/code/Magento/Sales/etc/adminhtml/system.xml @@ -17,31 +17,31 @@ <resource>Magento_Sales::config_sales</resource> <group id="general" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1"> <label>General</label> - <field id="hide_customer_ip" translate="label comment" type="select" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="hide_customer_ip" translate="label comment" type="select" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Hide Customer IP</label> <comment>Choose whether a customer IP is shown in orders, invoices, shipments, and credit memos.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> </group> - <group id="totals_sort" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="totals_sort" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1"> <label>Checkout Totals Sort Order</label> - <field id="discount" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="discount" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Discount</label> <validate>required-number validate-number</validate> </field> - <field id="grand_total" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="grand_total" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Grand Total</label> <validate>required-number validate-number</validate> </field> - <field id="shipping" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="shipping" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Shipping</label> <validate>required-number validate-number</validate> </field> - <field id="subtotal" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="subtotal" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Subtotal</label> <validate>required-number validate-number</validate> </field> - <field id="tax" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="tax" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Tax</label> <validate>required-number validate-number</validate> </field> @@ -86,21 +86,21 @@ </group> <group id="minimum_order" translate="label" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Minimum Order Amount</label> - <field id="active" translate="label" sortOrder="5" type="select" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="active" translate="label" sortOrder="5" type="select" showInDefault="1" showInWebsite="1"> <label>Enable</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="amount" translate="label comment" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="amount" translate="label comment" sortOrder="10" showInDefault="1" showInWebsite="1"> <label>Minimum Amount</label> <validate>validate-number validate-greater-than-zero</validate> <comment>Subtotal after discount.</comment> </field> - <field id="include_discount_amount" translate="label" sortOrder="12" type="select" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="include_discount_amount" translate="label" sortOrder="12" type="select" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Include Discount Amount</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Choosing yes will be used subtotal after discount, otherwise only subtotal will be used.</comment> </field> - <field id="tax_including" translate="label" sortOrder="15" type="select" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="tax_including" translate="label" sortOrder="15" type="select" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Include Tax to Amount</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> @@ -111,7 +111,7 @@ <field id="error_message" translate="label" sortOrder="30" type="textarea" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Error to Show in Shopping Cart</label> </field> - <field id="multi_address" translate="label" sortOrder="40" type="select" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="multi_address" translate="label" sortOrder="40" type="select" showInDefault="1" showInWebsite="1"> <label>Validate Each Address Separately in Multi-address Checkout</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> @@ -124,17 +124,17 @@ <comment>We'll use the default error above if you leave this empty.</comment> </field> </group> - <group id="dashboard" translate="label" sortOrder="60" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="dashboard" translate="label" sortOrder="60" showInDefault="1"> <label>Dashboard</label> - <field id="use_aggregated_data" translate="label comment" sortOrder="10" type="select" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="use_aggregated_data" translate="label comment" sortOrder="10" type="select" showInDefault="1" canRestore="1"> <label>Use Aggregated Data</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Improves dashboard performance but provides non-realtime data.</comment> </field> </group> - <group id="orders" translate="label" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="orders" translate="label" sortOrder="70" showInDefault="1" showInWebsite="1"> <label>Orders Cron Settings</label> - <field id="delete_pending_after" translate="label" type="text" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="delete_pending_after" translate="label" type="text" sortOrder="6" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Pending Payment Order Lifetime (minutes)</label> <validate>validate-number validate-greater-than-zero</validate> </field> @@ -144,14 +144,14 @@ <label>Sales Emails</label> <tab>sales</tab> <resource>Magento_Sales::sales_email</resource> - <group id="general" type="text" sortOrder="0" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="general" type="text" sortOrder="0" showInDefault="1"> <label>General Settings</label> - <field id="async_sending" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="async_sending" translate="label" type="select" sortOrder="1" showInDefault="1" canRestore="1"> <label>Asynchronous sending</label> <source_model>Magento\Config\Model\Config\Source\Enabledisable</source_model> <backend_model>Magento\Sales\Model\Config\Backend\Email\AsyncSending</backend_model> </field> - <field id="sending_limit" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="sending_limit" translate="label" type="text" sortOrder="2" showInDefault="1" canRestore="1"> <label>Limit per cron run</label> <comment>Limit how many entities (orders/shipments/etc) will be processed during one cron run.</comment> <validate>required-number validate-number validate-greater-than-zero</validate> @@ -169,25 +169,40 @@ <field id="identity" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>New Order Confirmation Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="template" translate="label comment" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>New Order Confirmation Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="guest_template" translate="label comment" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>New Order Confirmation Template for Guest</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_to" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Order Email Copy To</label> <comment>Comma-separated.</comment> <validate>validate-emails</validate> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_method" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Send Order Email Copy Method</label> <source_model>Magento\Config\Model\Config\Source\Email\Method</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> </group> <group id="order_comment" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1"> @@ -199,25 +214,40 @@ <field id="identity" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Order Comment Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="template" translate="label comment" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Order Comment Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="guest_template" translate="label comment" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Order Comment Email Template for Guest</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_to" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Order Comment Email Copy To</label> <comment>Comma-separated.</comment> <validate>validate-emails</validate> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_method" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Send Order Comments Email Copy Method</label> <source_model>Magento\Config\Model\Config\Source\Email\Method</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> </group> <group id="invoice" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1"> @@ -229,25 +259,40 @@ <field id="identity" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Invoice Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="template" translate="label comment" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Invoice Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="guest_template" translate="label comment" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Invoice Email Template for Guest</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_to" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Invoice Email Copy To</label> <comment>Comma-separated.</comment> <validate>validate-emails</validate> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_method" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Send Invoice Email Copy Method</label> <source_model>Magento\Config\Model\Config\Source\Email\Method</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> </group> <group id="invoice_comment" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> @@ -259,25 +304,40 @@ <field id="identity" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Invoice Comment Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="template" translate="label comment" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Invoice Comment Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="guest_template" translate="label comment" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Invoice Comment Email Template for Guest</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_to" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Invoice Comment Email Copy To</label> <comment>Comma-separated.</comment> <validate>validate-emails</validate> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_method" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Send Invoice Comments Email Copy Method</label> <source_model>Magento\Config\Model\Config\Source\Email\Method</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> </group> <group id="shipment" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1"> @@ -289,25 +349,40 @@ <field id="identity" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Shipment Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="template" translate="label comment" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Shipment Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="guest_template" translate="label comment" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Shipment Email Template for Guest</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_to" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Shipment Email Copy To</label> <comment>Comma-separated.</comment> <validate>validate-emails</validate> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_method" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Send Shipment Email Copy Method</label> <source_model>Magento\Config\Model\Config\Source\Email\Method</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> </group> <group id="shipment_comment" translate="label" type="text" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="1"> @@ -319,25 +394,40 @@ <field id="identity" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Shipment Comment Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="template" translate="label comment" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Shipment Comment Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="guest_template" translate="label comment" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Shipment Comment Email Template for Guest</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_to" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Shipment Comment Email Copy To</label> <comment>Comma-separated.</comment> <validate>validate-emails</validate> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_method" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Send Shipment Comments Email Copy Method</label> <source_model>Magento\Config\Model\Config\Source\Email\Method</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> </group> <group id="creditmemo" translate="label" type="text" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="1"> @@ -349,25 +439,40 @@ <field id="identity" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Credit Memo Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="template" translate="label comment" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Credit Memo Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="guest_template" translate="label comment" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Credit Memo Email Template for Guest</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_to" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Credit Memo Email Copy To</label> <comment>Comma-separated.</comment> <validate>validate-emails</validate> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_method" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Send Credit Memo Email Copy Method</label> <source_model>Magento\Config\Model\Config\Source\Email\Method</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> </group> <group id="creditmemo_comment" translate="label" type="text" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="1"> @@ -379,25 +484,40 @@ <field id="identity" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Credit Memo Comment Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="template" translate="label comment" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Credit Memo Comment Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="guest_template" translate="label comment" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Credit Memo Comment Email Template for Guest</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_to" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Credit Memo Comment Email Copy To</label> <comment>Comma-separated.</comment> <validate>validate-emails</validate> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="copy_method" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Send Credit Memo Comments Email Copy Method</label> <source_model>Magento\Config\Model\Config\Source\Email\Method</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> </group> </section> @@ -437,9 +557,9 @@ </group> </section> <section id="dev"> - <group id="grid" translate="label" type="text" sortOrder="131" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="grid" translate="label" type="text" sortOrder="131" showInDefault="1"> <label>Grid Settings</label> - <field id="async_indexing" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="async_indexing" translate="label" type="select" sortOrder="1" showInDefault="1" canRestore="1"> <label>Asynchronous indexing</label> <source_model>Magento\Config\Model\Config\Source\Enabledisable</source_model> <backend_model>Magento\Sales\Model\Config\Backend\Grid\AsyncIndexing</backend_model> diff --git a/app/code/Magento/Sales/etc/config.xml b/app/code/Magento/Sales/etc/config.xml index 2480da4ad214b..6918146d86337 100644 --- a/app/code/Magento/Sales/etc/config.xml +++ b/app/code/Magento/Sales/etc/config.xml @@ -8,6 +8,9 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> <default> <sales> + <general> + <hide_customer_ip>0</hide_customer_ip> + </general> <totals_sort> <discount>20</discount> <grand_total>100</grand_total> diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml index f6618c9884d60..9f705c1a674c1 100644 --- a/app/code/Magento/Sales/etc/di.xml +++ b/app/code/Magento/Sales/etc/di.xml @@ -819,14 +819,6 @@ </argument> </arguments> </type> - <type name="Magento\Framework\View\Element\UiComponent\DataProvider\FilterPool"> - <arguments> - <argument name="appliers" xsi:type="array"> - <item name="regular" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\RegularFilter</item> - <item name="fulltext" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\FulltextFilter</item> - </argument> - </arguments> - </type> <type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory"> <arguments> <argument name="collections" xsi:type="array"> diff --git a/app/code/Magento/Sales/i18n/en_US.csv b/app/code/Magento/Sales/i18n/en_US.csv index f30315437533f..970df2770a524 100644 --- a/app/code/Magento/Sales/i18n/en_US.csv +++ b/app/code/Magento/Sales/i18n/en_US.csv @@ -799,3 +799,4 @@ Refunds,Refunds "Allow Zero GrandTotal","Allow Zero GrandTotal" "Could not save the shipment tracking","Could not save the shipment tracking" "Please enter a coupon code!","Please enter a coupon code!" +"Reorder is not available.","Reorder is not available." diff --git a/app/code/Magento/SalesRule/etc/adminhtml/system.xml b/app/code/Magento/SalesRule/etc/adminhtml/system.xml index 0e30d1484c637..2655add5f8bbf 100644 --- a/app/code/Magento/SalesRule/etc/adminhtml/system.xml +++ b/app/code/Magento/SalesRule/etc/adminhtml/system.xml @@ -7,29 +7,29 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> - <section id="promo" translate="label" type="text" sortOrder="400" showInDefault="1" showInWebsite="0" showInStore="0"> + <section id="promo" translate="label" type="text" sortOrder="400" showInDefault="1"> <class>separator-top</class> <label>Promotions</label> <tab>customer</tab> <resource>Magento_SalesRule::config_promo</resource> <group id="auto_generated_coupon_codes" translate="label" showInDefault="1" sortOrder="10"> <label>Auto Generated Specific Coupon Codes</label> - <field id="length" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="length" translate="label comment" type="text" sortOrder="10" showInDefault="1" canRestore="1"> <label>Code Length</label> <comment>Excluding prefix, suffix and separators.</comment> <frontend_class>validate-digits</frontend_class> </field> - <field id="format" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="format" translate="label" type="select" sortOrder="20" showInDefault="1" canRestore="1"> <label>Code Format</label> <source_model>Magento\SalesRule\Model\System\Config\Source\Coupon\Format</source_model> </field> - <field id="prefix" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="prefix" translate="label" type="text" sortOrder="30" showInDefault="1"> <label>Code Prefix</label> </field> - <field id="suffix" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="suffix" translate="label" type="text" sortOrder="40" showInDefault="1"> <label>Code Suffix</label> </field> - <field id="dash" translate="label comment" type="text" sortOrder="50" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="dash" translate="label comment" type="text" sortOrder="50" showInDefault="1"> <label>Dash Every X Characters</label> <comment>If empty no separation.</comment> <frontend_class>validate-digits</frontend_class> diff --git a/app/code/Magento/Search/etc/adminhtml/system.xml b/app/code/Magento/Search/etc/adminhtml/system.xml index d3b7f64fbe0c4..8a539c9528e8e 100644 --- a/app/code/Magento/Search/etc/adminhtml/system.xml +++ b/app/code/Magento/Search/etc/adminhtml/system.xml @@ -9,7 +9,7 @@ <system> <section id="catalog"> <group id="search"> - <field id="engine" translate="label" type="select" sortOrder="19" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="engine" translate="label" type="select" sortOrder="19" showInDefault="1"> <label>Search Engine</label> <source_model>Magento\Search\Model\Adminhtml\System\Config\Source\Engine</source_model> </field> diff --git a/app/code/Magento/Search/etc/di.xml b/app/code/Magento/Search/etc/di.xml index 4aa211d23f0a2..382174f4327a2 100644 --- a/app/code/Magento/Search/etc/di.xml +++ b/app/code/Magento/Search/etc/di.xml @@ -45,14 +45,6 @@ <preference for="Magento\Search\Model\AutocompleteInterface" type="Magento\Search\Model\Autocomplete" /> <preference for="Magento\Search\Model\Autocomplete\ItemInterface" type="Magento\Search\Model\Autocomplete\Item" /> <preference for="Magento\Framework\Search\SearchEngineInterface" type="Magento\Search\Model\SearchEngine"/> - <type name="Magento\Framework\View\Element\UiComponent\DataProvider\FilterPool"> - <arguments> - <argument name="appliers" xsi:type="array"> - <item name="regular" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\RegularFilter</item> - <item name="fulltext" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\FulltextFilter</item> - </argument> - </arguments> - </type> <!-- @api --> <virtualType name="Magento\Search\Model\ResourceModel\Synonyms\Grid\Collection" type="Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult"> <arguments> diff --git a/app/code/Magento/SendFriend/etc/adminhtml/system.xml b/app/code/Magento/SendFriend/etc/adminhtml/system.xml index 5cace4bcf92db..6360c42655a09 100644 --- a/app/code/Magento/SendFriend/etc/adminhtml/system.xml +++ b/app/code/Magento/SendFriend/etc/adminhtml/system.xml @@ -24,22 +24,37 @@ <label>Select Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="allow_guest" translate="label" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Allow for Guests</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="max_recipients" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Max Recipients</label> <frontend_class>validate-digits</frontend_class> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="max_per_hour" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Max Products Sent in 1 Hour</label> <frontend_class>validate-digits</frontend_class> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="check_by" translate="label" type="select" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Limit Sending By</label> <source_model>Magento\SendFriend\Model\Source\Checktype</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> </group> </section> diff --git a/app/code/Magento/Shipping/etc/adminhtml/system.xml b/app/code/Magento/Shipping/etc/adminhtml/system.xml index b0d38bc0d61a1..29862bdcfc8b1 100644 --- a/app/code/Magento/Shipping/etc/adminhtml/system.xml +++ b/app/code/Magento/Shipping/etc/adminhtml/system.xml @@ -11,32 +11,32 @@ <label>Shipping Settings</label> <tab>sales</tab> <resource>Magento_Shipping::config_shipping</resource> - <group id="origin" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="origin" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1"> <label>Origin</label> - <field id="country_id" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="country_id" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Country</label> <frontend_class>countries</frontend_class> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> </field> - <field id="region_id" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="region_id" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Region/State</label> </field> - <field id="postcode" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="postcode" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" canRestore="1"> <label>ZIP/Postal Code</label> </field> - <field id="city" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="city" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1"> <label>City</label> </field> - <field id="street_line1" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="street_line1" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="1"> <label>Street Address</label> </field> - <field id="street_line2" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="street_line2" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="1"> <label>Street Address Line 2</label> </field> </group> <group id="shipping_policy" translate="label" type="text" sortOrder="120" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Shipping Policy Parameters</label> - <field id="enable_shipping_policy" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="enable_shipping_policy" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Apply custom Shipping Policy</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> diff --git a/app/code/Magento/Shipping/etc/config.xml b/app/code/Magento/Shipping/etc/config.xml index 4c2e018191b59..e1a71ef9741bb 100644 --- a/app/code/Magento/Shipping/etc/config.xml +++ b/app/code/Magento/Shipping/etc/config.xml @@ -13,6 +13,9 @@ <postcode>90034</postcode> <region_id>12</region_id> </origin> + <shipping_policy> + <enable_shipping_policy>0</enable_shipping_policy> + </shipping_policy> </shipping> </default> </config> diff --git a/app/code/Magento/Signifyd/etc/adminhtml/system.xml b/app/code/Magento/Signifyd/etc/adminhtml/system.xml index 272ce78aec2e5..182a67e4e1f35 100644 --- a/app/code/Magento/Signifyd/etc/adminhtml/system.xml +++ b/app/code/Magento/Signifyd/etc/adminhtml/system.xml @@ -11,9 +11,9 @@ <label>Fraud Protection</label> <tab>sales</tab> <resource>Magento_Sales::fraud_protection</resource> - <group id="signifyd" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="signifyd" type="text" sortOrder="10" showInDefault="1" showInWebsite="1"> <fieldset_css>signifyd-logo-header</fieldset_css> - <group id="about" translate="label comment" sortOrder="15" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="about" translate="label comment" sortOrder="15" showInDefault="1" showInWebsite="1"> <frontend_model>Magento\Signifyd\Block\Adminhtml\System\Config\Fieldset\Info</frontend_model> <fieldset_css>signifyd-about-header</fieldset_css> <label><![CDATA[Protect your store from fraud with Guaranteed Fraud Protection by Signifyd.]]></label> @@ -26,17 +26,17 @@ </comment> <more_url>https://www.signifyd.com/magento-guaranteed-fraud-protection</more_url> </group> - <group id="config" translate="label comment" sortOrder="15" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="config" translate="label comment" sortOrder="15" showInDefault="1" showInWebsite="1"> <fieldset_css>signifyd-about-header</fieldset_css> <label>Configuration</label> <comment><![CDATA[<a href="https://www.signifyd.com/resources/manual/magento-2/signifyd-on-magento-integration-guide/" target="_blank">View our setup guide</a> for step-by-step instructions on how to integrate Signifyd with Magento.<br />For support contact <a href="mailto:support@signifyd.com">support@signifyd.com</a>.]]> </comment> - <field id="active" translate="label" type="select" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="active" translate="label" type="select" showInDefault="1" showInWebsite="1"> <label>Enable this Solution</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>fraud_protection/signifyd/active</config_path> </field> - <field id="api_key" translate="label" type="obscure" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="api_key" translate="label" type="obscure" sortOrder="20" showInDefault="1" showInWebsite="1"> <label>API Key</label> <comment><![CDATA[Your API key can be found on the <a href="http://signifyd.com/settings" target="_blank">settings page</a> in the Signifyd console.]]></comment> <config_path>fraud_protection/signifyd/api_key</config_path> @@ -45,7 +45,7 @@ <field id="active">1</field> </depends> </field> - <field id="api_url" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="api_url" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1"> <label>API URL</label> <config_path>fraud_protection/signifyd/api_url</config_path> <comment>Don’t change unless asked to do so.</comment> @@ -53,7 +53,7 @@ <field id="active">1</field> </depends> </field> - <field id="debug" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="debug" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1"> <label>Debug</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>fraud_protection/signifyd/debug</config_path> @@ -61,7 +61,7 @@ <field id="active">1</field> </depends> </field> - <field id="webhook_url" translate="label comment" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="webhook_url" translate="label comment" type="text" sortOrder="50" showInDefault="1" showInWebsite="1"> <label>Webhook URL</label> <comment><![CDATA[Your webhook URL will be used to <a href="https://app.signifyd.com/settings/notifications" target="_blank">configure</a> a guarantee completed webhook in Signifyd. Webhooks are used to sync Signifyd`s guarantee decisions back to Magento.]]></comment> <attribute type="handler_url">signifyd/webhooks/handler</attribute> diff --git a/app/code/Magento/Sitemap/etc/adminhtml/system.xml b/app/code/Magento/Sitemap/etc/adminhtml/system.xml index c3c9c85027354..57c426c68e83f 100644 --- a/app/code/Magento/Sitemap/etc/adminhtml/system.xml +++ b/app/code/Magento/Sitemap/etc/adminhtml/system.xml @@ -51,7 +51,7 @@ <comment>Valid values range from 0.0 to 1.0.</comment> </field> </group> - <group id="generate" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="generate" translate="label" type="text" sortOrder="4" showInDefault="1"> <label>Generation Settings</label> <field id="enabled" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Enabled</label> @@ -60,23 +60,38 @@ <field id="error_email" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Error Email Recipient</label> <validate>validate-email</validate> + <depends> + <field id="enabled">1</field> + </depends> </field> - <field id="error_email_identity" translate="label" type="select" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="error_email_identity" translate="label" type="select" sortOrder="6" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Error Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> - <field id="error_email_template" translate="label comment" type="select" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="error_email_template" translate="label comment" type="select" sortOrder="7" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Error Email Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="frequency" translate="label" type="select" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Frequency</label> <source_model>Magento\Cron\Model\Config\Source\Frequency</source_model> <backend_model>Magento\Cron\Model\Config\Backend\Sitemap</backend_model> + <depends> + <field id="enabled">1</field> + </depends> </field> <field id="time" translate="label" type="time" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Start Time</label> + <depends> + <field id="enabled">1</field> + </depends> </field> </group> <group id="limit" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1"> diff --git a/app/code/Magento/Swatches/view/adminhtml/web/js/text.js b/app/code/Magento/Swatches/view/adminhtml/web/js/text.js index 2bb1672d9e8c8..48a14c14bdf3a 100644 --- a/app/code/Magento/Swatches/view/adminhtml/web/js/text.js +++ b/app/code/Magento/Swatches/view/adminhtml/web/js/text.js @@ -13,7 +13,8 @@ define([ 'mage/template', 'uiRegistry', 'jquery/ui', - 'prototype' + 'prototype', + 'validation' ], function (jQuery, mageTemplate, rg) { 'use strict'; diff --git a/app/code/Magento/Swatches/view/adminhtml/web/js/visual.js b/app/code/Magento/Swatches/view/adminhtml/web/js/visual.js index b91fea59229cd..19307432c4122 100644 --- a/app/code/Magento/Swatches/view/adminhtml/web/js/visual.js +++ b/app/code/Magento/Swatches/view/adminhtml/web/js/visual.js @@ -14,7 +14,8 @@ define([ 'uiRegistry', 'jquery/colorpicker/js/colorpicker', 'prototype', - 'jquery/ui' + 'jquery/ui', + 'validation' ], function (jQuery, mageTemplate, rg) { 'use strict'; diff --git a/app/code/Magento/Tax/etc/adminhtml/system.xml b/app/code/Magento/Tax/etc/adminhtml/system.xml index 7fc1744b8e27e..c1e1286041ce6 100644 --- a/app/code/Magento/Tax/etc/adminhtml/system.xml +++ b/app/code/Magento/Tax/etc/adminhtml/system.xml @@ -14,59 +14,59 @@ <resource>Magento_Tax::config_tax</resource> <group id="classes" translate="label" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Tax Classes</label> - <field id="shipping_tax_class" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="shipping_tax_class" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Tax Class for Shipping</label> <source_model>Magento\Tax\Model\TaxClass\Source\Product</source_model> </field> - <field id="default_product_tax_class" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="default_product_tax_class" translate="label" type="select" sortOrder="20" showInDefault="1" canRestore="1"> <label>Default Tax Class for Product</label> <source_model>Magento\Tax\Model\TaxClass\Source\Product</source_model> <backend_model>Magento\Tax\Model\Config\TaxClass</backend_model> </field> - <field id="default_customer_tax_class" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="default_customer_tax_class" translate="label" type="select" sortOrder="30" showInDefault="1" canRestore="1"> <label>Default Tax Class for Customer</label> <source_model>Magento\Tax\Model\TaxClass\Source\Customer</source_model> </field> </group> <group id="calculation" translate="label" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Calculation Settings</label> - <field id="algorithm" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="algorithm" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Tax Calculation Method Based On</label> <source_model>Magento\Tax\Model\System\Config\Source\Algorithm</source_model> </field> - <field id="based_on" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="based_on" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Tax Calculation Based On</label> <source_model>Magento\Tax\Model\Config\Source\Basedon</source_model> <backend_model>Magento\Tax\Model\Config\Notification</backend_model> </field> - <field id="price_includes_tax" translate="label comment" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="price_includes_tax" translate="label comment" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Catalog Prices</label> <comment>This sets whether catalog prices entered from Magento Admin include tax.</comment> <backend_model>Magento\Tax\Model\Config\Price\IncludePrice</backend_model> <source_model>Magento\Tax\Model\System\Config\Source\PriceType</source_model> </field> - <field id="shipping_includes_tax" translate="label comment" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="shipping_includes_tax" translate="label comment" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Shipping Prices</label> <comment>This sets whether shipping amounts entered from Magento Admin or obtained from gateways include tax.</comment> <backend_model>Magento\Tax\Model\Config\Price\IncludePrice</backend_model> <source_model>Magento\Tax\Model\System\Config\Source\PriceType</source_model> </field> - <field id="apply_after_discount" translate="label comment" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="apply_after_discount" translate="label comment" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Apply Customer Tax</label> <source_model>Magento\Tax\Model\System\Config\Source\Apply</source_model> <backend_model>Magento\Tax\Model\Config\Notification</backend_model> </field> - <field id="discount_tax" translate="label comment" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="discount_tax" translate="label comment" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Apply Discount On Prices</label> <source_model>Magento\Tax\Model\System\Config\Source\PriceType</source_model> <backend_model>Magento\Tax\Model\Config\Notification</backend_model> <comment>Warning: To apply the discount on prices including tax and apply the tax after discount, set Catalog Prices to “Including Tax”.</comment> </field> - <field id="apply_tax_on" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="apply_tax_on" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Apply Tax On</label> <source_model>Magento\Tax\Model\Config\Source\Apply\On</source_model> </field> - <field id="cross_border_trade_enabled" translate="label comment" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="cross_border_trade_enabled" translate="label comment" type="select" sortOrder="70" showInDefault="1" showInWebsite="1"> <label>Enable Cross Border Trade</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>When catalog price includes tax, enable this setting to fix the price no matter what the customer's tax rate.</comment> diff --git a/app/code/Magento/Theme/Block/Html/Title.php b/app/code/Magento/Theme/Block/Html/Title.php index ea0feb403180c..9059afe19ab05 100644 --- a/app/code/Magento/Theme/Block/Html/Title.php +++ b/app/code/Magento/Theme/Block/Html/Title.php @@ -5,7 +5,9 @@ */ namespace Magento\Theme\Block\Html; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\View\Element\Template; +use Magento\Store\Model\ScopeInterface; /** * Html page title block @@ -19,6 +21,16 @@ */ class Title extends Template { + /** + * Config path to 'Translate Title' header settings + */ + private const XML_PATH_HEADER_TRANSLATE_TITLE = 'design/header/translate_title'; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + /** * Own page title to display on the page * @@ -26,6 +38,22 @@ class Title extends Template */ protected $pageTitle; + /** + * Constructor + * + * @param Template\Context $context + * @param ScopeConfigInterface $scopeConfig + * @param array $data + */ + public function __construct( + Template\Context $context, + ScopeConfigInterface $scopeConfig, + array $data = [] + ) { + parent::__construct($context, $data); + $this->scopeConfig = $scopeConfig; + } + /** * Provide own page title or pick it from Head Block * @@ -36,7 +64,10 @@ public function getPageTitle() if (!empty($this->pageTitle)) { return $this->pageTitle; } - return __($this->pageConfig->getTitle()->getShort()); + + $pageTitle = $this->pageConfig->getTitle()->getShort(); + + return $this->shouldTranslateTitle() ? __($pageTitle) : $pageTitle; } /** @@ -46,10 +77,9 @@ public function getPageTitle() */ public function getPageHeading() { - if (!empty($this->pageTitle)) { - return __($this->pageTitle); - } - return __($this->pageConfig->getTitle()->getShortHeading()); + $pageTitle = !empty($this->pageTitle) ? $this->pageTitle : $this->pageConfig->getTitle()->getShortHeading(); + + return $this->shouldTranslateTitle() ? __($pageTitle) : $pageTitle; } /** @@ -62,4 +92,17 @@ public function setPageTitle($pageTitle) { $this->pageTitle = $pageTitle; } + + /** + * Check if page title should be translated + * + * @return bool + */ + private function shouldTranslateTitle(): bool + { + return $this->scopeConfig->isSetFlag( + static::XML_PATH_HEADER_TRANSLATE_TITLE, + ScopeInterface::SCOPE_STORE + ); + } } diff --git a/app/code/Magento/Theme/Test/Unit/Block/Html/TitleTest.php b/app/code/Magento/Theme/Test/Unit/Block/Html/TitleTest.php index a4d56748eee6b..d6357d8d61995 100644 --- a/app/code/Magento/Theme/Test/Unit/Block/Html/TitleTest.php +++ b/app/code/Magento/Theme/Test/Unit/Block/Html/TitleTest.php @@ -5,29 +5,51 @@ */ namespace Magento\Theme\Test\Unit\Block\Html; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Phrase; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\View\Element\Template\Context; +use Magento\Framework\View\Page\Config; +use Magento\Framework\View\Page\Title as PageTitle; +use Magento\Store\Model\ScopeInterface; +use Magento\Theme\Block\Html\Title; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class TitleTest extends \PHPUnit\Framework\TestCase +/** + * Test class for \Magento\Theme\Block\Html\Title + */ +class TitleTest extends TestCase { + /** + * Config path to 'Translate Title' header settings + */ + private const XML_PATH_HEADER_TRANSLATE_TITLE = 'design/header/translate_title'; + /** * @var ObjectManagerHelper */ - protected $objectManagerHelper; + private $objectManagerHelper; + + /** + * @var Config|MockObject + */ + private $pageConfigMock; /** - * @var \Magento\Framework\View\Page\Config|\PHPUnit_Framework_MockObject_MockObject + * @var PageTitle|MockObject */ - protected $pageConfigMock; + private $pageTitleMock; /** - * @var \Magento\Framework\View\Page\Title|\PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ - protected $pageTitleMock; + private $scopeConfigMock; /** - * @var \Magento\Theme\Block\Html\Title + * @var Title */ - protected $htmlTitle; + private $htmlTitle; /** * @return void @@ -35,17 +57,22 @@ class TitleTest extends \PHPUnit\Framework\TestCase protected function setUp() { $this->objectManagerHelper = new ObjectManagerHelper($this); - $this->pageConfigMock = $this->createMock(\Magento\Framework\View\Page\Config::class); - $this->pageTitleMock = $this->createMock(\Magento\Framework\View\Page\Title::class); + $this->pageConfigMock = $this->createMock(Config::class); + $this->pageTitleMock = $this->createMock(PageTitle::class); $context = $this->objectManagerHelper->getObject( - \Magento\Framework\View\Element\Template\Context::class, + Context::class, ['pageConfig' => $this->pageConfigMock] ); + $this->scopeConfigMock = $this->getMockForAbstractClass(ScopeConfigInterface::class); + $this->htmlTitle = $this->objectManagerHelper->getObject( - \Magento\Theme\Block\Html\Title::class, - ['context' => $context] + Title::class, + [ + 'context' => $context, + 'scopeConfig' => $this->scopeConfigMock + ] ); } @@ -64,10 +91,16 @@ public function testGetPageTitleWithSetPageTitle() } /** + * @param bool $shouldTranslateTitle + * * @return void + * @dataProvider dataProviderShouldTranslateTitle */ - public function testGetPageTitle() + public function testGetPageTitle($shouldTranslateTitle) { + $this->scopeConfigMock->method('isSetFlag') + ->with(static::XML_PATH_HEADER_TRANSLATE_TITLE, ScopeInterface::SCOPE_STORE) + ->willReturn($shouldTranslateTitle); $title = 'some title'; $this->pageTitleMock->expects($this->once()) @@ -77,28 +110,58 @@ public function testGetPageTitle() ->method('getTitle') ->willReturn($this->pageTitleMock); - $this->assertEquals($title, $this->htmlTitle->getPageTitle()); + $result = $this->htmlTitle->getPageTitle(); + + if ($shouldTranslateTitle) { + $this->assertInstanceOf(Phrase::class, $result); + } else { + $this->assertInternalType('string', $result); + } + + $this->assertEquals($title, $result); } /** + * @param bool $shouldTranslateTitle + * * @return void + * @dataProvider dataProviderShouldTranslateTitle */ - public function testGetPageHeadingWithSetPageTitle() + public function testGetPageHeadingWithSetPageTitle($shouldTranslateTitle) { + $this->scopeConfigMock->method('isSetFlag') + ->with(static::XML_PATH_HEADER_TRANSLATE_TITLE, ScopeInterface::SCOPE_STORE) + ->willReturn($shouldTranslateTitle); + $title = 'some title'; $this->htmlTitle->setPageTitle($title); $this->pageConfigMock->expects($this->never()) ->method('getTitle'); - $this->assertEquals($title, $this->htmlTitle->getPageHeading()); + $result = $this->htmlTitle->getPageHeading(); + + if ($shouldTranslateTitle) { + $this->assertInstanceOf(Phrase::class, $result); + } else { + $this->assertInternalType('string', $result); + } + + $this->assertEquals($title, $result); } /** + * @param bool $shouldTranslateTitle + * * @return void + * @dataProvider dataProviderShouldTranslateTitle */ - public function testGetPageHeading() + public function testGetPageHeading($shouldTranslateTitle) { + $this->scopeConfigMock->method('isSetFlag') + ->with(static::XML_PATH_HEADER_TRANSLATE_TITLE, ScopeInterface::SCOPE_STORE) + ->willReturn($shouldTranslateTitle); + $title = 'some title'; $this->pageTitleMock->expects($this->once()) @@ -108,6 +171,29 @@ public function testGetPageHeading() ->method('getTitle') ->willReturn($this->pageTitleMock); - $this->assertEquals($title, $this->htmlTitle->getPageHeading()); + $result = $this->htmlTitle->getPageHeading(); + + if ($shouldTranslateTitle) { + $this->assertInstanceOf(Phrase::class, $result); + } else { + $this->assertInternalType('string', $result); + } + + $this->assertEquals($title, $result); + } + + /** + * @return array + */ + public function dataProviderShouldTranslateTitle(): array + { + return [ + [ + true + ], + [ + false + ] + ]; } } diff --git a/app/code/Magento/Theme/etc/config.xml b/app/code/Magento/Theme/etc/config.xml index 1515c357e094e..c733f2a9503ee 100644 --- a/app/code/Magento/Theme/etc/config.xml +++ b/app/code/Magento/Theme/etc/config.xml @@ -43,6 +43,7 @@ Disallow: /*SID= </search_engine_robots> <header translate="welcome"> <welcome>Default welcome msg!</welcome> + <translate_title>1</translate_title> </header> <footer translate="copyright"> <copyright>Copyright © 2013-present Magento, Inc. All rights reserved.</copyright> diff --git a/app/code/Magento/Theme/etc/di.xml b/app/code/Magento/Theme/etc/di.xml index 62f51e74b6007..9e06f6c0f4e51 100644 --- a/app/code/Magento/Theme/etc/di.xml +++ b/app/code/Magento/Theme/etc/di.xml @@ -206,6 +206,10 @@ <item name="path" xsi:type="string">design/header/welcome</item> <item name="fieldset" xsi:type="string">other_settings/header</item> </item> + <item name="header_translate_title" xsi:type="array"> + <item name="path" xsi:type="string">design/header/translate_title</item> + <item name="fieldset" xsi:type="string">other_settings/header</item> + </item> <item name="footer_copyright" xsi:type="array"> <item name="path" xsi:type="string">design/footer/copyright</item> <item name="fieldset" xsi:type="string">other_settings/footer</item> diff --git a/app/code/Magento/Theme/view/adminhtml/ui_component/design_config_form.xml b/app/code/Magento/Theme/view/adminhtml/ui_component/design_config_form.xml index bc1f36222dd60..dfe11f3120cd8 100644 --- a/app/code/Magento/Theme/view/adminhtml/ui_component/design_config_form.xml +++ b/app/code/Magento/Theme/view/adminhtml/ui_component/design_config_form.xml @@ -208,6 +208,20 @@ <dataScope>header_logo_alt</dataScope> </settings> </field> + <field name="header_translate_title" formElement="select"> + <settings> + <dataType>text</dataType> + <label translate="true">Translate Title</label> + <dataScope>header_translate_title</dataScope> + </settings> + <formElements> + <select> + <settings> + <options class="Magento\Config\Model\Config\Source\Yesno"/> + </settings> + </select> + </formElements> + </field> </fieldset> <fieldset name="footer"> <settings> diff --git a/app/code/Magento/Translation/etc/adminhtml/system.xml b/app/code/Magento/Translation/etc/adminhtml/system.xml index ab854f8a4db52..b19ac5d1bfb09 100644 --- a/app/code/Magento/Translation/etc/adminhtml/system.xml +++ b/app/code/Magento/Translation/etc/adminhtml/system.xml @@ -9,7 +9,7 @@ <system> <section id="dev"> <group id="js"> - <field id="translate_strategy" translate="label comment" type="select" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="translate_strategy" translate="label comment" type="select" sortOrder="30" showInDefault="1" canRestore="1"> <label>Translation Strategy</label> <source_model>Magento\Translation\Model\Js\Config\Source\Strategy</source_model> <comment>Please put your store into maintenance mode and redeploy static files after changing strategy</comment> diff --git a/app/code/Magento/Ui/etc/adminhtml/system.xml b/app/code/Magento/Ui/etc/adminhtml/system.xml index ab4272f8d2a34..77af492c70b36 100644 --- a/app/code/Magento/Ui/etc/adminhtml/system.xml +++ b/app/code/Magento/Ui/etc/adminhtml/system.xml @@ -9,12 +9,12 @@ <system> <section id="dev"> <group id="js"> - <field id="session_storage_logging" translate="label comment" type="select" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="session_storage_logging" translate="label comment" type="select" sortOrder="100" showInDefault="1" canRestore="1"> <label>Log JS Errors to Session Storage</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>If enabled, can be used by functional tests for extended reporting</comment> </field> - <field id="session_storage_key" translate="label comment" type="text" sortOrder="110" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="session_storage_key" translate="label comment" type="text" sortOrder="110" showInDefault="1" canRestore="1"> <label>Log JS Errors to Session Storage Key</label> <comment>Use this key to retrieve collected js errors</comment> </field> diff --git a/app/code/Magento/Ui/etc/di.xml b/app/code/Magento/Ui/etc/di.xml index 05ace9d556fa0..b0cef3b90d431 100644 --- a/app/code/Magento/Ui/etc/di.xml +++ b/app/code/Magento/Ui/etc/di.xml @@ -194,13 +194,11 @@ <argument name="uiReader" xsi:type="object">uiDefinitionReader</argument> </arguments> </type> - <type name="Magento\Ui\Component\Filter\FilterPool"> + <type name="Magento\Framework\View\Element\UiComponent\DataProvider\FilterPool"> <arguments> - <argument name="filters" xsi:type="array"> - <item name="filter_input" xsi:type="string">Magento\Ui\Component\Filter\Type\Input</item> - <item name="filter_select" xsi:type="string">Magento\Ui\Component\Filter\Type\Select</item> - <item name="filter_range" xsi:type="string">Magento\Ui\Component\Filter\Type\Range</item> - <item name="filter_store" xsi:type="string">Magento\Ui\Component\Filter\Type\Store</item> + <argument name="appliers" xsi:type="array"> + <item name="regular" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\RegularFilter</item> + <item name="fulltext" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\FulltextFilter</item> </argument> </arguments> </type> diff --git a/app/code/Magento/Ui/view/base/web/js/lib/core/collection.js b/app/code/Magento/Ui/view/base/web/js/lib/core/collection.js index 0491390d2b6c2..33acba6103b10 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/core/collection.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/core/collection.js @@ -214,6 +214,19 @@ define([ return regions[name]; }, + /** + * Checks if the specified region has any elements + * associated with it. + * + * @param {String} name + * @returns {Boolean} + */ + regionHasElements: function (name) { + var region = this.getRegion(name); + + return region().length > 0; + }, + /** * Replaces specified regions' data with a provided one. * Creates region if it was not created yet. diff --git a/app/code/Magento/Ups/etc/adminhtml/system.xml b/app/code/Magento/Ups/etc/adminhtml/system.xml index c6a2516e96170..3a1676d221977 100644 --- a/app/code/Magento/Ups/etc/adminhtml/system.xml +++ b/app/code/Magento/Ups/etc/adminhtml/system.xml @@ -10,147 +10,147 @@ <section id="carriers"> <group id="ups" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="1"> <label>UPS</label> - <field id="access_license_number" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="access_license_number" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1"> <label>Access License Number</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> <depends> <field id="carriers/ups/active">1</field> </depends> </field> - <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enabled for Checkout</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="allowed_methods" translate="label" type="multiselect" sortOrder="170" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allowed_methods" translate="label" type="multiselect" sortOrder="170" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Allowed Methods</label> <source_model>Magento\Ups\Model\Config\Source\Method</source_model> <can_be_empty>1</can_be_empty> </field> - <field id="shipment_requesttype" translate="label" type="select" sortOrder="47" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="shipment_requesttype" translate="label" type="select" sortOrder="47" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Packages Request Type</label> <source_model>Magento\Shipping\Model\Config\Source\Online\Requesttype</source_model> </field> - <field id="container" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="container" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Container</label> <source_model>Magento\Ups\Model\Config\Source\Container</source_model> </field> - <field id="free_shipping_enable" translate="label" type="select" sortOrder="210" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="free_shipping_enable" translate="label" type="select" sortOrder="210" showInDefault="1" showInWebsite="1"> <label>Enable Free Shipping Threshold</label> <source_model>Magento\Config\Model\Config\Source\Enabledisable</source_model> </field> - <field id="free_shipping_subtotal" translate="label" type="text" sortOrder="220" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="free_shipping_subtotal" translate="label" type="text" sortOrder="220" showInDefault="1" showInWebsite="1"> <label>Free Shipping Amount Threshold</label> <validate>validate-number validate-zero-or-greater</validate> <depends> <field id="free_shipping_enable">1</field> </depends> </field> - <field id="dest_type" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="dest_type" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Destination Type</label> <source_model>Magento\Ups\Model\Config\Source\DestType</source_model> </field> - <field id="free_method" translate="label" type="select" sortOrder="200" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="free_method" translate="label" type="select" sortOrder="200" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Free Method</label> <frontend_class>free-method</frontend_class> <source_model>Magento\Ups\Model\Config\Source\Freemethod</source_model> </field> - <field id="gateway_url" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="gateway_url" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Gateway URL</label> <backend_model>Magento\Ups\Model\Config\Backend\UpsUrl</backend_model> </field> - <field id="gateway_xml_url" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="gateway_xml_url" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Gateway XML URL</label> <backend_model>Magento\Ups\Model\Config\Backend\UpsUrl</backend_model> </field> - <field id="handling_type" translate="label" type="select" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="handling_type" translate="label" type="select" sortOrder="110" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Calculate Handling Fee</label> <source_model>Magento\Shipping\Model\Source\HandlingType</source_model> </field> - <field id="handling_action" translate="label" type="select" sortOrder="120" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="handling_action" translate="label" type="select" sortOrder="120" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Handling Applied</label> <source_model>Magento\Shipping\Model\Source\HandlingAction</source_model> </field> - <field id="handling_fee" translate="label" type="text" sortOrder="130" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="handling_fee" translate="label" type="text" sortOrder="130" showInDefault="1" showInWebsite="1"> <label>Handling Fee</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="max_package_weight" translate="label" type="text" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="max_package_weight" translate="label" type="text" sortOrder="80" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Maximum Package Weight (Please consult your shipping carrier for maximum supported shipping weight)</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="min_package_weight" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="min_package_weight" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Minimum Package Weight (Please consult your shipping carrier for minimum supported shipping weight)</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="origin_shipment" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="origin_shipment" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Origin of the Shipment</label> <source_model>Magento\Ups\Model\Config\Source\OriginShipment</source_model> </field> - <field id="password" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="password" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1"> <label>Password</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> <depends> <field id="carriers/ups/active">1</field> </depends> </field> - <field id="pickup" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="pickup" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Pickup Method</label> <source_model>Magento\Ups\Model\Config\Source\Pickup</source_model> </field> - <field id="sort_order" translate="label" type="text" sortOrder="1000" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="1000" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <validate>validate-number validate-zero-or-greater</validate> </field> <field id="title" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Title</label> </field> - <field id="tracking_xml_url" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="tracking_xml_url" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Tracking XML URL</label> <backend_model>Magento\Ups\Model\Config\Backend\UpsUrl</backend_model> </field> - <field id="type" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="type" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" canRestore="1"> <label>UPS Type</label> <source_model>Magento\Ups\Model\Config\Source\Type</source_model> </field> - <field id="is_account_live" translate="label" type="select" sortOrder="25" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="is_account_live" translate="label" type="select" sortOrder="25" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Live Account</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="unit_of_measure" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="unit_of_measure" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Weight Unit</label> <source_model>Magento\Ups\Model\Config\Source\Unitofmeasure</source_model> </field> - <field id="username" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="username" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1"> <label>User ID</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> <depends> <field id="carriers/ups/active">1</field> </depends> </field> - <field id="negotiated_active" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="negotiated_active" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enable Negotiated Rates</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="include_taxes" translate="label" type="select" sortOrder="45" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="include_taxes" translate="label" type="select" sortOrder="45" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Request Tax-Inclusive Rate</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>When applicable, taxes (sales tax, VAT etc.) are included in the rate.</comment> </field> - <field id="shipper_number" translate="label comment" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="shipper_number" translate="label comment" type="text" sortOrder="50" showInDefault="1" showInWebsite="1"> <label>Shipper Number</label> <comment>Required for negotiated rates; 6-character UPS</comment> </field> - <field id="sallowspecific" translate="label" type="select" sortOrder="900" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="sallowspecific" translate="label" type="select" sortOrder="900" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Ship to Applicable Countries</label> <frontend_class>shipping-applicable-country</frontend_class> <source_model>Magento\Shipping\Model\Config\Source\Allspecificcountries</source_model> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="910" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="910" showInDefault="1" showInWebsite="1"> <label>Ship to Specific Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> </field> - <field id="showmethod" translate="label" type="select" sortOrder="920" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="showmethod" translate="label" type="select" sortOrder="920" showInDefault="1" showInWebsite="1"> <label>Show Method if Not Applicable</label> <frontend_class>shipping-skip-hide</frontend_class> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> @@ -158,12 +158,12 @@ <field id="specificerrmsg" translate="label" type="textarea" sortOrder="800" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Displayed Error Message</label> </field> - <field id="mode_xml" translate="label comment" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="mode_xml" translate="label comment" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Mode</label> <comment>This enables or disables SSL verification of the Magento server by UPS.</comment> <source_model>Magento\Shipping\Model\Config\Source\Online\Mode</source_model> </field> - <field id="debug" translate="label" type="select" sortOrder="920" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="debug" translate="label" type="select" sortOrder="920" showInDefault="1" showInWebsite="1"> <label>Debug</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUserClickRoleResourceTabActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUserClickRoleResourceTabActionGroup.xml new file mode 100644 index 0000000000000..3e20eaf973674 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUserClickRoleResourceTabActionGroup.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminUserClickRoleResourceTabActionGroup"> + <annotations> + <description>Switch to role resource tab.</description> + </annotations> + <click selector="{{AdminEditRoleInfoSection.roleResourcesTab}}" stepKey="clickRoleResourcesTab" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUserOpenAdminRolesPageActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUserOpenAdminRolesPageActionGroup.xml new file mode 100644 index 0000000000000..71be9117e5caf --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUserOpenAdminRolesPageActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminUserOpenAdminRolesPageActionGroup"> + <annotations> + <description>Navigate to User Role Grid</description> + </annotations> + <amOnPage url="{{AdminRolesPage.url}}" stepKey="navigateToUserRoleGrid" /> + <waitForPageLoad stepKey="waitForRolesGridLoad" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUserSaveRoleActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUserSaveRoleActionGroup.xml new file mode 100644 index 0000000000000..824e9407125f5 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUserSaveRoleActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminUserSaveRoleActionGroup"> + <annotations> + <description>Click to Save Role</description> + </annotations> + <click selector="{{AdminEditRoleInfoSection.saveButton}}" stepKey="clickSaveRoleButton" /> + <see userInput="You saved the role." stepKey="seeUserRoleSavedMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/etc/adminhtml/system.xml b/app/code/Magento/User/etc/adminhtml/system.xml index 6c57b268968c3..584b40a023c93 100644 --- a/app/code/Magento/User/etc/adminhtml/system.xml +++ b/app/code/Magento/User/etc/adminhtml/system.xml @@ -9,7 +9,7 @@ <system> <section id="admin"> <group id="emails"> - <field id="user_notification_template" translate="label comment" type="select" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="user_notification_template" translate="label comment" type="select" sortOrder="40" showInDefault="1" canRestore="1"> <label>User Notification Template</label> <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment> <source_model>Magento\Config\Model\Config\Source\Email\Template</source_model> diff --git a/app/code/Magento/User/etc/config.xml b/app/code/Magento/User/etc/config.xml index c1f51bcbecef4..57631df18bb85 100644 --- a/app/code/Magento/User/etc/config.xml +++ b/app/code/Magento/User/etc/config.xml @@ -16,6 +16,7 @@ </emails> <security> <password_reset_link_expiration_period>2</password_reset_link_expiration_period> + <use_case_sensitive_login>0</use_case_sensitive_login> <lockout_failures>6</lockout_failures> <lockout_threshold>30</lockout_threshold> <password_lifetime>90</password_lifetime> diff --git a/app/code/Magento/Usps/etc/adminhtml/system.xml b/app/code/Magento/Usps/etc/adminhtml/system.xml index 0849572e7eb1c..b01f7be9a19f9 100644 --- a/app/code/Magento/Usps/etc/adminhtml/system.xml +++ b/app/code/Magento/Usps/etc/adminhtml/system.xml @@ -10,102 +10,102 @@ <section id="carriers"> <group id="usps" translate="label" type="text" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="1"> <label>USPS</label> - <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enabled for Checkout</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="gateway_url" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="gateway_url" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Gateway URL</label> </field> - <field id="gateway_secure_url" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="gateway_secure_url" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Secure Gateway URL</label> </field> <field id="title" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Title</label> </field> - <field id="userid" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="userid" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1"> <label>User ID</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> - <field id="password" translate="label" type="obscure" sortOrder="53" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="password" translate="label" type="obscure" sortOrder="53" showInDefault="1" showInWebsite="1"> <label>Password</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> - <field id="mode" translate="label" type="select" sortOrder="54" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="mode" translate="label" type="select" sortOrder="54" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Mode</label> <source_model>Magento\Shipping\Model\Config\Source\Online\Mode</source_model> </field> - <field id="shipment_requesttype" translate="label" type="select" sortOrder="55" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="shipment_requesttype" translate="label" type="select" sortOrder="55" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Packages Request Type</label> <source_model>Magento\Shipping\Model\Config\Source\Online\Requesttype</source_model> </field> - <field id="container" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="container" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Container</label> <source_model>Magento\Usps\Model\Source\Container</source_model> </field> - <field id="size" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="size" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Size</label> <source_model>Magento\Usps\Model\Source\Size</source_model> </field> - <field id="width" translate="label" type="text" sortOrder="73" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="width" translate="label" type="text" sortOrder="73" showInDefault="1" showInWebsite="1"> <label>Width</label> <depends> <field id="size">LARGE</field> </depends> </field> - <field id="length" translate="label" type="text" sortOrder="72" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="length" translate="label" type="text" sortOrder="72" showInDefault="1" showInWebsite="1"> <label>Length</label> <depends> <field id="size">LARGE</field> </depends> </field> - <field id="height" translate="label" type="text" sortOrder="74" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="height" translate="label" type="text" sortOrder="74" showInDefault="1" showInWebsite="1"> <label>Height</label> <depends> <field id="size">LARGE</field> </depends> </field> - <field id="girth" translate="label" type="text" sortOrder="76" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="girth" translate="label" type="text" sortOrder="76" showInDefault="1" showInWebsite="1"> <label>Girth</label> <depends> <field id="size">LARGE</field> </depends> </field> - <field id="machinable" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="machinable" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Machinable</label> <source_model>Magento\Usps\Model\Source\Machinable</source_model> </field> - <field id="max_package_weight" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="max_package_weight" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Maximum Package Weight (Please consult your shipping carrier for maximum supported shipping weight)</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="handling_type" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="handling_type" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Calculate Handling Fee</label> <source_model>Magento\Shipping\Model\Source\HandlingType</source_model> </field> - <field id="handling_action" translate="label" type="select" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="handling_action" translate="label" type="select" sortOrder="110" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Handling Applied</label> <source_model>Magento\Shipping\Model\Source\HandlingAction</source_model> </field> - <field id="handling_fee" translate="label" type="text" sortOrder="120" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="handling_fee" translate="label" type="text" sortOrder="120" showInDefault="1" showInWebsite="1"> <label>Handling Fee</label> <validate>validate-number validate-zero-or-greater</validate> </field> - <field id="allowed_methods" translate="label" type="multiselect" sortOrder="130" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="allowed_methods" translate="label" type="multiselect" sortOrder="130" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Allowed Methods</label> <source_model>Magento\Usps\Model\Source\Method</source_model> <can_be_empty>1</can_be_empty> </field> - <field id="free_method" translate="label" type="select" sortOrder="140" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="free_method" translate="label" type="select" sortOrder="140" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Free Method</label> <frontend_class>free-method</frontend_class> <source_model>Magento\Usps\Model\Source\Freemethod</source_model> </field> - <field id="free_shipping_enable" translate="label" type="select" sortOrder="160" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="free_shipping_enable" translate="label" type="select" sortOrder="160" showInDefault="1" showInWebsite="1"> <label>Enable Free Shipping Threshold</label> <source_model>Magento\Config\Model\Config\Source\Enabledisable</source_model> </field> - <field id="free_shipping_subtotal" translate="label" type="text" sortOrder="165" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="free_shipping_subtotal" translate="label" type="text" sortOrder="165" showInDefault="1" showInWebsite="1"> <label>Free Shipping Amount Threshold</label> <validate>validate-number validate-zero-or-greater</validate> <depends> @@ -115,26 +115,26 @@ <field id="specificerrmsg" translate="label" type="textarea" sortOrder="170" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Displayed Error Message</label> </field> - <field id="sallowspecific" translate="label" type="select" sortOrder="180" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="sallowspecific" translate="label" type="select" sortOrder="180" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Ship to Applicable Countries</label> <frontend_class>shipping-applicable-country</frontend_class> <source_model>Magento\Shipping\Model\Config\Source\Allspecificcountries</source_model> </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="190" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="specificcountry" translate="label" type="multiselect" sortOrder="190" showInDefault="1" showInWebsite="1"> <label>Ship to Specific Countries</label> <source_model>Magento\Directory\Model\Config\Source\Country</source_model> <can_be_empty>1</can_be_empty> </field> - <field id="debug" translate="label" type="select" sortOrder="200" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="debug" translate="label" type="select" sortOrder="200" showInDefault="1" showInWebsite="1"> <label>Debug</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="showmethod" translate="label" type="select" sortOrder="210" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="showmethod" translate="label" type="select" sortOrder="210" showInDefault="1" showInWebsite="1"> <label>Show Method if Not Applicable</label> <frontend_class>shipping-skip-hide</frontend_class> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="sort_order" translate="label" type="text" sortOrder="220" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="sort_order" translate="label" type="text" sortOrder="220" showInDefault="1" showInWebsite="1"> <label>Sort Order</label> <validate>validate-number validate-zero-or-greater</validate> </field> diff --git a/app/code/Magento/WebapiSecurity/etc/adminhtml/system.xml b/app/code/Magento/WebapiSecurity/etc/adminhtml/system.xml index d6f40f5ac2023..ea60d34f78b0f 100644 --- a/app/code/Magento/WebapiSecurity/etc/adminhtml/system.xml +++ b/app/code/Magento/WebapiSecurity/etc/adminhtml/system.xml @@ -6,9 +6,9 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="webapi" type="text" sortOrder="102" showInDefault="1" showInWebsite="1" showInStore="1"> - <group id="webapisecurity" translate="label" type="text" sortOrder="250" showInDefault="1" showInWebsite="0" showInStore="0"> + <group id="webapisecurity" translate="label" type="text" sortOrder="250" showInDefault="1"> <label>Web API Security</label> - <field id="allow_insecure" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="allow_insecure" translate="label comment" type="select" sortOrder="1" showInDefault="1"> <label>Allow Anonymous Guest Access</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>This feature applies only to CMS, Catalog and Store APIs. Please consult your developers for details on potential security risks.</comment> diff --git a/app/code/Magento/Weee/etc/adminhtml/system.xml b/app/code/Magento/Weee/etc/adminhtml/system.xml index d3e9efb8f0b46..68ceae482daaa 100644 --- a/app/code/Magento/Weee/etc/adminhtml/system.xml +++ b/app/code/Magento/Weee/etc/adminhtml/system.xml @@ -10,39 +10,57 @@ <section id="tax"> <group id="weee" translate="label" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Fixed Product Taxes</label> - <field id="enable" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="enable" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Enable FPT</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="display_list" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="display_list" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Display Prices In Product Lists</label> <source_model>Magento\Weee\Model\Config\Source\Display</source_model> + <depends> + <field id="enable">1</field> + </depends> </field> - <field id="display" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="display" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Display Prices On Product View Page</label> <source_model>Magento\Weee\Model\Config\Source\Display</source_model> + <depends> + <field id="enable">1</field> + </depends> </field> - <field id="display_sales" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="display_sales" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Display Prices In Sales Modules</label> <source_model>Magento\Weee\Model\Config\Source\Display</source_model> + <depends> + <field id="enable">1</field> + </depends> </field> - <field id="display_email" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="display_email" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Display Prices In Emails</label> <source_model>Magento\Weee\Model\Config\Source\Display</source_model> + <depends> + <field id="enable">1</field> + </depends> </field> - <field id="apply_vat" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="apply_vat" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Apply Tax To FPT</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <depends> + <field id="enable">1</field> + </depends> </field> - <field id="include_in_subtotal" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="include_in_subtotal" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Include FPT In Subtotal</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <depends> + <field id="enable">1</field> + </depends> </field> </group> </section> <section id="sales"> <group id="totals_sort"> - <field id="weee" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="weee" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Fixed Product Tax</label> <validate>required-number validate-number</validate> </field> diff --git a/app/code/Magento/Wishlist/Controller/Index/Plugin.php b/app/code/Magento/Wishlist/Controller/Index/Plugin.php index 150e4de72b40d..63ae419759250 100644 --- a/app/code/Magento/Wishlist/Controller/Index/Plugin.php +++ b/app/code/Magento/Wishlist/Controller/Index/Plugin.php @@ -10,6 +10,7 @@ use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\RequestInterface; use Magento\Framework\App\Response\RedirectInterface; +use Magento\Store\Model\ScopeInterface; /** * Wishlist plugin before dispatch @@ -89,7 +90,7 @@ public function beforeDispatch(\Magento\Framework\App\ActionInterface $subject, $this->messageManager->addErrorMessage(__('You must login or register to add items to your wishlist.')); } } - if (!$this->config->isSetFlag('wishlist/general/active')) { + if (!$this->config->isSetFlag('wishlist/general/active', ScopeInterface::SCOPE_STORES)) { throw new NotFoundException(__('Page not found.')); } } diff --git a/app/code/Magento/Wishlist/Controller/Index/Update.php b/app/code/Magento/Wishlist/Controller/Index/Update.php index e5fbd4b93f82e..ceb001a61c405 100644 --- a/app/code/Magento/Wishlist/Controller/Index/Update.php +++ b/app/code/Magento/Wishlist/Controller/Index/Update.php @@ -11,7 +11,7 @@ use Magento\Framework\Controller\ResultFactory; /** - * Class Update + * Controller for updating wishlists */ class Update extends \Magento\Wishlist\Controller\AbstractIndex implements HttpPostActionInterface { @@ -70,7 +70,12 @@ public function execute() } $post = $this->getRequest()->getPostValue(); - if ($post && isset($post['description']) && is_array($post['description'])) { + $resultRedirect->setPath('*', ['wishlist_id' => $wishlist->getId()]); + if (!$post) { + return $resultRedirect; + } + + if (isset($post['description']) && is_array($post['description'])) { $updatedItems = 0; foreach ($post['description'] as $itemId => $description) { @@ -136,13 +141,12 @@ public function execute() $this->messageManager->addErrorMessage(__('Can\'t update wish list')); } } + } - if (isset($post['save_and_share'])) { - $resultRedirect->setPath('*/*/share', ['wishlist_id' => $wishlist->getId()]); - return $resultRedirect; - } + if (isset($post['save_and_share'])) { + $resultRedirect->setPath('*/*/share', ['wishlist_id' => $wishlist->getId()]); } - $resultRedirect->setPath('*', ['wishlist_id' => $wishlist->getId()]); + return $resultRedirect; } } diff --git a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/PluginTest.php b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/PluginTest.php index 2b583f9101516..53b9ba7d846b1 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/PluginTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/PluginTest.php @@ -6,8 +6,12 @@ namespace Magento\Wishlist\Test\Unit\Controller\Index; +use Magento\Store\Model\ScopeInterface; + /** * Test for wishlist plugin before dispatch + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class PluginTest extends \PHPUnit\Framework\TestCase { @@ -175,7 +179,7 @@ public function testBeforeDispatch() $this->config ->expects($this->once()) ->method('isSetFlag') - ->with('wishlist/general/active') + ->with('wishlist/general/active', ScopeInterface::SCOPE_STORES) ->willReturn(false); $this->getPlugin()->beforeDispatch($indexController, $this->request); diff --git a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/UpdateTest.php b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/UpdateTest.php new file mode 100644 index 0000000000000..88aeec5e5a924 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/UpdateTest.php @@ -0,0 +1,290 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Wishlist\Test\Unit\Controller\Index; + +use Magento\Backend\Model\View\Result\Redirect; +use Magento\Framework\App\Action\Context; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\Data\Form\FormKey\Validator; +use Magento\Framework\Exception\NotFoundException; +use Magento\Framework\Message\ManagerInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Wishlist\Controller\Index\Update; +use Magento\Wishlist\Controller\WishlistProviderInterface; +use Magento\Wishlist\Helper\Data; +use Magento\Wishlist\Model\Item; +use Magento\Wishlist\Model\LocaleQuantityProcessor; +use Magento\Wishlist\Model\Wishlist; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * Test for upate controller wishlist + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class UpdateTest extends TestCase +{ + private const STUB_ITEM_ID = 1; + + private const STUB_WISHLIST_PRODUCT_QTY = 21; + + /** + * @var MockObject|Validator $formKeyValidatorMock + */ + private $formKeyValidatorMock; + + /** + * @var MockObject|WishlistProviderInterface $wishlistProviderMock + */ + private $wishlistProviderMock; + + /** + * @var MockObject|LocaleQuantityProcessor $quantityProcessorMock + */ + private $quantityProcessorMock; + + /** + * @var Update $updateController + */ + private $updateController; + + /** + * @var MockObject|Context$contextMock + */ + private $contextMock; + + /** + * @var MockObject|Redirect $resultRedirectMock + */ + private $resultRedirectMock; + + /** + * @var MockObject|ResultFactory $resultFatoryMock + */ + private $resultFactoryMock; + + /** + * @var MockObject|RequestInterface $requestMock + */ + private $requestMock; + + /** + * @var MockObject|ObjectManagerInterface $objectManagerMock + */ + private $objectManagerMock; + + /** + * @var MockObject|ManagerInterface $messageManagerMock + */ + private $messageManagerMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->formKeyValidatorMock = $this->createMock(Validator::class); + $this->wishlistProviderMock = $this->createMock(WishlistProviderInterface::class); + $this->quantityProcessorMock = $this->createMock(LocaleQuantityProcessor::class); + $this->contextMock = $this->createMock(Context::class); + $this->resultRedirectMock = $this->createMock(Redirect::class); + $this->resultFactoryMock = $this->createPartialMock(ResultFactory::class, ['create']); + $this->messageManagerMock = $this->createMock(ManagerInterface::class); + $this->objectManagerMock = $this->createMock(ObjectManagerInterface::class); + $this->requestMock = $this->getMockBuilder(RequestInterface::class) + ->setMethods(['getPostValue']) + ->getMockForAbstractClass(); + + $this->resultFactoryMock->expects($this->any()) + ->method('create') + ->willReturn($this->resultRedirectMock); + $this->contextMock->expects($this->once()) + ->method('getResultFactory') + ->willReturn($this->resultFactoryMock); + $this->contextMock->expects($this->once()) + ->method('getObjectManager') + ->willReturn($this->objectManagerMock); + $this->contextMock->expects($this->any()) + ->method('getRequest') + ->willReturn($this->requestMock); + $this->contextMock->expects($this->any()) + ->method('getMessageManager') + ->willReturn($this->messageManagerMock); + + $objectManager = new ObjectManagerHelper($this); + + $this->updateController = $objectManager->getObject( + Update::class, + [ + 'context' => $this->contextMock, + '_formKeyValidator' => $this->formKeyValidatorMock, + 'wishlistProvider' => $this->wishlistProviderMock, + 'quantityProcessor' => $this->quantityProcessorMock + ] + ); + } + + /** + * Test for update method Wishlist controller. + * + * @dataProvider getWishlistDataProvider + * @param array $wishlistDataProvider + * @param array $postData + * @return void + */ + public function testUpdate(array $wishlistDataProvider, array $postData): void + { + $wishlist = $this->createMock(Wishlist::class); + $itemMock = $this->getMockBuilder(Item::class) + ->disableOriginalConstructor() + ->setMethods( + [ + 'load', + 'getId', + 'getWishlistId', + 'setQty', + 'save', + 'getDescription', + 'setDescription', + 'getProduct', + 'getName' + ] + )->getMock(); + $dataMock = $this->createMock(Data::class); + $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->formKeyValidatorMock->expects($this->once()) + ->method('validate') + ->with($this->requestMock) + ->willReturn(true); + $this->wishlistProviderMock->expects($this->once()) + ->method('getWishlist') + ->willReturn($wishlist); + $wishlist->expects($this->exactly(2)) + ->method('getId') + ->willReturn($wishlistDataProvider['id']); + $this->requestMock->expects($this->once()) + ->method('getPostValue') + ->willReturn($postData); + $this->resultRedirectMock->expects($this->once()) + ->method('setPath') + ->with('*', ['wishlist_id' => $wishlistDataProvider['id']]); + $this->objectManagerMock->expects($this->once()) + ->method('create') + ->with(Item::class) + ->willReturn($itemMock); + $itemMock->expects($this->once()) + ->method('load') + ->with(1) + ->willReturnSelf(); + $itemMock->expects($this->once()) + ->method('getWishLIstId') + ->willReturn($wishlistDataProvider['id']); + $itemMock->expects($this->once()) + ->method('getDescription') + ->willReturn(''); + $itemMock->expects($this->once()) + ->method('setDescription') + ->willReturnSelf(); + $itemMock->expects($this->once()) + ->method('setQty') + ->willReturnSelf(); + $this->objectManagerMock->expects($this->exactly(2)) + ->method('get') + ->with(Data::class) + ->willReturn($dataMock); + $dataMock->expects($this->once()) + ->method('defaultCommentString') + ->willReturn(''); + $dataMock->expects($this->once()) + ->method('calculate'); + $this->quantityProcessorMock->expects($this->once()) + ->method('process') + ->willReturn($postData['qty']); + $itemMock->expects($this->once()) + ->method('getProduct') + ->willReturn($productMock); + $productMock->expects($this->once()) + ->method('getName') + ->willReturn('product'); + $this->messageManagerMock->expects($this->once()) + ->method('addSuccessMessage'); + + $this->assertEquals($this->resultRedirectMock, $this->updateController->execute()); + } + + /** + * Verify update method if post data not available + * + * @dataProvider getWishlistDataProvider + * @param array $wishlistDataProvider + * @return void + */ + public function testUpdateRedirectWhenNoPostData(array $wishlistDataProvider): void + { + $wishlist = $this->createMock(Wishlist::class); + + $this->formKeyValidatorMock->expects($this->once()) + ->method('validate') + ->willReturn(true); + $this->wishlistProviderMock->expects($this->once()) + ->method('getWishlist') + ->willReturn($wishlist); + $wishlist->expects($this->exactly(1)) + ->method('getId') + ->willReturn($wishlistDataProvider['id']); + $this->resultRedirectMock->expects($this->once()) + ->method('setPath') + ->with('*', ['wishlist_id' => $wishlistDataProvider['id']]); + $this->requestMock->expects($this->once()) + ->method('getPostValue') + ->willReturn(null); + + $this->assertEquals($this->resultRedirectMock, $this->updateController->execute()); + } + + /** + * Check if wishlist not availbale, and exception is shown + * + * @return void + */ + public function testUpdateThrowsNotFoundExceptionWhenWishlistDoNotExist(): void + { + $this->formKeyValidatorMock->expects($this->once()) + ->method('validate') + ->willReturn(true); + $this->wishlistProviderMock->expects($this->once()) + ->method('getWishlist') + ->willReturn(null); + $this->expectException(NotFoundException::class); + $this->updateController->execute(); + } + + /** + * Dataprovider for Update test + * + * @return array + */ + public function getWishlistDataProvider(): array + { + return + [ + [ + [ + 'id' => self::STUB_ITEM_ID + ], + [ + 'qty' => [self::STUB_ITEM_ID => self::STUB_WISHLIST_PRODUCT_QTY], + 'description' => [self::STUB_ITEM_ID => 'Description for item_id 1'] + ] + ] + ]; + } +} diff --git a/app/code/Magento/Wishlist/etc/adminhtml/system.xml b/app/code/Magento/Wishlist/etc/adminhtml/system.xml index e61c07abca993..efadd6c4d58ba 100644 --- a/app/code/Magento/Wishlist/etc/adminhtml/system.xml +++ b/app/code/Magento/Wishlist/etc/adminhtml/system.xml @@ -13,6 +13,9 @@ <resource>Magento_Wishlist::config_wishlist</resource> <group id="email" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Share Options</label> + <depends> + <field id="*/general/active">1</field> + </depends> <field id="email_identity" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Email Sender</label> <source_model>Magento\Config\Model\Config\Source\Email\Identity</source_model> @@ -42,11 +45,17 @@ <field id="show_in_sidebar" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Show in Sidebar</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <depends> + <field id="active">1</field> + </depends> </field> </group> - <group id="wishlist_link" translate="label" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="0"> + <group id="wishlist_link" translate="label" sortOrder="3" showInDefault="1" showInWebsite="1"> <label>My Wish List Link</label> - <field id="use_qty" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0"> + <depends> + <field id="*/general/active">1</field> + </depends> + <field id="use_qty" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" canRestore="1"> <label>Display Wish List Summary</label> <source_model>Magento\Wishlist\Model\Config\Source\Summary</source_model> </field> diff --git a/app/code/Magento/Wishlist/etc/config.xml b/app/code/Magento/Wishlist/etc/config.xml index dd88e63bc90ad..780d6b904fedc 100644 --- a/app/code/Magento/Wishlist/etc/config.xml +++ b/app/code/Magento/Wishlist/etc/config.xml @@ -18,6 +18,9 @@ <number_limit>10</number_limit> <text_limit>255</text_limit> </email> + <wishlist_link> + <use_qty>0</use_qty> + </wishlist_link> </wishlist> <captcha translate="label"> <frontend> diff --git a/app/design/frontend/Magento/blank/web/css/source/_navigation.less b/app/design/frontend/Magento/blank/web/css/source/_navigation.less index 21b7315779764..fad906a089400 100644 --- a/app/design/frontend/Magento/blank/web/css/source/_navigation.less +++ b/app/design/frontend/Magento/blank/web/css/source/_navigation.less @@ -142,7 +142,7 @@ display: block; } } - } + } .header.links { .lib-list-reset-styles(); border-bottom: 1px solid @color-gray82; @@ -154,7 +154,7 @@ &.greet.welcome { border-top: 1px solid @color-gray82; font-weight: @font-weight__bold; - padding: .8rem @indent__base; + padding: .8rem @submenu__padding-left; } > a { @@ -168,7 +168,7 @@ .lib-css(text-decoration, @navigation-level0-item__text-decoration); display: block; font-weight: @font-weight__bold; - padding: .8rem @indent__base; + padding: .8rem @submenu__padding-left; } .header.links { diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Layer/QuickSearchByQuery.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Layer/QuickSearchByQuery.php new file mode 100644 index 0000000000000..506aba9f6bd84 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Layer/QuickSearchByQuery.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Model\Layer; + +use Magento\Catalog\Model\Layer\SearchFactory; +use Magento\Catalog\Model\ResourceModel\Product\Collection; + +/** + * Quick search products by query. + */ +class QuickSearchByQuery +{ + /** + * @var SearchFactory + */ + private $searchFactory; + + /** + * @param SearchFactory $searchFactory + */ + public function __construct( + SearchFactory $searchFactory + ) { + $this->searchFactory = $searchFactory; + } + + /** + * Flush search instances cache and find products by search query. + * + * @param string $query + * @param string $sortedField + * @param string $sortOrder + * @return Collection + */ + public function execute( + string $query, + string $sortedField = 'relevance', + string $sortOrder = 'desc' + ): Collection { + $productCollection = $this->searchFactory->create()->getProductCollection(); + $productCollection->addSearchFilter($query); + $productCollection->setOrder($sortedField, $sortOrder); + + return $productCollection; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/ConfigurableProduct/Model/DeleteConfigurableProduct.php b/dev/tests/integration/framework/Magento/TestFramework/ConfigurableProduct/Model/DeleteConfigurableProduct.php new file mode 100644 index 0000000000000..3414efa1e30ed --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/ConfigurableProduct/Model/DeleteConfigurableProduct.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\ConfigurableProduct\Model; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; + +/** + * Delete configurable product with linked products + */ +class DeleteConfigurableProduct +{ + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var ProductResource */ + private $productResource; + + /** @var Registry */ + private $registry; + + /** + * @param ProductRepositoryInterface $productRepository + * @param ProductResource $productResource + * @param Registry $registry + */ + public function __construct( + ProductRepositoryInterface $productRepository, + ProductResource $productResource, + Registry $registry + ) { + $this->productRepository = $productRepository; + $this->productResource = $productResource; + $this->registry = $registry; + } + + /** + * Delete configurable product and linked products + * + * @param string $sku + * @return void + */ + public function execute(string $sku): void + { + $configurableProduct = $this->productRepository->get($sku, false, null, true); + $childrenIds = $configurableProduct->getExtensionAttributes()->getConfigurableProductLinks(); + $childrenSkus = array_column($this->productResource->getProductsSku($childrenIds), 'sku'); + $childrenSkus[] = $sku; + $this->registry->unregister('isSecureArea'); + $this->registry->register('isSecureArea', true); + + foreach ($childrenSkus as $childSku) { + try { + $this->productRepository->deleteById($childSku); + } catch (NoSuchEntityException $e) { + //product already removed + } + } + + $this->registry->unregister('isSecureArea'); + $this->registry->register('isSecureArea', false); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/CheckProductPriceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/CheckProductPriceTest.php new file mode 100644 index 0000000000000..511b2afe2e0f4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/CheckProductPriceTest.php @@ -0,0 +1,310 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block\Product\ListProduct; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Block\Product\ListProduct; +use Magento\Customer\Model\Session; +use Magento\Framework\View\Element\Template; +use Magento\Framework\View\Result\PageFactory; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Check that product price render correctly on category page. + * + * @magentoDbIsolation enabled + * @magentoAppArea frontend + */ +class CheckProductPriceTest extends TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var PageFactory + */ + private $pageFactory; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var Session + */ + private $customerSession; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->pageFactory = $this->objectManager->get(PageFactory::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->customerSession = $this->objectManager->create(Session::class); + parent::setUp(); + } + + /** + * Assert that product price without additional price configurations will render as expected. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_tax_none.php + * + * @return void + */ + public function testCheckProductPriceWithoutAdditionalPriceConfigurations(): void + { + $priceHtml = $this->getProductPriceHtml('simple-product-tax-none'); + $this->assertFinalPrice($priceHtml, 205.00); + } + + /** + * Assert that product special price rendered correctly. + * + * @magentoDataFixture Magento/Catalog/_files/product_special_price.php + * + * @return void + */ + public function testCheckSpecialPrice(): void + { + $priceHtml = $this->getProductPriceHtml('simple'); + $this->assertFinalPrice($priceHtml, 5.99); + $this->assertRegularPrice($priceHtml, 10.00); + } + + /** + * Assert that product with fixed tier price is renders correctly. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_fixed_tier_price.php + * + * @return void + */ + public function testCheckFixedTierPrice(): void + { + $priceHtml = $this->getProductPriceHtml('simple-product-tax-none'); + $this->assertFinalPrice($priceHtml, 205.00); + $this->assertAsLowAsPrice($priceHtml, 40.00); + } + + /** + * Assert that price of product with percent tier price rendered correctly. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_percent_tier_price.php + * + * @return void + */ + public function testCheckPercentTierPrice(): void + { + $priceHtml = $this->getProductPriceHtml('simple-product-tax-none'); + $this->assertFinalPrice($priceHtml, 205.00); + $this->assertAsLowAsPrice($priceHtml, 102.50); + } + + /** + * Assert that price of product with fixed tier price for not logged user is renders correctly. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_not_logged_user.php + * + * @return void + */ + public function testCheckFixedTierPriceForNotLoggedUser(): void + { + $priceHtml = $this->getProductPriceHtml('simple-product-tax-none'); + $this->assertFinalPrice($priceHtml, 30.00); + $this->assertRegularPrice($priceHtml, 205.00); + } + + /** + * Assert that price of product with fixed tier price for logged user is renders correctly. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_logged_user.php + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDbIsolation disabled + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * + * @return void + */ + public function testCheckFixedTierPriceForLoggedUser(): void + { + $priceHtml = $this->getProductPriceHtml('simple-product-tax-none'); + $this->assertFinalPrice($priceHtml, 205.00); + $this->assertNotRegExp('/\$10/', $priceHtml); + $this->customerSession->setCustomerId(1); + try { + $priceHtml = $this->getProductPriceHtml('simple-product-tax-none'); + $this->assertFinalPrice($priceHtml, 10.00); + $this->assertRegularPrice($priceHtml, 205.00); + } finally { + $this->customerSession->setCustomerId(null); + } + } + + /** + * Assert that price of product with catalog rule with action equal to "Apply as percentage of original" + * is renders correctly. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_tax_none.php + * @magentoDataFixture Magento/CatalogRule/_files/rule_apply_as_percentage_of_original_not_logged_user.php + * @magentoDbIsolation disabled + * @magentoAppArea frontend + * + * @return void + */ + public function testCheckPriceRendersCorrectlyWithApplyAsPercentageOfOriginalRule(): void + { + $priceHtml = $this->getProductPriceHtml('simple-product-tax-none'); + $this->assertFinalPrice($priceHtml, 184.50); + $this->assertRegularPrice($priceHtml, 205.00); + } + + /** + * Assert that price of product with catalog rule with action equal to "Apply as fixed amount" + * is renders correctly. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_tax_none.php + * @magentoDataFixture Magento/CatalogRule/_files/rule_apply_as_fixed_amount_not_logged_user.php + * @magentoDbIsolation disabled + * @magentoAppArea frontend + * + * @return void + */ + public function testCheckPriceRendersCorrectlyWithApplyAsFixedAmountRule(): void + { + $priceHtml = $this->getProductPriceHtml('simple-product-tax-none'); + $this->assertFinalPrice($priceHtml, 195.00); + $this->assertRegularPrice($priceHtml, 205.00); + } + + /** + * Assert that price of product with catalog rule with action equal to "Adjust final price to this percentage" + * is renders correctly. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_tax_none.php + * @magentoDataFixture Magento/CatalogRule/_files/rule_adjust_final_price_to_this_percentage_not_logged_user.php + * @magentoDbIsolation disabled + * @magentoAppArea frontend + * + * @return void + */ + public function testCheckPriceRendersCorrectlyWithAdjustFinalPriceToThisPercentageRule(): void + { + $priceHtml = $this->getProductPriceHtml('simple-product-tax-none'); + $this->assertFinalPrice($priceHtml, 20.50); + $this->assertRegularPrice($priceHtml, 205.00); + } + + /** + * Assert that price of product with catalog rule with action equal to "Adjust final price to discount value" + * is renders correctly. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_tax_none.php + * @magentoDataFixture Magento/CatalogRule/_files/rule_adjust_final_price_to_discount_value_not_logged_user.php + * @magentoDbIsolation disabled + * @magentoAppArea frontend + * + * @return void + */ + public function testCheckPriceRendersCorrectlyWithAdjustFinalPriceToDiscountValueRule(): void + { + $priceHtml = $this->getProductPriceHtml('simple-product-tax-none'); + $this->assertFinalPrice($priceHtml, 10.00); + $this->assertRegularPrice($priceHtml, 205.00); + } + + /** + * Assert that price html contain "As low as" label and expected price amount. + * + * @param string $priceHtml + * @param float $expectedPrice + * @return void + */ + private function assertAsLowAsPrice(string $priceHtml, float $expectedPrice): void + { + $this->assertRegExp( + sprintf( + '/<span class="price-label">As low as<\/span> {1,}<span.*data-price-amount="%s".*>\$%01.2f<\/span>/', + round($expectedPrice, 2), + $expectedPrice + ), + $priceHtml + ); + } + + /** + * Assert that price html contain expected final price amount. + * + * @param string $priceHtml + * @param float $expectedPrice + * @return void + */ + private function assertFinalPrice(string $priceHtml, float $expectedPrice): void + { + $this->assertRegExp( + sprintf( + '/data-price-type="finalPrice".*<span class="price">\$%01.2f<\/span><\/span>/', + $expectedPrice + ), + $priceHtml + ); + } + + /** + * Assert that price html contain "Regular price" label and expected price amount. + * + * @param string $priceHtml + * @param float $expectedPrice + * @return void + */ + private function assertRegularPrice(string $priceHtml, float $expectedPrice): void + { + $regex = '<span class="price-label">Regular Price<\/span> {1,}<span.*data-price-amount="%s".*>\$%01.2f<\/span>'; + $this->assertRegExp( + sprintf("/{$regex}/", round($expectedPrice, 2), $expectedPrice), + $priceHtml + ); + } + + /** + * Return html of product price without new line characters. + * + * @param string $sku + * @return string + */ + private function getProductPriceHtml(string $sku): string + { + $product = $this->productRepository->get($sku, false, null, true); + + return preg_replace('/[\n\r]/', '', $this->getListProductBlock()->getProductPrice($product)); + } + + /** + * Get list product block from layout. + * + * @return ListProduct + */ + private function getListProductBlock(): ListProduct + { + $page = $this->pageFactory->create(); + $page->addHandle([ + 'default', + 'catalog_category_view', + ]); + $page->getLayout()->generateXml(); + /** @var Template $categoryProductsBlock */ + $categoryProductsBlock = $page->getLayout()->getBlock('category.products'); + + return $categoryProductsBlock->getChildBlock('product_list'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_tax_none_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_tax_none_rollback.php index ceffb1c87d970..79245d255a7e6 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_tax_none_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_tax_none_rollback.php @@ -5,9 +5,9 @@ */ declare(strict_types=1); -use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\ObjectManager; @@ -15,19 +15,16 @@ $objectManager = Bootstrap::getObjectManager(); /** @var ProductRepositoryInterface $productRepository */ $productRepository = $objectManager->get(ProductRepositoryInterface::class); -/** @var \Magento\Framework\Registry $registry */ -$registry =$objectManager->get(\Magento\Framework\Registry::class); - +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); $registry->unregister('isSecureArea'); $registry->register('isSecureArea', true); - try { - /** @var ProductInterface $product */ $product = $productRepository->get('simple-product-tax-none', false, null, true); $productRepository->delete($product); } catch (NoSuchEntityException $e) { // isolation on } - +$productRepository->cleanCache(); $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price.php new file mode 100644 index 0000000000000..31576f2baf55b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductTierPriceExtensionFactory; +use Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory; +use Magento\Customer\Model\Group; +use Magento\Store\Api\WebsiteRepositoryInterface; + +require __DIR__ . '/product_simple_tax_none.php'; + +$product = $productRepository->get('simple-product-tax-none'); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +/** @var ProductTierPriceInterfaceFactory $tierPriceFactory */ +$tierPriceFactory = $objectManager->get(ProductTierPriceInterfaceFactory::class); +/** @var ProductTierPriceExtensionFactory $tpExtensionAttributeFactory */ +$tpExtensionAttributeFactory = $objectManager->get(ProductTierPriceExtensionFactory::class); +$adminWebsite = $websiteRepository->get('admin'); +$tierPriceExtensionAttribute = $tpExtensionAttributeFactory->create( + [ + 'data' => [ + 'website_id' => $adminWebsite->getId(), + ] + ] +); +$tierPrices[] = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => Group::CUST_GROUP_ALL, + 'qty' => 2, + 'value' => 40 + ] + ] +)->setExtensionAttributes($tierPriceExtensionAttribute); +$product->setTierPrices($tierPrices); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_logged_user.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_logged_user.php new file mode 100644 index 0000000000000..44a4c82c277d4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_logged_user.php @@ -0,0 +1,39 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductTierPriceExtensionFactory; +use Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory; +use Magento\Store\Api\WebsiteRepositoryInterface; + +require __DIR__ . '/product_simple_tax_none.php'; + +$product = $productRepository->get('simple-product-tax-none'); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +/** @var ProductTierPriceInterfaceFactory $tierPriceFactory */ +$tierPriceFactory = $objectManager->get(ProductTierPriceInterfaceFactory::class); +/** @var ProductTierPriceExtensionFactory $tpExtensionAttributeFactory */ +$tpExtensionAttributeFactory = $objectManager->get(ProductTierPriceExtensionFactory::class); +$adminWebsite = $websiteRepository->get('admin'); +$tierPriceExtensionAttribute = $tpExtensionAttributeFactory->create( + [ + 'data' => [ + 'website_id' => $adminWebsite->getId(), + ] + ] +); +$tierPrices[] = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => 1, + 'qty' => 1, + 'value' => 10 + ] + ] +)->setExtensionAttributes($tierPriceExtensionAttribute); +$product->setTierPrices($tierPrices); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_logged_user_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_logged_user_rollback.php new file mode 100644 index 0000000000000..554f953580a5c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_logged_user_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/product_simple_tax_none_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_not_logged_user.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_not_logged_user.php new file mode 100644 index 0000000000000..68afbe529808e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_not_logged_user.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductTierPriceExtensionFactory; +use Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory; +use Magento\Customer\Model\Group; +use Magento\Store\Api\WebsiteRepositoryInterface; + +require __DIR__ . '/product_simple_tax_none.php'; + +$product = $productRepository->get('simple-product-tax-none'); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +/** @var ProductTierPriceInterfaceFactory $tierPriceFactory */ +$tierPriceFactory = $objectManager->get(ProductTierPriceInterfaceFactory::class); +/** @var ProductTierPriceExtensionFactory $tpExtensionAttributeFactory */ +$tpExtensionAttributeFactory = $objectManager->get(ProductTierPriceExtensionFactory::class); +$adminWebsite = $websiteRepository->get('admin'); +$tierPriceExtensionAttribute = $tpExtensionAttributeFactory->create( + [ + 'data' => [ + 'website_id' => $adminWebsite->getId(), + ] + ] +); +$tierPrices[] = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => Group::NOT_LOGGED_IN_ID, + 'qty' => 1, + 'value' => 30 + ] + ] +)->setExtensionAttributes($tierPriceExtensionAttribute); +$product->setTierPrices($tierPrices); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_not_logged_user_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_not_logged_user_rollback.php new file mode 100644 index 0000000000000..554f953580a5c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_for_not_logged_user_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/product_simple_tax_none_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_rollback.php new file mode 100644 index 0000000000000..554f953580a5c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_fixed_tier_price_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/product_simple_tax_none_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_percent_tier_price.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_percent_tier_price.php new file mode 100644 index 0000000000000..0bef251a254f3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_percent_tier_price.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductTierPriceExtensionFactory; +use Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory; +use Magento\Customer\Model\Group; +use Magento\Store\Api\WebsiteRepositoryInterface; + +require __DIR__ . '/product_simple_tax_none.php'; + +$product = $productRepository->get('simple-product-tax-none'); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +/** @var ProductTierPriceInterfaceFactory $tierPriceFactory */ +$tierPriceFactory = $objectManager->get(ProductTierPriceInterfaceFactory::class); +/** @var ProductTierPriceExtensionFactory $tpExtensionAttributeFactory */ +$tpExtensionAttributeFactory = $objectManager->get(ProductTierPriceExtensionFactory::class); +$adminWebsite = $websiteRepository->get('admin'); +$tierPriceExtensionAttribute = $tpExtensionAttributeFactory->create( + [ + 'data' => [ + 'website_id' => $adminWebsite->getId(), + 'percentage_value' => 50, + ] + ] +); +$tierPrices[] = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => Group::CUST_GROUP_ALL, + 'qty' => 2, + ] + ] +)->setExtensionAttributes($tierPriceExtensionAttribute); +$product->setTierPrices($tierPrices); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_percent_tier_price_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_percent_tier_price_rollback.php new file mode 100644 index 0000000000000..554f953580a5c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_percent_tier_price_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/product_simple_tax_none_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_adjust_final_price_to_discount_value_not_logged_user.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_adjust_final_price_to_discount_value_not_logged_user.php new file mode 100644 index 0000000000000..55824abe28ee9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_adjust_final_price_to_discount_value_not_logged_user.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Api\Data\RuleInterface; +use Magento\CatalogRule\Api\Data\RuleInterfaceFactory; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\Customer\Model\Group; +use Magento\Store\Model\WebsiteRepository; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepository $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepository::class); +$baseWebsite = $websiteRepository->get('base'); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); +/** @var CatalogRuleRepositoryInterface $catalogRuleRepository */ +$catalogRuleRepository = $objectManager->get(CatalogRuleRepositoryInterface::class); +/** @var RuleInterfaceFactory $catalogRuleFactory */ +$catalogRuleFactory = $objectManager->get(RuleInterfaceFactory::class); +$catalogRule = $catalogRuleFactory->create( + [ + 'data' => [ + RuleInterface::IS_ACTIVE => 1, + RuleInterface::NAME => 'Rule adjust final price to discount value. Not logged user.', + 'customer_group_ids' => Group::NOT_LOGGED_IN_ID, + RuleInterface::DISCOUNT_AMOUNT => 10, + 'website_ids' => [$baseWebsite->getId()], + RuleInterface::SIMPLE_ACTION => 'to_fixed', + RuleInterface::STOP_RULES_PROCESSING => false, + RuleInterface::SORT_ORDER => 0, + 'sub_is_enable' => 0, + 'sub_discount_amount' => 0, + ] + ] +); +$catalogRuleRepository->save($catalogRule); +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_adjust_final_price_to_discount_value_not_logged_user_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_adjust_final_price_to_discount_value_not_logged_user_rollback.php new file mode 100644 index 0000000000000..8b77787d40f14 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_adjust_final_price_to_discount_value_not_logged_user_rollback.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory; +use Magento\CatalogRule\Model\Rule; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); +/** @var CatalogRuleRepositoryInterface $ruleRepository */ +$ruleRepository = $objectManager->create(CatalogRuleRepositoryInterface::class); +/** @var CollectionFactory $ruleCollectionFactory */ +$ruleCollectionFactory = $objectManager->get(CollectionFactory::class); +$ruleCollection = $ruleCollectionFactory->create(); +$ruleCollection->addFieldToFilter('name', ['eq' => 'Rule adjust final price to discount value. Not logged user.']); +$ruleCollection->setPageSize(1); +/** @var Rule $rule */ +$rule = $ruleCollection->getFirstItem(); +if ($rule->getId()) { + $ruleRepository->delete($rule); +} +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_adjust_final_price_to_this_percentage_not_logged_user.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_adjust_final_price_to_this_percentage_not_logged_user.php new file mode 100644 index 0000000000000..233e231d7cac4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_adjust_final_price_to_this_percentage_not_logged_user.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Api\Data\RuleInterface; +use Magento\CatalogRule\Api\Data\RuleInterfaceFactory; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\Customer\Model\Group; +use Magento\Store\Model\WebsiteRepository; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepository $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepository::class); +$baseWebsite = $websiteRepository->get('base'); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); +/** @var CatalogRuleRepositoryInterface $catalogRuleRepository */ +$catalogRuleRepository = $objectManager->get(CatalogRuleRepositoryInterface::class); +/** @var RuleInterfaceFactory $catalogRuleFactory */ +$catalogRuleFactory = $objectManager->get(RuleInterfaceFactory::class); +$catalogRule = $catalogRuleFactory->create( + [ + 'data' => [ + RuleInterface::IS_ACTIVE => 1, + RuleInterface::NAME => 'Rule adjust final price to this percentage. Not logged user.', + 'customer_group_ids' => Group::NOT_LOGGED_IN_ID, + RuleInterface::DISCOUNT_AMOUNT => 10, + 'website_ids' => [$baseWebsite->getId()], + RuleInterface::SIMPLE_ACTION => 'to_percent', + RuleInterface::STOP_RULES_PROCESSING => false, + RuleInterface::SORT_ORDER => 0, + 'sub_is_enable' => 0, + 'sub_discount_amount' => 0, + ] + ] +); +$catalogRuleRepository->save($catalogRule); +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_adjust_final_price_to_this_percentage_not_logged_user_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_adjust_final_price_to_this_percentage_not_logged_user_rollback.php new file mode 100644 index 0000000000000..5b1b6501019b8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_adjust_final_price_to_this_percentage_not_logged_user_rollback.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory; +use Magento\CatalogRule\Model\Rule; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); +/** @var CatalogRuleRepositoryInterface $ruleRepository */ +$ruleRepository = $objectManager->create(CatalogRuleRepositoryInterface::class); +/** @var CollectionFactory $ruleCollectionFactory */ +$ruleCollectionFactory = $objectManager->get(CollectionFactory::class); +$ruleCollection = $ruleCollectionFactory->create(); +$ruleCollection->addFieldToFilter('name', ['eq' => 'Rule adjust final price to this percentage. Not logged user.']); +$ruleCollection->setPageSize(1); +/** @var Rule $rule */ +$rule = $ruleCollection->getFirstItem(); +if ($rule->getId()) { + $ruleRepository->delete($rule); +} +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_apply_as_fixed_amount_not_logged_user.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_apply_as_fixed_amount_not_logged_user.php new file mode 100644 index 0000000000000..c37e74de0054c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_apply_as_fixed_amount_not_logged_user.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Api\Data\RuleInterface; +use Magento\CatalogRule\Api\Data\RuleInterfaceFactory; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\Customer\Model\Group; +use Magento\Store\Model\WebsiteRepository; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepository $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepository::class); +$baseWebsite = $websiteRepository->get('base'); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); +/** @var CatalogRuleRepositoryInterface $catalogRuleRepository */ +$catalogRuleRepository = $objectManager->get(CatalogRuleRepositoryInterface::class); +/** @var RuleInterfaceFactory $catalogRuleFactory */ +$catalogRuleFactory = $objectManager->get(RuleInterfaceFactory::class); +$catalogRule = $catalogRuleFactory->create( + [ + 'data' => [ + RuleInterface::IS_ACTIVE => 1, + RuleInterface::NAME => 'Rule apply as fixed amount. Not logged user.', + 'customer_group_ids' => Group::NOT_LOGGED_IN_ID, + RuleInterface::DISCOUNT_AMOUNT => 10, + 'website_ids' => [$baseWebsite->getId()], + RuleInterface::SIMPLE_ACTION => 'by_fixed', + RuleInterface::STOP_RULES_PROCESSING => false, + RuleInterface::SORT_ORDER => 0, + 'sub_is_enable' => 0, + 'sub_discount_amount' => 0, + ] + ] +); +$catalogRuleRepository->save($catalogRule); +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_apply_as_fixed_amount_not_logged_user_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_apply_as_fixed_amount_not_logged_user_rollback.php new file mode 100644 index 0000000000000..33b72dac08924 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_apply_as_fixed_amount_not_logged_user_rollback.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory; +use Magento\CatalogRule\Model\Rule; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); +/** @var CatalogRuleRepositoryInterface $ruleRepository */ +$ruleRepository = $objectManager->create(CatalogRuleRepositoryInterface::class); +/** @var CollectionFactory $ruleCollectionFactory */ +$ruleCollectionFactory = $objectManager->get(CollectionFactory::class); +$ruleCollection = $ruleCollectionFactory->create(); +$ruleCollection->addFieldToFilter('name', ['eq' => 'Rule apply as fixed amount. Not logged user.']); +$ruleCollection->setPageSize(1); +/** @var Rule $rule */ +$rule = $ruleCollection->getFirstItem(); +if ($rule->getId()) { + $ruleRepository->delete($rule); +} +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_apply_as_percentage_of_original_not_logged_user.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_apply_as_percentage_of_original_not_logged_user.php new file mode 100644 index 0000000000000..633a265241e33 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_apply_as_percentage_of_original_not_logged_user.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Api\Data\RuleInterface; +use Magento\CatalogRule\Api\Data\RuleInterfaceFactory; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\Customer\Model\Group; +use Magento\Store\Model\WebsiteRepository; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepository $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepository::class); +$baseWebsite = $websiteRepository->get('base'); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); +/** @var CatalogRuleRepositoryInterface $catalogRuleRepository */ +$catalogRuleRepository = $objectManager->get(CatalogRuleRepositoryInterface::class); +/** @var RuleInterfaceFactory $catalogRuleFactory */ +$catalogRuleFactory = $objectManager->get(RuleInterfaceFactory::class); +$catalogRule = $catalogRuleFactory->create( + [ + 'data' => [ + RuleInterface::IS_ACTIVE => 1, + RuleInterface::NAME => 'Rule apply as percentage of original. Not logged user.', + 'customer_group_ids' => Group::NOT_LOGGED_IN_ID, + RuleInterface::DISCOUNT_AMOUNT => 10, + 'website_ids' => [$baseWebsite->getId()], + RuleInterface::SIMPLE_ACTION => 'by_percent', + RuleInterface::STOP_RULES_PROCESSING => false, + RuleInterface::SORT_ORDER => 0, + 'sub_is_enable' => 0, + 'sub_discount_amount' => 0, + ] + ] +); +$catalogRuleRepository->save($catalogRule); +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_apply_as_percentage_of_original_not_logged_user_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_apply_as_percentage_of_original_not_logged_user_rollback.php new file mode 100644 index 0000000000000..d9a1e61b7022e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_apply_as_percentage_of_original_not_logged_user_rollback.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\CatalogRule\Api\CatalogRuleRepositoryInterface; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory; +use Magento\CatalogRule\Model\Rule; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(IndexBuilder::class); +/** @var CatalogRuleRepositoryInterface $ruleRepository */ +$ruleRepository = $objectManager->create(CatalogRuleRepositoryInterface::class); +/** @var CollectionFactory $ruleCollectionFactory */ +$ruleCollectionFactory = $objectManager->get(CollectionFactory::class); +$ruleCollection = $ruleCollectionFactory->create(); +$ruleCollection->addFieldToFilter('name', ['eq' => 'Rule apply as percentage of original. Not logged user.']); +$ruleCollection->setPageSize(1); +/** @var Rule $rule */ +$rule = $ruleCollection->getFirstItem(); +if ($rule->getId()) { + $ruleRepository->delete($rule); +} +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/AttributeSearchWeightTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/AttributeSearchWeightTest.php index 4ca8e0b0726d4..bfde20950d105 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/AttributeSearchWeightTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/AttributeSearchWeightTest.php @@ -3,18 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - declare(strict_types=1); namespace Magento\CatalogSearch\Model\Search; use Magento\Catalog\Api\ProductAttributeRepositoryInterface; -use Magento\Catalog\Model\Layer\Search as CatalogLayerSearch; -use Magento\Catalog\Model\Product; -use Magento\CatalogSearch\Model\ResourceModel\Fulltext\CollectionFactory; -use Magento\Framework\Search\Request\Builder; -use Magento\Framework\Search\Request\Config as RequestConfig; -use Magento\Search\Model\Search; +use Magento\TestFramework\Catalog\Model\Layer\QuickSearchByQuery; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\ObjectManager; use PHPUnit\Framework\TestCase; @@ -42,9 +36,9 @@ class AttributeSearchWeightTest extends TestCase private $collectedAttributesWeight = []; /** - * @var CatalogLayerSearch + * @var QuickSearchByQuery */ - private $catalogLayerSearch; + private $quickSearchByQuery; /** * @inheritdoc @@ -53,7 +47,7 @@ protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); $this->productAttributeRepository = $this->objectManager->get(ProductAttributeRepositoryInterface::class); - $this->catalogLayerSearch = $this->objectManager->get(CatalogLayerSearch::class); + $this->quickSearchByQuery = $this->objectManager->get(QuickSearchByQuery::class); $this->collectCurrentProductAttributesWeights(); } @@ -85,9 +79,7 @@ public function testAttributeSearchWeight( array $expectedProductNames ): void { $this->updateAttributesWeight($attributeWeights); - $this->removeInstancesCache(); - $products = $this->findProducts($searchQuery); - $actualProductNames = $this->collectProductsName($products); + $actualProductNames = $this->quickSearchByQuery->execute($searchQuery)->getColumnValues('name'); $this->assertEquals($expectedProductNames, $actualProductNames, 'Products order is not as expected.'); } @@ -164,58 +156,11 @@ protected function updateAttributesWeight(array $attributeWeights): void { foreach ($attributeWeights as $attributeCode => $weight) { $attribute = $this->productAttributeRepository->get($attributeCode); - - if ($attribute) { - $attribute->setSearchWeight($weight); - $this->productAttributeRepository->save($attribute); - } + $attribute->setSearchWeight($weight); + $this->productAttributeRepository->save($attribute); } } - /** - * Get all names from founded products. - * - * @param Product[] $products - * @return array - */ - protected function collectProductsName(array $products): array - { - $result = []; - foreach ($products as $product) { - $result[] = $product->getName(); - } - - return $result; - } - - /** - * Reindex catalogsearch fulltext index. - * - * @return void - */ - protected function removeInstancesCache(): void - { - $this->objectManager->removeSharedInstance(RequestConfig::class); - $this->objectManager->removeSharedInstance(Builder::class); - $this->objectManager->removeSharedInstance(Search::class); - $this->objectManager->removeSharedInstance(CatalogLayerSearch::class); - } - - /** - * Find products by search query. - * - * @param string $query - * @return Product[] - */ - protected function findProducts(string $query): array - { - $testProductCollection = $this->catalogLayerSearch->getProductCollection(); - $testProductCollection->addSearchFilter($query); - $testProductCollection->setOrder('relevance', 'desc'); - - return $testProductCollection->getItems(); - } - /** * Collect weight of attributes which use in test. * diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFilesTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFilesTest.php index 1fc07d32c77b9..c135a89a00bc7 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFilesTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFilesTest.php @@ -75,22 +75,47 @@ protected function setUp() * Execute method with correct directory path and file name to check that files under WYSIWYG media directory * can be removed. * + * @param string $filename * @return void + * @dataProvider executeDataProvider */ - public function testExecute() + public function testExecute(string $filename) { + $filePath = $this->fullDirectoryPath . DIRECTORY_SEPARATOR . $filename; + $fixtureDir = realpath(__DIR__ . '/../../../../../Catalog/_files'); + copy($fixtureDir . '/' . $this->fileName, $filePath); + $this->model->getRequest()->setMethod('POST') - ->setPostValue('files', [$this->imagesHelper->idEncode($this->fileName)]); + ->setPostValue('files', [$this->imagesHelper->idEncode($filename)]); $this->model->getStorage()->getSession()->setCurrentPath($this->fullDirectoryPath); $this->model->execute(); $this->assertFalse( $this->mediaDirectory->isExist( - $this->mediaDirectory->getRelativePath($this->fullDirectoryPath . '/' . $this->fileName) + $this->mediaDirectory->getRelativePath($this->fullDirectoryPath . '/' . $filename) ) ); } + /** + * DataProvider for testExecute + * + * @return array + */ + public function executeDataProvider(): array + { + return [ + ['name with spaces.jpg'], + ['name with, comma.jpg'], + ['name with* asterisk.jpg'], + ['name with[ bracket.jpg'], + ['magento_small_image.jpg'], + ['_.jpg'], + [' - .jpg'], + ['-.jpg'], + ]; + } + /** * Check that htaccess file couldn't be removed via * \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images\DeleteFiles::execute method diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/StorageTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/StorageTest.php index 5d256f2234a53..c77646ca5be1c 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/StorageTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/StorageTest.php @@ -9,9 +9,12 @@ use Magento\Framework\App\Filesystem\DirectoryList; /** + * Test methods of class Storage * * @SuppressWarnings(PHPMD.LongVariable) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.TooManyPublicMethods) */ class StorageTest extends \PHPUnit\Framework\TestCase { @@ -260,4 +263,87 @@ public function testUploadFileWithWrongFile(): void $this->assertFalse(is_file(self::$_baseDir . DIRECTORY_SEPARATOR . $fileName)); // phpcs:enable } + + /** + * Test that getThumbnailUrl() returns correct URL for root folder or sub-folders images + * + * @param string $directory + * @param string $filename + * @param string $expectedUrl + * @return void + * @magentoAppIsolation enabled + * @magentoAppArea adminhtml + * @dataProvider getThumbnailUrlDataProvider + */ + public function testGetThumbnailUrl(string $directory, string $filename, string $expectedUrl): void + { + $root = $this->storage->getCmsWysiwygImages()->getStorageRoot(); + $directory = implode('/', array_filter([rtrim($root, '/'), trim($directory, '/')])); + $path = $directory . '/' . $filename; + $this->generateImage($path); + $this->storage->resizeFile($path); + $collection = $this->storage->getFilesCollection($directory, 'image'); + $paths = []; + foreach ($collection as $item) { + $paths[] = parse_url($item->getThumbUrl(), PHP_URL_PATH); + } + $this->assertEquals([$expectedUrl], $paths); + $this->storage->deleteFile($path); + } + + /** + * Provide scenarios for testing getThumbnailUrl() + * + * @return array + */ + public function getThumbnailUrlDataProvider(): array + { + return [ + [ + '/', + 'image1.png', + '/pub/media/.thumbs/image1.png' + ], + [ + '/cms', + 'image2.png', + '/pub/media/.thumbscms/image2.png' + ], + [ + '/cms/pages', + 'image3.png', + '/pub/media/.thumbscms/pages/image3.png' + ] + ]; + } + + /** + * Generate a dummy image of the given width and height. + * + * @param string $path + * @param int $width + * @param int $height + * @return string + */ + private function generateImage(string $path, int $width = 1024, int $height = 768) + { + $dir = dirname($path); + if (!file_exists($dir)) { + mkdir($dir, 0777, true); + } + $file = fopen($path, 'wb'); + $filename = basename($path); + ob_start(); + $image = imagecreatetruecolor($width, $height); + switch (substr($filename, strrpos($filename, '.'))) { + case '.jpeg': + imagejpeg($image); + break; + case '.png': + imagepng($image); + break; + } + fwrite($file, ob_get_clean()); + return $path; + } } diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableTest.php index feca63015ca7c..75f5b9928b881 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableTest.php @@ -3,79 +3,102 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\ConfigurableProduct\Block\Product\View\Type; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + /** - * Test class for \Magento\ConfigurableProduct\Block\Product\View\Type\Configurable. + * Test class to check configurable product view behaviour + * + * @see \Magento\ConfigurableProduct\Block\Product\View\Type\Configurable * * @magentoAppIsolation enabled * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php */ -class ConfigurableTest extends \PHPUnit\Framework\TestCase +class ConfigurableTest extends TestCase { - /** - * @var \Magento\ConfigurableProduct\Block\Product\View\Type\Configurable - */ - protected $_block; + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Configurable */ + private $block; + + /** @var Product */ + private $product; + + /** @var LayoutInterface */ + private $layout; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var SerializerInterface */ + private $json; + + /** @var ProductResource */ + private $productResource; /** - * @var \Magento\Catalog\Model\Product + * @inheritdoc */ - protected $_product; - protected function setUp() { - $this->_product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\Product::class - ); - $this->_product->load(1); - $this->_block = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Framework\View\LayoutInterface::class - )->createBlock( - \Magento\ConfigurableProduct\Block\Product\View\Type\Configurable::class - ); - $this->_block->setProduct($this->_product); + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->product = $this->productRepository->get('configurable'); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->block = $this->layout->createBlock(Configurable::class); + $this->json = $this->objectManager->get(SerializerInterface::class); + $this->block->setProduct($this->product); + $this->productResource = $this->objectManager->create(ProductResource::class); } /** - * @magentoAppIsolation enabled + * @return void */ - public function testGetAllowAttributes() + public function testGetAllowAttributes(): void { - $attributes = $this->_block->getAllowAttributes(); - $this->assertInstanceOf( - \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection::class, - $attributes - ); + $attributes = $this->block->getAllowAttributes(); + $this->assertInstanceOf(Collection::class, $attributes); $this->assertGreaterThanOrEqual(1, $attributes->getSize()); } /** - * @magentoAppIsolation enabled + * @return void */ - public function testHasOptions() + public function testHasOptions(): void { - $this->assertTrue($this->_block->hasOptions()); + $this->assertTrue($this->block->hasOptions()); } /** - * @magentoAppIsolation enabled + * @return void */ - public function testGetAllowProducts() + public function testGetAllowProducts(): void { - $products = $this->_block->getAllowProducts(); + $products = $this->block->getAllowProducts(); $this->assertGreaterThanOrEqual(2, count($products)); foreach ($products as $product) { - $this->assertInstanceOf(\Magento\Catalog\Model\Product::class, $product); + $this->assertInstanceOf(Product::class, $product); } } /** - * @magentoAppIsolation enabled + * @return void */ - public function testGetJsonConfig() + public function testGetJsonConfig(): void { - $config = json_decode($this->_block->getJsonConfig(), true); + $config = $this->json->unserialize($this->block->getJsonConfig()); $this->assertNotEmpty($config); $this->assertArrayHasKey('productId', $config); $this->assertEquals(1, $config['productId']); @@ -84,4 +107,72 @@ public function testGetJsonConfig() $this->assertArrayHasKey('prices', $config); $this->assertArrayHasKey('basePrice', $config['prices']); } + + /** + * @dataProvider expectedDataProvider + * + * @param string $label + * @param array $expectedConfig + * @return void + */ + public function testConfigurableProductView(string $label, array $expectedConfig): void + { + $attributes = $this->block->decorateArray($this->block->getAllowAttributes()); + $this->assertCount(1, $attributes); + $attribute = $attributes->getFirstItem(); + $this->assertEquals($label, $attribute->getLabel()); + $config = $this->json->unserialize($this->block->getJsonConfig())['attributes'] ?? null; + $this->assertNotNull($config); + $this->assertConfig(reset($config), $expectedConfig); + } + + /** + * @return array + */ + public function expectedDataProvider(): array + { + return [ + [ + 'label' => 'Test Configurable', + 'config_data' => [ + 'label' => 'Test Configurable', + 'options' => [ + [ + 'label' => 'Option 1', + 'sku' => 'simple_10', + ], + [ + 'label' => 'Option 2', + 'sku' => 'simple_20', + ], + ], + ], + ], + ]; + } + + /** + * Assert that data was generated + * + * @param array $data + * @param array $expectedData + * @return void + */ + private function assertConfig($data, $expectedData): void + { + $this->assertEquals($expectedData['label'], $data['label']); + $skus = array_column($expectedData['options'], 'sku'); + $idBySkuMap = $this->productResource->getProductsIdsBySkus($skus); + foreach ($expectedData['options'] as &$option) { + $sku = $option['sku']; + unset($option['sku']); + $option['products'] = [$idBySkuMap[$sku]]; + foreach ($data['options'] as $actualOption) { + if ($option['label'] === $actualOption['label']) { + unset($actualOption['id']); + $this->assertEquals($option, $actualOption); + } + } + } + } } diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableViewOnCategoryPageTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableViewOnCategoryPageTest.php new file mode 100644 index 0000000000000..94aa958b44c26 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableViewOnCategoryPageTest.php @@ -0,0 +1,83 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Block\Product\View\Type; + +use Magento\Catalog\Block\Product\ListProduct; +use Magento\Eav\Model\Entity\Collection\AbstractCollection; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Class checks configurable product displaying on category view page + * + * @magentoDbIsolation disabled + * @magentoAppIsolation enabled + * @magentoAppArea frontend + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_out_of_stock_children.php + */ +class ConfigurableViewOnCategoryPageTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var LayoutInterface */ + private $layout; + + /** @var ListProduct $listingBlock */ + private $listingBlock; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->listingBlock = $this->layout->createBlock(ListProduct::class); + $this->listingBlock->setCategoryId(333); + } + + /** + * @magentoConfigFixture current_store cataloginventory/options/show_out_of_stock 1 + * + * @return void + */ + public function testOutOfStockProductWithEnabledConfigView(): void + { + $collection = $this->listingBlock->getLoadedProductCollection(); + $this->assertCollectionSize(1, $collection); + } + + /** + * @magentoConfigFixture current_store cataloginventory/options/show_out_of_stock 0 + * + * @return void + */ + public function testOutOfStockProductWithDisabledConfigView(): void + { + $collection = $this->listingBlock->getLoadedProductCollection(); + $this->assertCollectionSize(0, $collection); + } + + /** + * Check collection size + * + * @param int $expectedSize + * @param AbstractCollection $collection + * @return void + */ + private function assertCollectionSize(int $expectedSize, AbstractCollection $collection): void + { + $this->assertEquals($expectedSize, $collection->getSize()); + $this->assertCount($expectedSize, $collection->getItems()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableViewOnProductPageTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableViewOnProductPageTest.php new file mode 100644 index 0000000000000..21ba9d2764b91 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableViewOnProductPageTest.php @@ -0,0 +1,290 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Block\Product\View\Type; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\CatalogInventory\Api\Data\StockStatusInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Class checks configurable product view with out of stock children + * + * @magentoAppArea frontend + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ConfigurableViewOnProductPageTest extends TestCase +{ + private const STOCK_DISPLAY_TEMPLATE = 'Magento_Catalog::product/view/type/default.phtml'; + + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var LayoutInterface */ + private $layout; + + /** @var Configurable */ + private $block; + + /** @var SerializerInterface */ + private $json; + + /** @var ProductResource */ + private $productResource; + + /** @var StoreManagerInterface */ + private $storeManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->block = $this->layout->createBlock(Configurable::class); + $this->json = $this->objectManager->get(SerializerInterface::class); + $this->productResource = $this->objectManager->get(ProductResource::class); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + } + + /** + * @dataProvider oneChildNotVisibleDataProvider + * @magentoDbIsolation disabled + * + * @param string $sku + * @param array $data + * @param array $expectedData + * @return void + */ + public function testOneChildNotVisible(string $sku, array $data, array $expectedData): void + { + $configurableProduct = $this->prepareConfigurableProduct($sku, $data); + $result = $this->renderStockBlock($configurableProduct); + $this->performAsserts($result, $expectedData); + } + + /** + * @return array + */ + public function oneChildNotVisibleDataProvider(): array + { + return [ + 'one_child_out_of_stock' => [ + 'sku' => 'simple_10', + 'data' => [ + 'stock_data' => [ + 'use_config_manage_stock' => 1, + 'is_in_stock' => StockStatusInterface::STATUS_OUT_OF_STOCK, + ], + ], + 'expected_data' => [ + 'stock_status' => 'In stock', + 'options' => [ + [ + 'label' => 'Option 2', + 'product' => 'simple_20', + ], + ], + ], + ], + 'one_child_disabled' => [ + 'sku' => 'simple_10', + 'data' => [ + 'status' => Status::STATUS_DISABLED, + 'stock_data' => [ + 'use_config_manage_stock' => 1, + 'is_in_stock' => StockStatusInterface::STATUS_IN_STOCK, + ], + ], + 'expected_data' => [ + 'stock_status' => 'In stock', + 'options' => [ + [ + 'label' => 'Option 2', + 'product' => 'simple_20', + ], + ], + ], + ], + ]; + } + + /** + * @magentoConfigFixture current_store cataloginventory/options/show_out_of_stock 1 + * + * @dataProvider oneChildNotVisibleDataProviderWithEnabledConfig + * + * @param string $sku + * @param array $data + * @param array $expectedData + * @return void + */ + public function testOneChildNotVisibleWithEnabledShowOutOfStockProducts( + string $sku, + array $data, + array $expectedData + ): void { + $configurableProduct = $this->prepareConfigurableProduct($sku, $data); + $result = $this->renderStockBlock($configurableProduct); + $this->performAsserts($result, $expectedData); + } + + /** + * @return array + */ + public function oneChildNotVisibleDataProviderWithEnabledConfig(): array + { + return [ + 'one_child_out_of_stock' => [ + 'sku' => 'simple_10', + 'data' => [ + 'stock_data' => [ + 'use_config_manage_stock' => 1, + 'is_in_stock' => StockStatusInterface::STATUS_OUT_OF_STOCK, + ], + ], + 'expected_data' => [ + 'stock_status' => 'In stock', + 'options' => [ + [ + 'label' => 'Option 2', + 'product' => 'simple_20' + ], + [ + 'label' => 'Option 1', + 'product' => 'simple_10', + ], + ], + ], + ], + 'one_child_disabled' => [ + 'sku' => 'simple_10', + 'data' => [ + 'status' => Status::STATUS_DISABLED, + 'stock_data' => [ + 'use_config_manage_stock' => 1, + 'is_in_stock' => StockStatusInterface::STATUS_IN_STOCK, + ], + ], + 'expected_data' => [ + 'stock_status' => 'In stock', + 'options' => [ + [ + 'label' => 'Option 2', + 'product' => 'simple_20', + ], + ], + ], + ], + ]; + } + + /** + * Update product with data + * + * @param array $sku + * @param array $data + * @return void + */ + private function updateProduct(string $sku, array $data): void + { + $currentStore = $this->storeManager->getStore(); + try { + $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); + $product = $this->productRepository->get($sku); + $product->addData($data); + $this->productRepository->save($product); + } finally { + $this->storeManager->setCurrentStore($currentStore); + } + } + + /** + * Check attribute options + * + * @param array $actualData + * @param array $expectedData + * @return void + */ + private function assertConfig(array $actualData, array $expectedData): void + { + $this->assertCount(count($expectedData), $actualData['options_data'], 'Redundant options were loaded'); + $sku = array_column($expectedData, 'product'); + $idBySkuMapping = $this->productResource->getProductsIdsBySkus($sku); + foreach ($expectedData as $expectedOption) { + $expectedId = $idBySkuMapping[$expectedOption['product']]; + $itemToCheck = $actualData['options_data'][$expectedId] ?? null; + $this->assertNotNull($itemToCheck); + foreach ($actualData['attributes']['options'] as $actualAttributeDataItem) { + if ($actualAttributeDataItem['id'] === reset($itemToCheck)) { + $this->assertEquals($expectedOption['label'], $actualAttributeDataItem['label']); + } + } + } + } + + /** + * Render stock block + * + * @param ProductInterface $configurableProduct + * @return string + */ + private function renderStockBlock(ProductInterface $configurableProduct): string + { + $this->block->setProduct($configurableProduct); + $this->block->setTemplate(self::STOCK_DISPLAY_TEMPLATE); + + return $this->block->toHtml(); + } + + /** + * Perform test asserts + * + * @param string $result + * @param array $expectedData + * @return void + */ + private function performAsserts(string $result, array $expectedData): void + { + $this->assertEquals((string)__($expectedData['stock_status']), trim(strip_tags($result))); + $config = $this->json->unserialize($this->block->getJsonConfig()); + $dataToCheck = ['attributes' => reset($config['attributes']), 'options_data' => $config['index']]; + $this->assertConfig($dataToCheck, $expectedData['options']); + } + + /** + * Prepare configurable product with children to test + * + * @param string $sku + * @param array $data + * @return ProductInterface + */ + private function prepareConfigurableProduct(string $sku, array $data): ProductInterface + { + $this->updateProduct($sku, $data); + + return $this->productRepository->get('configurable', false, null, true); + } +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/RenderConfigurableOptionsTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/RenderConfigurableOptionsTest.php new file mode 100644 index 0000000000000..6a8dea32be620 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/RenderConfigurableOptionsTest.php @@ -0,0 +1,145 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Block\Product\View\Type; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Block\Product\View; +use Magento\Catalog\Model\Product\Visibility; +use Magento\ConfigurableProduct\Helper\Data; +use Magento\ConfigurableProduct\Model\ConfigurableAttributeData; +use Magento\Framework\Registry; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\View\Result\Page; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Test cases related to render configurable options. + * + * @magentoAppArea frontend + * @magentoDbIsolation enabled + */ +class RenderConfigurableOptionsTest extends TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var Data + */ + private $configurableHelper; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var ConfigurableAttributeData + */ + private $configurableAttributeData; + + /** + * @var Registry + */ + private $registry; + + /** + * @var Page + */ + private $page; + + /** + * @var Json + */ + private $json; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->configurableHelper = $this->objectManager->get(Data::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->configurableAttributeData = $this->objectManager->get(ConfigurableAttributeData::class); + $this->registry = $this->objectManager->get(Registry::class); + $this->page = $this->objectManager->create(Page::class); + $this->json = $this->objectManager->get(Json::class); + parent::setUp(); + } + + /** + * Assert that all configurable options was rendered correctly if one of + * child product is visible on catalog\search. + * + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products.php + * + * @return void + */ + public function testRenderConfigurableOptionsBlockWithOneVisibleOption(): void + { + $configurableProduct = $this->productRepository->get('Configurable product'); + $childProduct = $this->productRepository->get('Simple option 1'); + $childProduct->setVisibility(Visibility::VISIBILITY_BOTH); + $this->productRepository->save($childProduct); + $allProducts = $configurableProduct->getTypeInstance()->getUsedProducts($configurableProduct, null); + $options = $this->configurableHelper->getOptions($configurableProduct, $allProducts); + $confAttrData = $this->configurableAttributeData->getAttributesData($configurableProduct, $options); + $attributesJson = str_replace( + ['[', ']'], + ['\[', '\]'], + $this->json->serialize($confAttrData['attributes']) + ); + $optionsHtml = $this->getConfigurableOptionsHtml('Configurable product'); + $this->assertRegExp("/\"spConfig\": {\"attributes\":{$attributesJson}/", $optionsHtml); + } + + /** + * Render configurable options block. + * + * @param string $configurableSku + * @return string + */ + private function getConfigurableOptionsHtml(string $configurableSku): string + { + $product = $this->productRepository->get($configurableSku); + $this->registry->unregister('product'); + $this->registry->register('product', $product); + $optionsBlock = $this->getOptionsWrapperBlockWithOnlyConfigurableBlock(); + $optionHtml = $optionsBlock->toHtml(); + $this->registry->unregister('product'); + + return $optionHtml; + } + + /** + * Get options wrapper without extra blocks(only configurable child block). + * + * @return View + */ + private function getOptionsWrapperBlockWithOnlyConfigurableBlock(): View + { + $this->page->addHandle([ + 'default', + 'catalog_product_view', + 'catalog_product_view_type_configurable', + ]); + $this->page->getLayout()->generateXml(); + + /** @var View $productInfoOptionsWrapper */ + $productInfoOptionsWrapper = $this->page->getLayout()->getBlock('product.info.options.wrapper'); + $productInfoOptionsWrapper->unsetChild('product_options'); + $productInfoOptionsWrapper->unsetChild('html_calendar'); + + return $productInfoOptionsWrapper; + } +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/HelperTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/HelperTest.php new file mode 100644 index 0000000000000..ba684f37175e4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/HelperTest.php @@ -0,0 +1,369 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Controller\Adminhtml\Product\Initialization; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper; +use Magento\Catalog\Model\Product\Media\Config; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\WriteInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Store\Model\Store; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Tests for image processing plugins for child products by saving a configurable product. + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class HelperTest extends TestCase +{ + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var Helper + */ + private $helper; + + /** + * @var RequestInterface + */ + private $request; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var ProductResource + */ + private $productResource; + + /** + * @var ProductAttributeRepositoryInterface + */ + private $productAttributeRepository; + + /** + * @var SerializerInterface + */ + private $jsonSerializer; + + /** + * @var SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + + /** + * @var Config + */ + private $config; + + /** + * @var WriteInterface + */ + private $mediaDirectory; + + /** + * @var ProductInterface + */ + private $configurableProduct; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->objectManager = Bootstrap::getObjectManager(); + $this->request = $this->objectManager->get(RequestInterface::class); + $this->helper = $this->objectManager->create(Helper::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->productResource =$this->objectManager->get(ProductResource::class); + $this->productAttributeRepository = $this->objectManager->create(ProductAttributeRepositoryInterface::class); + $this->jsonSerializer = $this->objectManager->get(SerializerInterface::class); + $this->searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $this->config = $this->objectManager->get(Config::class); + $this->mediaDirectory = $this->objectManager->get(Filesystem::class)->getDirectoryWrite(DirectoryList::MEDIA); + $this->configurableProduct = $this->productRepository->get('configurable'); + } + + /** + * Tests adding images with various roles to child products by saving a configurable product. + * + * @magentoDataFixture Magento/Catalog/_files/product_image.php + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @dataProvider initializeDataProvider + * @param array $childProducts + * @param array $expectedImages + * @return void + */ + public function testInitialize(array $childProducts, array $expectedImages): void + { + $this->setRequestParams($childProducts); + $this->helper->initialize($this->configurableProduct); + $this->assertChildProductImages($expectedImages); + } + + /** + * Tests replacing images with various roles to child products by saving a configurable product. + * + * @magentoDataFixture Magento/Catalog/_files/product_image.php + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @dataProvider initializeWithExistingChildImagesDataProvider + * @param array $childProducts + * @param array $expectedImages + * @return void + */ + public function testInitializeWithExistingChildImages(array $childProducts, array $expectedImages): void + { + $this->updateChildProductsImages( + [ + 'simple_10' => '/m/a/magento_thumbnail.jpg.tmp', + 'simple_20' => '/m/a/magento_small_image.jpg.tmp', + ] + ); + $this->setRequestParams($childProducts); + $this->helper->initialize($this->configurableProduct); + $this->assertChildProductImages($expectedImages); + } + + /** + * @return array + */ + public function initializeDataProvider(): array + { + return [ + 'children_with_same_image_and_roles' => [ + 'child_products' => [ + 'simple_10' => [ + 'media_gallery' => $this->getMediaGallery(['ben062bdw2v' => '/m/a/magento_image.jpg.tmp']), + 'images' => [ + '/m/a/magento_image.jpg.tmp' => ['swatch_image', 'small_image', 'image', 'thumbnail'], + ], + ], + 'simple_20' => [ + 'media_gallery' => $this->getMediaGallery(['ben062bdw2v' => '/m/a/magento_image.jpg.tmp']), + 'images' => [ + '/m/a/magento_image.jpg.tmp' => ['swatch_image', 'small_image', 'image', 'thumbnail'], + ], + ], + ], + 'expected_images' => [ + 'simple_10' => [ + '/m/a/magento_image_1.jpg' => ['swatch_image', 'small_image', 'image', 'thumbnail'], + ], + 'simple_20' => [ + '/m/a/magento_image_2.jpg' => ['swatch_image', 'small_image', 'image', 'thumbnail'], + ], + ], + ], + 'children_with_different_images' => [ + 'child_products' => [ + 'simple_10' => [ + 'media_gallery' => $this->getMediaGallery(['ben062bdw2v' => '/m/a/magento_image.jpg.tmp']), + 'images' => [ + '/m/a/magento_image.jpg.tmp' => ['swatch_image', 'small_image', 'image', 'thumbnail'], + ], + ], + 'simple_20' => [ + 'media_gallery' => $this->getMediaGallery( + ['lrwuv5ukisn' => '/m/a/magento_small_image.jpg.tmp'] + ), + 'images' => [ + '/m/a/magento_small_image.jpg.tmp' => ['swatch_image', 'small_image', 'image', 'thumbnail'], + ], + ], + ], + 'expected_images' => [ + 'simple_10' => [ + '/m/a/magento_image_1.jpg' => ['swatch_image', 'small_image', 'image', 'thumbnail'], + ], + 'simple_20' => [ + '/m/a/magento_small_image_1.jpg' => ['swatch_image', 'small_image', 'image', 'thumbnail'], + ], + ], + ], + 'children_with_different_image_roles' => [ + 'child_products' => [ + 'simple_10' => [ + 'media_gallery' => $this->getMediaGallery( + [ + 'ben062bdw2v' => '/m/a/magento_image.jpg.tmp', + 'lrwuv5ukisn' => '/m/a/magento_small_image.jpg.tmp', + ] + ), + 'images' => [ + '/m/a/magento_image.jpg.tmp' => ['swatch_image', 'small_image'], + '/m/a/magento_small_image.jpg.tmp' => ['image', 'thumbnail'], + ], + ], + 'simple_20' => [ + 'media_gallery' => $this->getMediaGallery( + [ + 'ben062bdw2v' => '/m/a/magento_image.jpg.tmp', + 'lrwuv5ukisn' => '/m/a/magento_small_image.jpg.tmp', + ] + ), + 'images' => [ + '/m/a/magento_small_image.jpg.tmp' => ['swatch_image', 'small_image'], + '/m/a/magento_image.jpg.tmp' => ['image', 'thumbnail'], + ], + ], + ], + 'expected_images' => [ + 'simple_10' => [ + '/m/a/magento_image_1.jpg' => ['swatch_image', 'small_image'], + '/m/a/magento_small_image_1.jpg' => ['image', 'thumbnail'], + ], + 'simple_20' => [ + '/m/a/magento_small_image_2.jpg' => ['swatch_image', 'small_image'], + '/m/a/magento_image_2.jpg' => ['image', 'thumbnail'], + ], + ], + ], + ]; + } + + /** + * @return array + */ + public function initializeWithExistingChildImagesDataProvider(): array + { + $dataProvider = $this->initializeDataProvider(); + unset($dataProvider['children_with_different_images'], $dataProvider['children_with_different_image_roles']); + + return array_values($dataProvider); + } + + /** + * Sets configurable product params to request. + * + * @param array $childProducts + * @return void + */ + private function setRequestParams(array $childProducts): void + { + $matrix = $associatedProductIds = []; + $attribute = $this->productAttributeRepository->get('test_configurable'); + + foreach ($childProducts as $sku => $product) { + $simpleProduct = $this->productRepository->get($sku); + $attributeValue = $simpleProduct->getData('test_configurable'); + foreach ($product['images'] as $image => $roles) { + foreach ($roles as $role) { + $product[$role] = $image; + } + } + unset($product['images']); + $product['configurable_attribute'] = $this->jsonSerializer->serialize( + ['test_configurable' => $attributeValue] + ); + $product['variationKey'] = $attributeValue; + $product['id'] = $simpleProduct->getId(); + $product['sku'] = $sku; + $product['was_changed'] = true; + $product['newProduct'] = 0; + $matrix[] = $product; + $associatedProductIds[] = $simpleProduct->getId(); + } + $this->request->setParams( + [ + 'attributes' => [$attribute->getAttributeId()], + 'configurable-matrix-serialized' => $this->jsonSerializer->serialize($matrix), + ] + ); + $this->request->setPostValue( + 'associated_product_ids_serialized', + $this->jsonSerializer->serialize($associatedProductIds) + ); + } + + /** + * Asserts child products images. + * + * @param array $expectedImages + * @return void + */ + private function assertChildProductImages(array $expectedImages): void + { + $simpleIds = $this->configurableProduct->getExtensionAttributes()->getConfigurableProductLinks(); + $criteria = $this->searchCriteriaBuilder->addFilter('entity_id', $simpleIds, 'in')->create(); + foreach ($this->productRepository->getList($criteria)->getItems() as $simpleProduct) { + $images = $expectedImages[$simpleProduct->getSku()]; + foreach ($images as $image => $roles) { + foreach ($roles as $role) { + $this->assertEquals($image, $simpleProduct->getData($role)); + } + $this->assertFileExists( + $this->mediaDirectory->getAbsolutePath($this->config->getBaseMediaPath() . $image) + ); + } + } + } + + /** + * Returns media gallery product param. + * + * @param array $imageNames + * @return array + */ + private function getMediaGallery(array $imageNames): array + { + $images = []; + foreach ($imageNames as $key => $item) { + $images[$key] = ['file' => $item, 'label' => '', 'media_type' => 'image']; + } + + return ['images' => $images]; + } + + /** + * Sets image to child products. + * + * @param array $imageNames + * @return void + */ + private function updateChildProductsImages(array $imageNames): void + { + $simpleIds = $this->configurableProduct->getExtensionAttributes()->getConfigurableProductLinks(); + $criteria = $this->searchCriteriaBuilder->addFilter('entity_id', $simpleIds, 'in')->create(); + $products = $this->productRepository->getList($criteria)->getItems(); + foreach ($products as $simpleProduct) { + $simpleProduct->setStoreId(Store::DEFAULT_STORE_ID) + ->setImage($imageNames[$simpleProduct->getSku()]) + ->setSmallImage($imageNames[$simpleProduct->getSku()]) + ->setThumbnail($imageNames[$simpleProduct->getSku()]) + ->setSwatchImage($imageNames[$simpleProduct->getSku()]) + ->setData( + 'media_gallery', + [ + 'images' => [ + ['file' => $imageNames[$simpleProduct->getSku()], 'label' => '', 'media_type' => 'image'] + ] + ] + ); + $this->productResource->save($simpleProduct); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php index b71507ae43f9f..833100e3e4740 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php @@ -3,76 +3,521 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\ConfigurableProduct\Controller\Adminhtml; -use Magento\Catalog\Model\Product; -use Magento\Framework\Registry; -use Magento\TestFramework\ObjectManager; +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type\Simple; +use Magento\Catalog\Model\Product\Type\Virtual; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Eav\Model\Config; use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Registry; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\TestFramework\TestCase\AbstractBackendController; /** + * Tests for configurable product admin save. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @magentoAppArea adminhtml + * @magentoDbIsolation enabled */ -class ProductTest extends \Magento\TestFramework\TestCase\AbstractBackendController +class ProductTest extends AbstractBackendController { + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var ProductAttributeRepositoryInterface + */ + private $productAttributeRepository; + + /** + * @var Registry + */ + private $registry; + + /** + * @var SerializerInterface + */ + private $jsonSerializer; + + /** + * @var Config + */ + private $eavConfig; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->productAttributeRepository = $this->_objectManager->create(ProductAttributeRepositoryInterface::class); + $this->registry = $this->_objectManager->get(Registry::class); + $this->jsonSerializer = $this->_objectManager->get(SerializerInterface::class); + $this->eavConfig = $this->_objectManager->get(Config::class); + } + /** * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php * @magentoDataFixture Magento/ConfigurableProduct/_files/associated_products.php + * @return void */ - public function testSaveActionAssociatedProductIds() + public function testSaveActionAssociatedProductIds(): void { $associatedProductIds = ['3', '14', '15', '92']; - $associatedProductIdsJSON = json_encode($associatedProductIds); $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue( [ 'id' => 1, - 'attributes' => [$this->_getConfigurableAttribute()->getId()], - 'associated_product_ids_serialized' => $associatedProductIdsJSON, + 'attributes' => [$this->getAttribute('test_configurable')->getId()], + 'associated_product_ids_serialized' => $this->jsonSerializer->serialize($associatedProductIds), ] ); + $this->dispatch('backend/catalog/product/save'); + $this->assertSessionMessages($this->equalTo([__('You saved the product.')]), MessageInterface::TYPE_SUCCESS); + $this->assertRegistryConfigurableLinks($associatedProductIds); + $this->assertConfigurableLinks('configurable', $associatedProductIds); + } + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_attribute.php + * @dataProvider saveNewProductDataProvider + * @param array $childProducts + * @return void + */ + public function testSaveNewProduct(array $childProducts): void + { + $this->serRequestParams($childProducts); $this->dispatch('backend/catalog/product/save'); + $this->assertSessionMessages($this->equalTo([__('You saved the product.')]), MessageInterface::TYPE_SUCCESS); + $this->assertChildProducts($childProducts); + $this->assertConfigurableOptions('configurable', $childProducts); + $this->assertConfigurableLinks('configurable', $this->getProductIds(array_keys($childProducts))); + } - /** @var $objectManager ObjectManager */ - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + /** + * @return array + */ + public function saveNewProductDataProvider(): array + { + return [ + 'with_different_prices_and_qty' => [ + 'child_products' => [ + 'simple_1' => [ + 'name' => 'simple_1', + 'sku' => 'simple_1', + 'price' => '200', + 'weight' => '1', + 'qty' => '100', + 'attributes' => ['test_configurable' => 'Option 1'], + ], + 'simple_2' => [ + 'name' => 'simple_2', + 'sku' => 'simple_2', + 'price' => '100', + 'weight' => '1', + 'qty' => '200', + 'attributes' => ['test_configurable' => 'Option 2'], + ], + ], + ], + 'without_weight' => [ + 'child_products' => [ + 'simple_1' => [ + 'name' => 'simple_1', + 'sku' => 'simple_1', + 'price' => '100', + 'qty' => '100', + 'attributes' => ['test_configurable' => 'Option 1'], + ], + 'simple_2' => [ + 'name' => 'simple_2', + 'sku' => 'simple_2', + 'price' => '100', + 'qty' => '100', + 'attributes' => ['test_configurable' => 'Option 2'], + ], + ], + ], + ]; + } - /** @var $product Product */ - $product = $objectManager->get(Registry::class)->registry('current_product'); - $configurableProductLinks = array_values($product->getExtensionAttributes()->getConfigurableProductLinks()); - self::assertEquals( - $associatedProductIds, - $configurableProductLinks, - 'Product links are not available in the registry' + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_one_simple.php + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_attribute_2.php + * @dataProvider saveExistProductDataProvider + * @param array $childProducts + * @param array $associatedProducts + * @return void + */ + public function testSaveExistProduct(array $childProducts, array $associatedProducts): void + { + $configurableProduct = $this->productRepository->get('configurable'); + $this->serRequestParams($childProducts, $associatedProducts, (int)$configurableProduct->getId()); + $this->dispatch('backend/catalog/product/save'); + $this->assertSessionMessages($this->equalTo([__('You saved the product.')]), MessageInterface::TYPE_SUCCESS); + $this->assertChildProducts($childProducts); + $this->assertConfigurableOptions('configurable', $childProducts); + $this->assertConfigurableLinks( + 'configurable', + $this->getProductIds(array_merge($associatedProducts, array_keys($childProducts))) + ); + } + + /** + * @return array + */ + public function saveExistProductDataProvider(): array + { + return [ + 'added_new_option' => [ + 'child_products' => [ + 'simple_2' => [ + 'name' => 'simple_2', + 'sku' => 'simple_2', + 'price' => '100', + 'weight' => '1', + 'qty' => '200', + 'attributes' => ['test_configurable' => 'Option 2'], + ], + ], + 'associated_products' => ['simple_1'], + ], + 'added_new_option_and_delete_old' => [ + 'child_products' => [ + 'simple_2' => [ + 'name' => 'simple_2', + 'sku' => 'simple_2', + 'price' => '100', + 'qty' => '100', + 'attributes' => ['test_configurable' => 'Option 2'], + ], + ], + 'associated_products' => [], + ], + 'delete_all_options' => [ + 'child_products' => [], + 'associated_products' => [], + ], + 'added_new_attribute' => [ + 'child_products' => [ + 'simple_1_1' => [ + 'name' => 'simple_1_1', + 'sku' => 'simple_1_1', + 'price' => '100', + 'weight' => '1', + 'qty' => '200', + 'attributes' => [ + 'test_configurable' => 'Option 1', + 'test_configurable_2' => 'Option 1', + ], + ], + 'simple_1_2' => [ + 'name' => 'simple_1_2', + 'sku' => 'simple_1_2', + 'price' => '100', + 'weight' => '1', + 'qty' => '200', + 'attributes' => [ + 'test_configurable' => 'Option 1', + 'test_configurable_2' => 'Option 2', + ], + ], + ], + 'associated_products' => [], + ], + 'added_new_attribute_and_delete_old' => [ + 'child_products' => [ + 'simple_2_1' => [ + 'name' => 'simple_2_1', + 'sku' => 'simple_2_1', + 'price' => '100', + 'qty' => '100', + 'attributes' => ['test_configurable_2' => 'Option 1'], + ], + 'simple_2_2' => [ + 'name' => 'simple_2_2', + 'sku' => 'simple_2_2', + 'price' => '100', + 'qty' => '100', + 'attributes' => ['test_configurable_2' => 'Option 2'], + ], + ], + 'associated_products' => [], + ], + ]; + } + + /** + * Sets products data into request. + * + * @param array $childProducts + * @param array|null $associatedProducts + * @param int|null $mainProductId + * @return void + */ + private function serRequestParams( + array $childProducts, + ?array $associatedProducts = [], + ?int $mainProductId = null + ): void { + $this->setVariationMatrix($childProducts); + $this->setAssociatedProducts($associatedProducts); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setParams( + [ + 'type' => Configurable::TYPE_CODE, + 'set' => $this->getDefaultAttributeSetId(), + 'id' => $mainProductId, + ] + ); + $this->getRequest()->setPostValue( + 'product', + [ + 'attribute_set_id' => $this->getDefaultAttributeSetId(), + 'name' => 'configurable', + 'sku' => 'configurable', + 'configurable_attributes_data' => $this->getConfigurableAttributesData($childProducts) ?: null, + ] ); + } - /** @var $product \Magento\Catalog\Api\Data\ProductInterface */ - $product = $objectManager->get(ProductRepositoryInterface::class)->getById(1, false, null, true); - $configurableProductLinks = array_values($product->getExtensionAttributes()->getConfigurableProductLinks()); - self::assertEquals( + /** + * Asserts product configurable links. + * + * @param string $sku + * @param array $associatedProductIds + * @return void + */ + private function assertConfigurableLinks(string $sku, array $associatedProductIds): void + { + $product = $this->productRepository->get($sku, false, null, true); + $this->assertEquals( $associatedProductIds, - $configurableProductLinks, + array_values($product->getExtensionAttributes()->getConfigurableProductLinks() ?: []), 'Product links are not available in the database' ); } /** - * Retrieve configurable attribute instance + * Asserts product from registry configurable links. * - * @return \Magento\Catalog\Model\Entity\Attribute - */ - protected function _getConfigurableAttribute() - { - return \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\Entity\Attribute::class - )->loadByCode( - \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Eav\Model\Config::class - )->getEntityType( - 'catalog_product' - )->getId(), - 'test_configurable' + * @param array $associatedProductIds + * @return void + */ + private function assertRegistryConfigurableLinks(array $associatedProductIds): void + { + $product = $this->registry->registry('current_product'); + $this->assertNotNull($product); + $this->assertEquals( + $associatedProductIds, + array_values($product->getExtensionAttributes()->getConfigurableProductLinks() ?: []), + 'Product links are not available in the registry' + ); + } + + /** + * Asserts child products data. + * + * @param array $childProducts + * @return void + */ + private function assertChildProducts(array $childProducts): void + { + foreach ($this->getProducts(array_column($childProducts, 'sku')) as $product) { + $expectedProduct = $childProducts[$product->getSku()]; + $this->assertEquals($expectedProduct['price'], $product->getPrice()); + + if (!empty($expectedProduct['weight'])) { + $this->assertEquals($expectedProduct['weight'], (double)$product->getWeight()); + $this->assertInstanceOf(Simple::class, $product->getTypeInstance()); + } else { + $this->assertInstanceOf(Virtual::class, $product->getTypeInstance()); + } + + $this->assertEquals($expectedProduct['qty'], $product->getExtensionAttributes()->getStockItem()->getQty()); + } + } + + /** + * Asserts that configurable attributes present in product configurable option list. + * + * @param string $sku + * @param array $childProducts + * @return void + */ + private function assertConfigurableOptions(string $sku, array $childProducts): void + { + $configurableProduct = $this->productRepository->get($sku, false, null, true); + $options = $configurableProduct->getExtensionAttributes()->getConfigurableProductOptions(); + if (empty($childProducts)) { + $this->assertNull($options); + } else { + foreach ($options as $option) { + $attribute = $this->getAttribute($option->getAttributeId()); + foreach ($childProducts as $childProduct) { + $this->assertContains($attribute->getAttributeCode(), array_keys($childProduct['attributes'])); + } + } + } + } + + /** + * Sets configurable product params to request. + * + * @param array $childProducts + * @return void + */ + private function setVariationMatrix(array $childProducts): void + { + $matrix = $attributeIds = $configurableAttributes = []; + foreach ($childProducts as $product) { + foreach ($product['attributes'] as $attributeCode => $optionLabel) { + $attribute = $this->getAttribute($attributeCode); + $configurableAttributes[$attributeCode] = $attribute->getSource()->getOptionId($optionLabel); + $attributeIds[] = $attribute->getAttributeId(); + } + $product['status'] = Status::STATUS_ENABLED; + $product['configurable_attribute'] = $this->jsonSerializer->serialize($configurableAttributes); + $product['newProduct'] = 1; + $product['variationKey'] = implode('-', array_values($configurableAttributes)); + $matrix[] = $product; + } + $this->getRequest()->setPostValue( + [ + 'affect_configurable_product_attributes' => 1, + 'attributes' => $attributeIds, + 'new-variations-attribute-set-id' => $this->getDefaultAttributeSetId(), + 'configurable-matrix-serialized' => $this->jsonSerializer->serialize($matrix), + ] ); } + + /** + * Sets associated product ids param to request. + * + * @param array|null $associatedProducts + */ + private function setAssociatedProducts(?array $associatedProducts): void + { + if (!empty($associatedProducts)) { + $associatedProductIds = array_map( + function (ProductInterface $product) { + return $product->getId(); + }, + $this->getProducts($associatedProducts) + ); + $this->getRequest()->setPostValue( + 'associated_product_ids_serialized', + $this->jsonSerializer->serialize($associatedProductIds) + ); + } + } + + /** + * Returns product configurable attributes data. + * + * @param array $childProducts + * @return array + */ + private function getConfigurableAttributesData(array $childProducts): array + { + $result = []; + foreach ($childProducts as $product) { + foreach ($product['attributes'] as $attributeCode => $optionLabel) { + $attribute = $this->getAttribute($attributeCode); + $optionId = $attribute->getSource()->getOptionId($optionLabel); + if (empty($result[$attribute->getAttributeId()])) { + $result[$attribute->getAttributeId()] = [ + 'attribute_id' =>$attribute->getAttributeId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getAttributeCode(), + 'position' => '0', + 'values' => [ + $optionId => [ + 'include' => '1', + 'value_index' => $optionId, + ], + ], + ]; + } else { + $result[$attribute->getAttributeId()]['values'][$optionId] = [ + 'include' => '1', + 'value_index' => $optionId, + ]; + } + } + } + + return $result; + } + + /** + * Retrieve default product attribute set id. + * + * @return int + */ + private function getDefaultAttributeSetId(): int + { + return (int)$this->eavConfig + ->getEntityType(ProductAttributeInterface::ENTITY_TYPE_CODE) + ->getDefaultAttributeSetId(); + } + + /** + * Retrieve configurable attribute instance. + * + * @param string $attributeCode + * @return ProductAttributeInterface + */ + private function getAttribute(string $attributeCode): ProductAttributeInterface + { + return $this->productAttributeRepository->get($attributeCode); + } + + /** + * Returns products by sku list. + * + * @param array $skuList + * @return ProductInterface[] + */ + private function getProducts(array $skuList): array + { + $result = []; + foreach ($skuList as $sku) { + $result[] = $this->productRepository->get($sku); + } + + return $result; + } + + /** + * Returns product ids by sku list. + * + * @param array $skuList + * @return array + */ + private function getProductIds(array $skuList): array + { + $associatedProductIds = []; + foreach ($this->getProducts($skuList) as $product) { + $associatedProductIds[] = $product->getId(); + } + + return $associatedProductIds; + } } diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/FindByUrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/FindByUrlRewriteTest.php new file mode 100644 index 0000000000000..23ab905fa0eab --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/FindByUrlRewriteTest.php @@ -0,0 +1,267 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Model; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection; +use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollectionFactory; +use Magento\UrlRewrite\Model\UrlRewrite as UrlRewriteItem; +use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; +use PHPUnit\Framework\TestCase; + +/** + * Test cases related to check that URL rewrite has created or not. + */ +class FindByUrlRewriteTest extends TestCase +{ + /** + * @var ObjectManager + */ + private $objectManger; + + /** + * @var ProductResource + */ + private $productResource; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var UrlRewriteCollectionFactory + */ + private $urlRewriteCollectionFactory; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManger = Bootstrap::getObjectManager(); + $this->productResource = $this->objectManger->get(ProductResource::class); + $this->productRepository = $this->objectManger->get(ProductRepositoryInterface::class); + $this->urlRewriteCollectionFactory = $this->objectManger->get(UrlRewriteCollectionFactory::class); + parent::setUp(); + } + + /** + * Assert that product is available by URL rewrite with different visibility. + * + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products.php + * @dataProvider visibilityWithExpectedResultDataProvider + * @magentoDbIsolation enabled + * + * @param array $productsData + * @return void + */ + public function testCheckIsUrlRewriteForChildrenProductsHasCreated(array $productsData): void + { + $this->checkConfigurableUrlRewriteWasCreated(); + $this->updateProductsVisibility($productsData); + $productIdsBySkus = $this->getProductIdsBySkus($productsData); + $urlRewritesCollection = $this->getUrlRewritesCollectionByProductIds($productIdsBySkus); + $expectedCount = 0; + foreach ($productsData as $productData) { + $productId = $productIdsBySkus[$productData['sku']]; + /** @var UrlRewriteItem $urlRewrite */ + $urlRewrite = $urlRewritesCollection->getItemByColumnValue( + UrlRewrite::TARGET_PATH, + "catalog/product/view/id/{$productId}" + ); + if ($productData['url_rewrite_created']) { + $this->assertNotNull($urlRewrite); + $this->assertEquals($productId, $urlRewrite->getEntityId()); + $this->assertEquals('product', $urlRewrite->getEntityType()); + $expectedCount++; + } else { + $this->assertNull($urlRewrite); + } + } + $this->assertCount($expectedCount, $urlRewritesCollection); + } + + /** + * Return products visibility, expected result and other product additional data. + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * + * @return array + */ + public function visibilityWithExpectedResultDataProvider(): array + { + return [ + 'visibility_for_both_product_only_catalog' => [ + [ + [ + 'sku' => 'Simple option 1', + 'visibility' => Visibility::VISIBILITY_IN_CATALOG, + 'url_rewrite_created' => true, + ], + [ + 'sku' => 'Simple option 2', + 'visibility' => Visibility::VISIBILITY_IN_CATALOG, + 'url_rewrite_created' => true, + ], + ], + ], + 'visibility_for_both_product_catalog_search' => [ + [ + [ + 'sku' => 'Simple option 1', + 'visibility' => Visibility::VISIBILITY_BOTH, + 'url_rewrite_created' => true, + ], + [ + 'sku' => 'Simple option 2', + 'visibility' => Visibility::VISIBILITY_BOTH, + 'url_rewrite_created' => true, + ], + ], + ], + 'visibility_for_both_product_only_search' => [ + [ + [ + 'sku' => 'Simple option 1', + 'visibility' => Visibility::VISIBILITY_IN_SEARCH, + 'url_rewrite_created' => true, + ], + [ + 'sku' => 'Simple option 2', + 'visibility' => Visibility::VISIBILITY_IN_SEARCH, + 'url_rewrite_created' => true, + ], + ], + ], + 'visibility_for_both_product_not_visible_individuality' => [ + [ + [ + 'sku' => 'Simple option 1', + 'visibility' => Visibility::VISIBILITY_NOT_VISIBLE, + 'url_rewrite_created' => false, + ], + [ + 'sku' => 'Simple option 2', + 'visibility' => Visibility::VISIBILITY_NOT_VISIBLE, + 'url_rewrite_created' => false, + ], + ], + ], + 'visibility_for_one_product_only_catalog' => [ + [ + [ + 'sku' => 'Simple option 1', + 'visibility' => Visibility::VISIBILITY_IN_CATALOG, + 'url_rewrite_created' => true, + ], + [ + 'sku' => 'Simple option 2', + 'visibility' => Visibility::VISIBILITY_NOT_VISIBLE, + 'url_rewrite_created' => false, + ], + ], + ], + 'visibility_for_one_product_catalog_search' => [ + [ + [ + 'sku' => 'Simple option 1', + 'visibility' => Visibility::VISIBILITY_BOTH, + 'url_rewrite_created' => true, + ], + [ + 'sku' => 'Simple option 2', + 'visibility' => Visibility::VISIBILITY_NOT_VISIBLE, + 'url_rewrite_created' => false, + ], + ], + ], + 'visibility_for_one_product_only_search' => [ + [ + [ + 'sku' => 'Simple option 1', + 'visibility' => Visibility::VISIBILITY_IN_SEARCH, + 'url_rewrite_created' => true, + ], + [ + 'sku' => 'Simple option 2', + 'visibility' => Visibility::VISIBILITY_NOT_VISIBLE, + 'url_rewrite_created' => false, + ], + ], + ], + ]; + } + + /** + * Update products visibility. + * + * @param array $productsData + * @return void + */ + private function updateProductsVisibility(array $productsData): void + { + foreach ($productsData as $productData) { + $product = $this->productRepository->get($productData['sku']); + $product->setVisibility($productData['visibility']); + $this->productRepository->save($product); + } + } + + /** + * Get URL rewrite collection by product ids. + * + * @param int[] $productIds + * @param string $storeCode + * @return UrlRewriteCollection + */ + private function getUrlRewritesCollectionByProductIds( + array $productIds, + string $storeCode = 'default' + ): UrlRewriteCollection { + $collection = $this->urlRewriteCollectionFactory->create(); + $collection->addStoreFilter($storeCode); + $collection->addFieldToFilter(UrlRewrite::ENTITY_TYPE, ['eq' => 'product']); + $collection->addFieldToFilter(UrlRewrite::ENTITY_ID, ['in' => $productIds]); + + return $collection; + } + + /** + * Check that configurable url rewrite was created. + * + * @return void + */ + private function checkConfigurableUrlRewriteWasCreated(): void + { + $configurableProduct = $this->productRepository->get('Configurable product'); + $configurableUrlRewrite = $this->getUrlRewritesCollectionByProductIds([$configurableProduct->getId()]) + ->getFirstItem(); + $this->assertEquals( + $configurableUrlRewrite->getTargetPath(), + "catalog/product/view/id/{$configurableProduct->getId()}" + ); + } + + /** + * Load all product ids by skus. + * + * @param array $productsData + * @return array + */ + private function getProductIdsBySkus(array $productsData): array + { + $skus = array_column($productsData, 'sku'); + + return $this->productResource->getProductsIdsBySkus($skus); + } +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/SalableTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/SalableTest.php new file mode 100644 index 0000000000000..33c82dce21963 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/SalableTest.php @@ -0,0 +1,121 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Model\Product\Type\Configurable; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\CatalogInventory\Api\Data\StockStatusInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Check is configurable product salable with different conditions + * + * @magentoAppArea frontend + */ +class SalableTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + } + + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * + * @dataProvider salableDataProvider + * + * @param array $productSkus + * @param array $productData + * @param bool $expectedValue + * @return void + */ + public function testIsSalable(array $productSkus, array $productData, bool $expectedValue): void + { + $this->updateProduct($productSkus, $productData); + $configurableProduct = $this->productRepository->get('configurable', false, null, true); + + $this->assertEquals($expectedValue, $configurableProduct->getIsSalable()); + } + + /** + * @return array + */ + public function salableDataProvider(): array + { + return [ + 'all children enabled_and_in_stock' => [ + 'product_skus' => [], + 'data' => [], + 'expected_value' => true, + ], + 'one_child_out_of_stock' => [ + 'product_skus' => ['simple_10'], + 'data' => [ + 'stock_data' => [ + 'use_config_manage_stock' => 1, + 'is_in_stock' => StockStatusInterface::STATUS_OUT_OF_STOCK, + ], + ], + 'expected_value' => true, + ], + 'one_child_disabled' => [ + 'product_skus' => ['simple_10'], + 'data' => ['status' => Status::STATUS_DISABLED], + 'expected_value' => true, + ], + 'all_children_disabled' => [ + 'product_skus' => ['simple_10', 'simple_20'], + 'data' => ['status' => Status::STATUS_DISABLED], + 'expected_value' => false, + ], + 'all_children_out_of_stock' => [ + 'product_skus' => ['simple_10', 'simple_20'], + 'data' => [ + 'stock_data' => [ + 'use_config_manage_stock' => 1, + 'is_in_stock' => StockStatusInterface::STATUS_OUT_OF_STOCK, + ], + ], + 'expected_value' => false, + ] + ]; + } + + /** + * Update product with data + * + * @param array $skus + * @param array $data + * @return void + */ + private function updateProduct(array $skus, array $data): void + { + if (!empty($skus)) { + foreach ($skus as $sku) { + $product = $this->productRepository->get($sku); + $product->addData($data); + $this->productRepository->save($product); + } + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/VariationHandlerTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/VariationHandlerTest.php index fc2e99d6a9d10..c3b845694c6a0 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/VariationHandlerTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/VariationHandlerTest.php @@ -3,78 +3,105 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\ConfigurableProduct\Model\Product; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\CatalogInventory\Api\StockRegistryInterface; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Framework\ObjectManagerInterface; use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; /** + * Tests for simple products generation by saving a configurable product. + * * @magentoAppIsolation enabled - * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @magentoDbIsolation enabled */ -class VariationHandlerTest extends \PHPUnit\Framework\TestCase +class VariationHandlerTest extends TestCase { - /** @var \Magento\ConfigurableProduct\Model\Product\VariationHandler */ - private $_model; + /** + * @var ObjectManagerInterface + */ + private $objectManager; - /** @var \Magento\Catalog\Model\Product */ - private $_product; + /** + * @var VariationHandler + */ + private $variationHandler; - /** @var \Magento\CatalogInventory\Api\StockRegistryInterface */ + /** + * @var ProductInterface + */ + private $product; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var StockRegistryInterface + */ private $stockRegistry; + /** + * @inheritdoc + */ protected function setUp() { - $this->_product = Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\Product::class - ); - $this->_product->load(1); - - $this->_model = Bootstrap::getObjectManager()->create( - \Magento\ConfigurableProduct\Model\Product\VariationHandler::class - ); - // prevent fatal errors by assigning proper "singleton" of type instance to the product - $this->_product->setTypeInstance($this->_model); - $this->stockRegistry = Bootstrap::getObjectManager()->get( - \Magento\CatalogInventory\Api\StockRegistryInterface::class - ); + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->variationHandler = $this->objectManager->create(VariationHandler::class); + $this->product = $this->productRepository->get('configurable'); + $this->stockRegistry = $this->objectManager->get(StockRegistryInterface::class); } /** - * @param array $productsData + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php * @dataProvider generateSimpleProductsDataProvider + * @param array $productsData + * @return void */ - public function testGenerateSimpleProducts($productsData) + public function testGenerateSimpleProducts(array $productsData): void { - $this->_product->setNewVariationsAttributeSetId(4); - // Default attribute set id - $generatedProducts = $this->_model->generateSimpleProducts($this->_product, $productsData); + $this->product->setImage('some_test_image.jpg') + ->setSmallImage('some_test_image.jpg') + ->setThumbnail('some_test_image.jpg') + ->setSwatchImage('some_test_image.jpg') + ->setNewVariationsAttributeSetId($this->product->getDefaultAttributeSetId()); + $generatedProducts = $this->variationHandler->generateSimpleProducts($this->product, $productsData); $this->assertEquals(3, count($generatedProducts)); foreach ($generatedProducts as $productId) { $stockItem = $this->stockRegistry->getStockItem($productId); - /** @var $product \Magento\Catalog\Model\Product */ - $product = Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\Product::class - ); - $product->load($productId); + $product = $this->productRepository->getById($productId); $this->assertNotNull($product->getName()); $this->assertNotNull($product->getSku()); $this->assertNotNull($product->getPrice()); $this->assertNotNull($product->getWeight()); $this->assertEquals('1', $stockItem->getIsInStock()); + $this->assertNull($product->getImage()); + $this->assertNull($product->getSmallImage()); + $this->assertNull($product->getThumbnail()); + $this->assertNull($product->getSwatchImage()); } } /** * @param array $productsData + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php * @dataProvider generateSimpleProductsWithPartialDataDataProvider - * @magentoDbIsolation enabled + * @return void */ - public function testGenerateSimpleProductsWithPartialData($productsData) + public function testGenerateSimpleProductsWithPartialData(array $productsData): void { - $this->_product->setNewVariationsAttributeSetId(4); - $generatedProducts = $this->_model->generateSimpleProducts($this->_product, $productsData); - $parentStockItem = $this->stockRegistry->getStockItem($this->_product->getId()); + $this->product->setNewVariationsAttributeSetId(4); + $generatedProducts = $this->variationHandler->generateSimpleProducts($this->product, $productsData); + $parentStockItem = $this->stockRegistry->getStockItem($this->product->getId()); foreach ($generatedProducts as $productId) { $stockItem = $this->stockRegistry->getStockItem($productId); $this->assertEquals($parentStockItem->getManageStock(), $stockItem->getManageStock()); @@ -85,7 +112,7 @@ public function testGenerateSimpleProductsWithPartialData($productsData) /** * @return array */ - public static function generateSimpleProductsDataProvider() + public function generateSimpleProductsDataProvider(): array { return [ [ @@ -122,7 +149,7 @@ public static function generateSimpleProductsDataProvider() /** * @return array */ - public static function generateSimpleProductsWithPartialDataDataProvider() + public function generateSimpleProductsWithPartialDataDataProvider(): array { return [ [ diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/QuickSearchTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/QuickSearchTest.php new file mode 100644 index 0000000000000..6884be3b04d14 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/QuickSearchTest.php @@ -0,0 +1,170 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Model; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Visibility; +use Magento\TestFramework\Catalog\Model\Layer\QuickSearchByQuery; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Test cases related to find configurable product via quick search using mysql search engine. + * + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products.php + * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php + * + * @magentoDbIsolation disabled + * @magentoAppIsolation enabled + */ +class QuickSearchTest extends TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var QuickSearchByQuery + */ + private $quickSearchByQuery; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->quickSearchByQuery = $this->objectManager->get(QuickSearchByQuery::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + parent::setUp(); + } + + /** + * Assert that configurable child products has not found by query using mysql search engine. + * + * @magentoConfigFixture default/catalog/search/engine mysql + * + * @return void + */ + public function testChildProductsHasNotFoundedByQuery(): void + { + $this->checkThatOnlyConfigurableProductIsAvailableBySearch('Configurable Option'); + } + + /** + * Assert that child product of configurable will be available by search after + * set to product visibility by catalog and search using mysql search engine. + * + * @magentoConfigFixture default/catalog/search/engine mysql + * @dataProvider productAvailabilityInSearchByVisibilityDataProvider + * + * @param int $visibility + * @param bool $expectedResult + * @return void + */ + public function testOneOfChildIsAvailableBySearch(int $visibility, bool $expectedResult): void + { + $this->checkThatOnlyConfigurableProductIsAvailableBySearch('Configurable Option'); + $this->updateProductVisibility($visibility); + $this->checkProductAvailabilityInSearch($expectedResult); + $this->checkThatOnlyConfigurableProductIsAvailableBySearch('White'); + } + + /** + * Return data with product visibility and expected result. + * + * @return array + */ + public function productAvailabilityInSearchByVisibilityDataProvider(): array + { + return [ + 'visible_catalog_only' => [ + Visibility::VISIBILITY_IN_CATALOG, + false, + ], + 'visible_catalog_and_search' => [ + Visibility::VISIBILITY_BOTH, + true, + ], + 'visible_search_only' => [ + Visibility::VISIBILITY_IN_SEARCH, + true, + ], + 'visible_search_not_visible_individuality' => [ + Visibility::VISIBILITY_NOT_VISIBLE, + false, + ], + ]; + } + + /** + * Assert that configurable product was found by option value using mysql search engine. + * + * @magentoConfigFixture default/catalog/search/engine mysql + * + * @return void + */ + public function testSearchByOptionValue(): void + { + $this->checkThatOnlyConfigurableProductIsAvailableBySearch('Option 1'); + } + + /** + * Assert that anyone child product is not available by quick search. + * + * @param string $searchQuery + * + * @return void + */ + private function checkThatOnlyConfigurableProductIsAvailableBySearch(string $searchQuery): void + { + $searchResult = $this->quickSearchByQuery->execute($searchQuery); + $this->assertCount(1, $searchResult->getItems()); + /** @var Product $configurableProduct */ + $configurableProduct = $searchResult->getFirstItem(); + $this->assertEquals('Configurable product', $configurableProduct->getSku()); + } + + /** + * Update product visibility. + * + * @param int $visibility + * @return void + */ + private function updateProductVisibility(int $visibility): void + { + $childProduct = $this->productRepository->get('Simple option 1'); + $childProduct->setVisibility($visibility); + $this->productRepository->save($childProduct); + } + + /** + * Assert that configurable and one of child product is available by search. + * + * @param bool $firstChildIsVisible + * @return void + */ + private function checkProductAvailabilityInSearch(bool $firstChildIsVisible): void + { + $searchResult = $this->quickSearchByQuery->execute('Black'); + $this->assertNotNull($searchResult->getItemByColumnValue(Product::SKU, 'Configurable product')); + $this->assertEquals( + $firstChildIsVisible, + (bool)$searchResult->getItemByColumnValue(Product::SKU, 'Simple option 1') + ); + $this->assertNull($searchResult->getItemByColumnValue(Product::SKU, 'Simple option 2')); + } +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_one_simple.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_one_simple.php new file mode 100644 index 0000000000000..dd3a31bb29016 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_one_simple.php @@ -0,0 +1,72 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductExtensionInterfaceFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\ConfigurableProduct\Helper\Product\Options\Factory; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/configurable_attribute.php'; +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +/** @var Factory $optionsFactory */ +$optionsFactory = $objectManager->get(Factory::class); +/** @var ProductExtensionInterfaceFactory $productExtensionAttributes */ +$productExtensionAttributesFactory = $objectManager->get(ProductExtensionInterfaceFactory::class); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$defaultWebsiteId = $websiteRepository->get('base')->getId(); + +$option = $attribute->getSource()->getOptionId('Option 1'); +$product = $productFactory->create(); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$defaultWebsiteId]) + ->setName('Configurable Option 1') + ->setSku('simple_1') + ->setPrice(10.00) + ->setTestConfigurable($option) + ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); +$product = $productRepository->save($product); + +$configurableOptions = $optionsFactory->create( + [ + [ + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getStoreLabel(), + 'position' => '0', + 'values' => [['label' => 'test', 'attribute_id' => $attribute->getId(), 'value_index' => $option]], + ], + ] +); +$extensionConfigurableAttributes = $product->getExtensionAttributes() ?: $productExtensionAttributesFactory->create(); +$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions); +$extensionConfigurableAttributes->setConfigurableProductLinks([$product->getId()]); + +$configurableProduct = $productFactory->create(); +$configurableProduct->setExtensionAttributes($extensionConfigurableAttributes); +$configurableProduct->setTypeId(Configurable::TYPE_CODE) + ->setAttributeSetId($configurableProduct->getDefaultAttributeSetId()) + ->setWebsiteIds([$defaultWebsiteId]) + ->setName('Configurable Product') + ->setSku('configurable') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]); +$productRepository->save($configurableProduct); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_one_simple_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_one_simple_rollback.php new file mode 100644 index 0000000000000..165b714caa508 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_one_simple_rollback.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +foreach (['simple_1', 'configurable'] as $sku) { + try { + $product = $productRepository->get($sku); + $productRepository->delete($product); + } catch (NoSuchEntityException $e) { + //Product already removed + } +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); +require __DIR__ . '/configurable_attribute_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_out_of_stock_children.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_out_of_stock_children.php new file mode 100644 index 0000000000000..5c749584b2917 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_out_of_stock_children.php @@ -0,0 +1,104 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductExtensionFactory; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type as ProductType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\CatalogInventory\Api\Data\StockStatusInterface; +use Magento\ConfigurableProduct\Helper\Product\Options\Factory; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/configurable_attribute.php'; +require __DIR__ . '/../../Catalog/_files/category.php'; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductAttributeRepositoryInterface $productAttributeRepository */ +$productAttributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +$attribute = $productAttributeRepository->get('test_configurable'); +$options = $attribute->getOptions(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsite = $websiteRepository->get('base'); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$attributeValues = []; +$associatedProductIds = []; +$rootCategoryId = $baseWebsite->getDefaultStore()->getRootCategoryId(); +array_shift($options); + +foreach ($options as $option) { + $product = $productFactory->create(); + $product->setTypeId(ProductType::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsite->getId()]) + ->setName('Configurable Option ' . $option->getLabel()) + ->setSku(strtolower(str_replace(' ', '_', 'simple ' . $option->getLabel()))) + ->setPrice(150) + ->setTestConfigurable($option->getValue()) + ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([$rootCategoryId, 333]) + ->setStockData([ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => StockStatusInterface::STATUS_OUT_OF_STOCK + ]); + $product = $productRepository->save($product); + + $attributeValues[] = [ + 'label' => 'test', + 'attribute_id' => $attribute->getId(), + 'value_index' => $option->getValue(), + ]; + $associatedProductIds[] = $product->getId(); +} +/** @var Factory $optionsFactory */ +$optionsFactory = $objectManager->get(Factory::class); +$configurableAttributesData = [ + [ + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getStoreLabel(), + 'position' => '0', + 'values' => $attributeValues, + ], +]; +$configurableOptions = $optionsFactory->create($configurableAttributesData); + +$product = $productFactory->create(); +/** @var ProductExtensionFactory $extensionAttributesFactory */ +$extensionAttributesFactory = $objectManager->get(ProductExtensionFactory::class); +$extensionConfigurableAttributes = $product->getExtensionAttributes() ?: $extensionAttributesFactory->create(); +$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions); +$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds); +$product->setExtensionAttributes($extensionConfigurableAttributes); + +$product->setTypeId(Configurable::TYPE_CODE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsite->getId()]) + ->setName('Configurable Product') + ->setSku('configurable') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([$rootCategoryId, 333]) + ->setStockData([ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_in_stock' => StockStatusInterface::STATUS_IN_STOCK + ]); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_out_of_stock_children_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_out_of_stock_children_rollback.php new file mode 100644 index 0000000000000..d13b7688f6d91 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_out_of_stock_children_rollback.php @@ -0,0 +1,19 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\ConfigurableProduct\Model\DeleteConfigurableProduct; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var DeleteConfigurableProduct $deleteConfigurableProductService */ +$deleteConfigurableProductService = $objectManager->get(DeleteConfigurableProduct::class); +$deleteConfigurableProductService->execute('configurable'); + +require __DIR__ . '/configurable_attribute_rollback.php'; +require __DIR__ . '/../../Catalog/_files/category_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products.php new file mode 100644 index 0000000000000..bdf7b1e87d77c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products.php @@ -0,0 +1,93 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductExtensionInterfaceFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\CatalogInventory\Model\Stock\ItemFactory; +use Magento\ConfigurableProduct\Helper\Product\Options\Factory; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Eav\Api\Data\AttributeOptionInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/configurable_attribute.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Factory $optionsFactory */ +$optionsFactory = $objectManager->get(Factory::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +/** @var ProductExtensionInterfaceFactory $productExtensionFactory */ +$productExtensionFactory = $objectManager->get(ProductExtensionInterfaceFactory::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsite = $websiteRepository->get('base'); +/** @var AttributeOptionInterface[] $options */ +$options = $attribute->getOptions(); +$associatedProductIds = $attributeValues = []; +$simpleProductsData = [ + ['Simple option 1', 10, 'Black'], + ['Simple option 2', 20, 'White'], +]; +foreach ($options as $option) { + if (!$option->getValue()) { + continue; + } + [$productSku, $productPrice, $productDescription] = array_shift($simpleProductsData); + $product = $productFactory->create(); + $product->isObjectNew(true); + $product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsite->getId()]) + ->setName('Configurable ' . $option->getLabel()) + ->setSku($productSku) + ->setPrice($productPrice) + ->setTestConfigurable($option->getValue()) + ->setDescription($productDescription) + ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); + $product = $productRepository->save($product); + $attributeValues[] = [ + 'label' => 'test', + 'attribute_id' => $attribute->getId(), + 'value_index' => $option->getValue(), + ]; + $associatedProductIds[] = $product->getId(); +} +$product = $productFactory->create(); +$product->isObjectNew(true); +$product->setTypeId(Configurable::TYPE_CODE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsite->getId()]) + ->setName('Configurable product with two child') + ->setSku('Configurable product') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]); +$configurableOptions = $optionsFactory->create( + [ + [ + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getStoreLabel(), + 'position' => '0', + 'values' => $attributeValues, + ], + ] +); +$extensionConfigurableAttributes = $product->getExtensionAttributes() ?? $productExtensionFactory->create(); +$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions); +$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds); +$product->setExtensionAttributes($extensionConfigurableAttributes); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products_rollback.php new file mode 100644 index 0000000000000..b0d9b3d80e11e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products_rollback.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +foreach (['Simple option 1', 'Simple option 2', 'Configurable product'] as $sku) { + try { + $productRepository->deleteById($sku); + } catch (NoSuchEntityException $e) { + //Product has deleted. + } +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); +require __DIR__ . '/configurable_attribute_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerTest.php b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerTest.php index 77ceae27e0774..7b5ddc4b9fa5f 100644 --- a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerTest.php +++ b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/CustomerTest.php @@ -6,11 +6,17 @@ namespace Magento\CustomerImportExport\Model\Import; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\ImportExport\Model\Import; /** * Test for class \Magento\CustomerImportExport\Model\Import\Customer which covers validation logic + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.TooManyPublicMethods) */ class CustomerTest extends \PHPUnit\Framework\TestCase { @@ -82,6 +88,8 @@ public function testImportData() $this->directoryWrite ); + $existingCustomer = $this->getCustomer('CharlesTAlston@teleworm.us', 1); + /** @var $customersCollection \Magento\Customer\Model\ResourceModel\Customer\Collection */ $customersCollection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( \Magento\Customer\Model\ResourceModel\Customer\Collection::class @@ -107,13 +115,6 @@ public function testImportData() $this->assertEquals($expectAddedCustomers, $addedCustomers, 'Added unexpected amount of customers'); - /** @var $objectManager \Magento\TestFramework\ObjectManager */ - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - - $existingCustomer = $objectManager->get( - \Magento\Framework\Registry::class - )->registry('_fixture/Magento_ImportExport_Customer'); - $updatedCustomer = $customers[$existingCustomer->getId()]; $this->assertNotEquals( @@ -154,6 +155,12 @@ public function testImportDataWithOneAdditionalColumn(): void $this->directoryWrite ); + $existingCustomer = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Customer\Model\Customer::class + ); + $existingCustomer->setWebsiteId(1); + $existingCustomer = $existingCustomer->loadByEmail('CharlesTAlston@teleworm.us'); + /** @var $customersCollection \Magento\Customer\Model\ResourceModel\Customer\Collection */ $customersCollection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( \Magento\Customer\Model\ResourceModel\Customer\Collection::class @@ -171,12 +178,6 @@ public function testImportDataWithOneAdditionalColumn(): void $customers = $customersCollection->getItems(); - /** @var $objectManager \Magento\TestFramework\ObjectManager */ - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - - $existingCustomer = $objectManager->get(\Magento\Framework\Registry::class) - ->registry('_fixture/Magento_ImportExport_Customer'); - $updatedCustomer = $customers[$existingCustomer->getId()]; $this->assertNotEquals( @@ -210,8 +211,8 @@ public function testImportDataWithOneAdditionalColumn(): void ); $this->assertEquals( - $existingCustomer->getCustomerGroupId(), - $updatedCustomer->getCustomerGroupId(), + $existingCustomer->getGroupId(), + $updatedCustomer->getGroupId(), 'Customer group must not be changed' ); } @@ -352,4 +353,61 @@ public function testValidateEmailForDeleteBehavior() $this->_model->getErrorAggregator()->getErrorsByCode([Customer::ERROR_CUSTOMER_NOT_FOUND]) ); } + + /** + * Test import existing customers + * + * @magentoDataFixture Magento/Customer/_files/import_export/customers.php + * @return void + */ + public function testUpdateExistingCustomers(): void + { + $this->doImport(__DIR__ . '/_files/customers_to_update.csv', Import::BEHAVIOR_ADD_UPDATE); + $customer = $this->getCustomer('customer@example.com', 1); + $this->assertEquals('Firstname-updated', $customer->getFirstname()); + $this->assertEquals('Lastname-updated', $customer->getLastname()); + $this->assertEquals(1, $customer->getStoreId()); + $customer = $this->getCustomer('julie.worrell@example.com', 1); + $this->assertEquals('Julie-updated', $customer->getFirstname()); + $this->assertEquals('Worrell-updated', $customer->getLastname()); + $this->assertEquals(1, $customer->getStoreId()); + $customer = $this->getCustomer('david.lamar@example.com', 1); + $this->assertEquals('David-updated', $customer->getFirstname()); + $this->assertEquals('Lamar-updated', $customer->getLastname()); + $this->assertEquals(1, $customer->getStoreId()); + } + + /** + * Gets customer entity. + * + * @param string $email + * @param int $websiteId + * @return CustomerInterface + * @throws NoSuchEntityException + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function getCustomer(string $email, int $websiteId): CustomerInterface + { + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + /** @var CustomerRepositoryInterface $repository */ + $repository = $objectManager->get(CustomerRepositoryInterface::class); + return $repository->get($email, $websiteId); + } + + /** + * Import using given file and behavior + * + * @param string $file + * @param string $behavior + */ + private function doImport(string $file, string $behavior): void + { + $source = new \Magento\ImportExport\Model\Import\Source\Csv($file, $this->directoryWrite); + $this->_model + ->setParameters(['behavior' => $behavior]) + ->setSource($source) + ->validateData() + ->hasToBeTerminated(); + $this->_model->importData(); + } } diff --git a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/_files/customers_to_update.csv b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/_files/customers_to_update.csv new file mode 100644 index 0000000000000..b6d8e24860ed3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Import/_files/customers_to_update.csv @@ -0,0 +1,4 @@ +email,_website,_store,confirmation,created_at,created_in,default_billing,default_shipping,disable_auto_group_change,dob,firstname,gender,group_id,lastname,middlename,password_hash,prefix,rp_token,rp_token_created_at,store_id,suffix,taxvat,website_id,password +customer@example.com,base,"default",,5/6/2012 16:15,Admin,"1","1",0,,"Firstname-updated",Male,1,"Lastname-updated",T.,145d12bfff8a6a279eb61e277e3d727c0ba95acc1131237f1594ddbb7687a564:l1,,,,0,,,"1", +julie.worrell@example.com,base,"",,5/6/2012 16:19,Admin,"1","1",0,,"Julie-updated",Female,1,"Worrell-updated",T.,145d12bfff8a6a279eb61e277e3d727c0ba95acc1131237f1594ddbb7687a564:l1,,,,0,,,"1", +david.lamar@example.com,base,"",,5/6/2012 16:25,Admin,"1","1",0,,"David-updated",Male,1,"Lamar-updated",T.,145d12bfff8a6a279eb61e277e3d727c0ba95acc1131237f1594ddbb7687a564:l1,,,,0,,,"1", diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch6/ConfigurableProduct/Model/QuickSearchTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch6/ConfigurableProduct/Model/QuickSearchTest.php new file mode 100644 index 0000000000000..50cb4974a9cf1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch6/ConfigurableProduct/Model/QuickSearchTest.php @@ -0,0 +1,69 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch6\ConfigurableProduct\Model; + +use Magento\ConfigurableProduct\Model\QuickSearchTest as ConfigurableProductQuickSearchTest; + +/** + * Test cases related to find configurable product via quick search using Elasticsearch 6.0+ search engine. + * + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products.php + * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php + * + * @magentoDbIsolation disabled + * @magentoAppIsolation enabled + */ +class QuickSearchTest extends ConfigurableProductQuickSearchTest +{ + /** + * Assert that configurable child products has not found by query using Elasticsearch 6.0+ search engine. + * + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod + * + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 + * + * @return void + */ + public function testChildProductsHasNotFoundedByQuery(): void + { + parent::testChildProductsHasNotFoundedByQuery(); + } + + /** + * Assert that child product of configurable will be available by search after + * set to product visibility by catalog and search using Elasticsearch 6.0+ search engine. + * + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod + * + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 + * + * @dataProvider productAvailabilityInSearchByVisibilityDataProvider + * + * @param int $visibility + * @param bool $expectedResult + * @return void + */ + public function testOneOfChildIsAvailableBySearch(int $visibility, bool $expectedResult): void + { + parent::testOneOfChildIsAvailableBySearch($visibility, $expectedResult); + } + + /** + * Assert that configurable product was found by option value using Elasticsearch 6.0+ search engine. + * + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod + * + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 + * + * @return void + */ + public function testSearchByOptionValue(): void + { + parent::testSearchByOptionValue(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php index 545638bcb0c57..dcd36d4078f8c 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php @@ -5,30 +5,41 @@ */ namespace Magento\Quote\Model\Quote; +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Api\Data\AddressInterfaceFactory; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Framework\Reflection\DataObjectProcessor; +use Magento\Quote\Model\Quote; use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Indexer\TestCase; /** + * Class to test Sales Quote address model functionality + * * @magentoDataFixture Magento/Sales/_files/quote_with_customer.php * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php */ -class AddressTest extends \Magento\TestFramework\Indexer\TestCase +class AddressTest extends TestCase { - /** @var \Magento\Quote\Model\Quote $quote */ + /** @var Quote $quote */ protected $_quote; - /** @var \Magento\Customer\Api\Data\CustomerInterface $customer */ + /** @var CustomerInterface $customer */ protected $_customer; - /** @var \Magento\Quote\Model\Quote\Address */ + /** @var Address */ protected $_address; - /**@var \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository */ + /**@var CustomerRepositoryInterface $customerRepository */ protected $customerRepository; - /** @var \Magento\Customer\Api\AddressRepositoryInterface $addressRepository */ + /** @var AddressRepositoryInterface $addressRepository */ protected $addressRepository; - /** @var \Magento\Framework\Reflection\DataObjectProcessor */ + /** @var DataObjectProcessor */ protected $dataProcessor; /** @@ -36,7 +47,7 @@ class AddressTest extends \Magento\TestFramework\Indexer\TestCase */ public static function setUpBeforeClass() { - $db = \Magento\TestFramework\Helper\Bootstrap::getInstance()->getBootstrap() + $db = Bootstrap::getInstance()->getBootstrap() ->getApplication() ->getDbInstance(); if (!$db->isDbDumpExists()) { @@ -48,43 +59,46 @@ public static function setUpBeforeClass() } /** - * Initialize quote and customer fixtures + * @inheritdoc */ public function setUp() { - $this->_quote = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Quote\Model\Quote::class + $this->_quote = Bootstrap::getObjectManager()->create( + Quote::class ); $this->_quote->load('test01', 'reserved_order_id'); $this->_quote->setIsMultiShipping('0'); - $this->customerRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Customer\Api\CustomerRepositoryInterface::class + $this->customerRepository = Bootstrap::getObjectManager()->create( + CustomerRepositoryInterface::class ); $this->_customer = $this->customerRepository->getById(1); /** @var \Magento\Sales\Model\Order\Address $address */ - $this->_address = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Quote\Model\Quote\Address::class + $this->_address = Bootstrap::getObjectManager()->create( + Address::class ); $this->_address->setId(1); $this->_address->load($this->_address->getId()); $this->_address->setQuote($this->_quote); - $this->addressRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Customer\Api\AddressRepositoryInterface::class + $this->addressRepository = Bootstrap::getObjectManager()->create( + AddressRepositoryInterface::class ); - $this->dataProcessor = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Framework\Reflection\DataObjectProcessor::class + $this->dataProcessor = Bootstrap::getObjectManager()->create( + DataObjectProcessor::class ); } + /** + * @inheritdoc + */ protected function tearDown() { - /** @var \Magento\Customer\Model\CustomerRegistry $customerRegistry */ - $customerRegistry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() - ->get(\Magento\Customer\Model\CustomerRegistry::class); + /** @var CustomerRegistry $customerRegistry */ + $customerRegistry = Bootstrap::getObjectManager() + ->get(CustomerRegistry::class); //Cleanup customer from registry $customerRegistry->remove(1); } @@ -102,9 +116,9 @@ public function testSameAsBillingForBillingAddress($unsetId) if ($unsetId) { $address->setId(null); } - /** @var \Magento\Customer\Api\AddressRepositoryInterface $addressRepository */ + /** @var AddressRepositoryInterface $addressRepository */ $addressRepository = Bootstrap::getObjectManager() - ->create(\Magento\Customer\Api\AddressRepositoryInterface::class); + ->create(AddressRepositoryInterface::class); $customerAddressData = $addressRepository->getById($this->_customer->getDefaultBilling()); $address->setSameAsBilling(0)->setCustomerAddressData($customerAddressData)->save(); $this->assertEquals(0, $this->_quote->getBillingAddress()->getSameAsBilling()); @@ -155,9 +169,9 @@ public function testSameAsBillingWhenQuoteAddressHasNoCustomerAddress($unsetId) */ public function testSameAsBillingWhenCustomerHasNoDefaultShippingAddress($unsetId) { - /** @var \Magento\Customer\Api\AddressRepositoryInterface $addressRepository */ + /** @var AddressRepositoryInterface $addressRepository */ $addressRepository = Bootstrap::getObjectManager() - ->create(\Magento\Customer\Api\AddressRepositoryInterface::class); + ->create(AddressRepositoryInterface::class); $this->_customer->setDefaultShipping(-1) ->setAddresses( [ @@ -194,9 +208,9 @@ public function testSameAsBillingWhenCustomerHasBillingSameShipping($unsetId) */ public function testSameAsBillingWhenCustomerHasDefaultShippingAddress() { - /** @var \Magento\Customer\Api\AddressRepositoryInterface $addressRepository */ + /** @var AddressRepositoryInterface $addressRepository */ $addressRepository = Bootstrap::getObjectManager() - ->create(\Magento\Customer\Api\AddressRepositoryInterface::class); + ->create(AddressRepositoryInterface::class); $this->_customer->setDefaultShipping(2) ->setAddresses([$addressRepository->getById($this->_address->getId())]); $this->_customer = $this->customerRepository->save($this->_customer); @@ -218,19 +232,39 @@ protected function _setCustomerAddressAndSave($unsetId) if ($unsetId) { $shippingAddress->setId(null); } - /** @var \Magento\Customer\Api\AddressRepositoryInterface $addressRepository */ + /** @var AddressRepositoryInterface $addressRepository */ $addressRepository = Bootstrap::getObjectManager() - ->create(\Magento\Customer\Api\AddressRepositoryInterface::class); + ->create(AddressRepositoryInterface::class); $shippingAddress->setSameAsBilling(0) ->setCustomerAddressData($addressRepository->getById($this->_customer->getDefaultBilling())) ->save(); } + /** + * @return array + */ public function unsetAddressIdDataProvider() { return [[true], [false]]; } + /** + * Test to get same as billing flag after change quote customer + */ + public function testSameAsBillingAfterCustomerWesChanged() + { + $shippingAddressId = 2; + $this->_quote->setCustomer($this->_customer); + /** Make different default shipping and default billing addresses */ + $this->_customer->setDefaultShipping($shippingAddressId); + $this->_quote->getShippingAddress()->setCustomerAddressId($shippingAddressId); + /** Emulate to change customer */ + $this->_quote->setOrigData('customer_id', null); + $shippingAddress = $this->_quote->getShippingAddress(); + $shippingAddress->beforeSave(); + $this->assertEquals(false, $this->_quote->getShippingAddress()->getSameAsBilling()); + } + /** * Import customer address to quote address */ @@ -241,13 +275,13 @@ public function testImportCustomerAddressDataWithCustomer() $city = 'TestCity'; $street = 'Street1'; - /** @var \Magento\Customer\Api\Data\AddressInterfaceFactory $addressFactory */ + /** @var AddressInterfaceFactory $addressFactory */ $addressFactory = Bootstrap::getObjectManager()->create( - \Magento\Customer\Api\Data\AddressInterfaceFactory::class + AddressInterfaceFactory::class ); - /** @var \Magento\Customer\Api\AddressRepositoryInterface $addressRepository */ + /** @var AddressRepositoryInterface $addressRepository */ $addressRepository = Bootstrap::getObjectManager()->create( - \Magento\Customer\Api\AddressRepositoryInterface::class + AddressRepositoryInterface::class ); $addressData = $addressFactory->create() ->setCustomerId($customerIdFromFixture) @@ -284,6 +318,9 @@ public function testExportCustomerAddressData() $this->assertEquals($company, $customerAddress->getCompany(), 'Company was exported incorrectly.'); } + /** + * Test to Set the required fields + */ public function testPopulateBeforeSaveData() { /** Preconditions */ @@ -303,9 +340,9 @@ public function testPopulateBeforeSaveData() "Precondition failed: Customer address ID was not set." ); - /** @var \Magento\Customer\Api\Data\AddressInterfaceFactory $addressFactory */ + /** @var AddressInterfaceFactory $addressFactory */ $addressFactory = Bootstrap::getObjectManager()->create( - \Magento\Customer\Api\Data\AddressInterfaceFactory::class + AddressInterfaceFactory::class ); $customerAddressData = $addressFactory->create()->setId($customerAddressId); $this->_address->setCustomerAddressData($customerAddressData); @@ -317,22 +354,26 @@ public function testPopulateBeforeSaveData() } /** - * Tests + * Test to retrieve applied taxes * - * @covers \Magento\Quote\Model\Quote\Address::setAppliedTaxes() - * @covers \Magento\Quote\Model\Quote\Address::getAppliedTaxes() - * @dataProvider dataProvider * @param $taxes * @param $expected + * @covers \Magento\Quote\Model\Quote\Address::setAppliedTaxes() + * @covers \Magento\Quote\Model\Quote\Address::getAppliedTaxes() + * @dataProvider appliedTaxesDataProvider */ public function testAppliedTaxes($taxes, $expected) { $this->_address->setAppliedTaxes($taxes); - $this->assertSame($expected, $this->_address->getAppliedTaxes()); } - public function dataProvider() + /** + * Retrieve applied taxes data provider + * + * @return array + */ + public function appliedTaxesDataProvider() { return [ ['test', 'test'], @@ -340,6 +381,9 @@ public function dataProvider() ]; } + /** + * Test to sate shipping address without region + */ public function testSaveShippingAddressWithEmptyRegionId() { $customerAddress = $this->addressRepository->getById(1); @@ -347,7 +391,7 @@ public function testSaveShippingAddressWithEmptyRegionId() $address = $this->dataProcessor->buildOutputDataArray( $customerAddress, - \Magento\Customer\Api\Data\AddressInterface::class + AddressInterface::class ); $shippingAddress = $this->_quote->getShippingAddress(); diff --git a/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/Listing/CategoryPageViewTest.php b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/Listing/CategoryPageViewTest.php new file mode 100644 index 0000000000000..7f842bf49644e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/Listing/CategoryPageViewTest.php @@ -0,0 +1,109 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Swatches\Block\Product\Renderer\Configurable\Listing; + +use Magento\Swatches\Block\Product\Renderer\Configurable\ProductPageViewTest; +use Magento\Swatches\Block\Product\Renderer\Listing\Configurable; + +/** + * Test class to check configurable product with swatch attributes view behaviour on category page + * + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + */ +class CategoryPageViewTest extends ProductPageViewTest +{ + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->block = $this->layout->createBlock(Configurable::class); + $this->template = 'Magento_Swatches::product/listing/renderer.phtml'; + } + + /** + * @magentoDataFixture Magento/Swatches/_files/configurable_product_visual_swatch_attribute.php + * + * @dataProvider expectedVisualSwatchDataProvider + * + * @param array $expectedConfig + * @param array $expectedSwatchConfig + * @return void + */ + public function testCategoryPageVisualSwatchAttributeView(array $expectedConfig, array $expectedSwatchConfig): void + { + $this->checkProductViewCategoryPage($expectedConfig, $expectedSwatchConfig, ['visual_swatch_attribute']); + } + + /** + * @magentoDataFixture Magento/Swatches/_files/configurable_product_text_swatch_attribute.php + * + * @dataProvider expectedTextSwatchDataProvider + * + * @param array $expectedConfig + * @param array $expectedSwatchConfig + * @return void + */ + public function testCategoryPageTextSwatchAttributeView(array $expectedConfig, array $expectedSwatchConfig): void + { + $this->checkProductViewCategoryPage($expectedConfig, $expectedSwatchConfig, ['text_swatch_attribute']); + } + + /** + * @magentoDataFixture Magento/Swatches/_files/configurable_product_two_attributes.php + * + * @dataProvider expectedTwoAttributesProvider + * + * @param array $expectedConfig + * @param array $expectedSwatchConfig + * @return void + */ + public function testCategoryPageTwoAttributesView(array $expectedConfig, array $expectedSwatchConfig): void + { + $this->checkProductViewCategoryPage( + $expectedConfig, + $expectedSwatchConfig, + ['visual_swatch_attribute', 'text_swatch_attribute'] + ); + } + + /** + * Check configurable product view on category view page + * + * @param array $expectedConfig + * @param array $expectedSwatchConfig + * @param array $attributes + * @return void + */ + private function checkProductViewCategoryPage( + array $expectedConfig, + array $expectedSwatchConfig, + array $attributes + ): void { + $this->setAttributeUsedInProductListing($attributes); + $this->checkProductView($expectedConfig, $expectedSwatchConfig); + } + + /** + * Set used in product listing attributes value to true + * + * @param array $attributeCodes + * @return void + */ + private function setAttributeUsedInProductListing(array $attributeCodes): void + { + foreach ($attributeCodes as $attributeCode) { + $attribute = $this->productAttributeRepository->get($attributeCode); + $attribute->setUsedInProductListing('1'); + $this->productAttributeRepository->save($attribute); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/ProductPageViewTest.php b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/ProductPageViewTest.php new file mode 100644 index 0000000000000..2d016ef48faf5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/ProductPageViewTest.php @@ -0,0 +1,414 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Swatches\Block\Product\Renderer\Configurable; + +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\Swatches\Block\Product\Renderer\Configurable; +use Magento\Swatches\Model\Swatch; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Test class to check configurable product with swatch attributes view behaviour on product page + * + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + */ +class ProductPageViewTest extends TestCase +{ + /** @var ObjectManagerInterface */ + protected $objectManager; + + /** @var Configurable */ + protected $block; + + /** @var string */ + protected $template; + + /** @var ProductAttributeRepositoryInterface */ + protected $productAttributeRepository; + + /** @var LayoutInterface */ + protected $layout; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var Registry */ + private $registry; + + /** @var SerializerInterface */ + private $json; + + /** @var ProductResource */ + private $productResource; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->block = $this->layout->createBlock(Configurable::class); + $this->registry = $this->objectManager->get(Registry::class); + $this->json = $this->objectManager->get(SerializerInterface::class); + $this->productAttributeRepository = $this->objectManager->create(ProductAttributeRepositoryInterface::class); + $this->productResource = $this->objectManager->create(ProductResource::class); + $this->template = Configurable::SWATCH_RENDERER_TEMPLATE; + } + + /** + * @magentoDataFixture Magento/Swatches/_files/configurable_product_text_swatch_attribute.php + * + * @dataProvider expectedTextSwatchDataProvider + * + * @param array $expectedConfig + * @param array $expectedSwatchConfig + * @return void + */ + public function testProductPageTextSwatchAttributeView(array $expectedConfig, array $expectedSwatchConfig): void + { + $this->checkProductView($expectedConfig, $expectedSwatchConfig); + } + + /** + * @return array + */ + public function expectedTextSwatchDataProvider(): array + { + return [ + [ + 'json_config' => [ + 'text_swatch_attribute' => [ + 'label' => 'Text swatch attribute', + 'options' => [ + ['label' => 'Option 3', 'skus' => ['simple_option_3']], + ['label' => 'Option 1', 'skus' => ['simple_option_1']], + ['label' => 'Option 2', 'skus' => ['simple_option_2']], + ], + ], + ], + 'json_swatch_config' => [ + Swatch::SWATCH_INPUT_TYPE_TEXT => [ + [ + 'type' => Swatch::SWATCH_TYPE_TEXTUAL, + 'value' => 'Swatch 3', + 'label' => 'Option 3', + ], + [ + 'type' => Swatch::SWATCH_TYPE_TEXTUAL, + 'value' => 'Swatch 1', + 'label' => 'Option 1', + ], + [ + 'type' => Swatch::SWATCH_TYPE_TEXTUAL, + 'value' => 'Swatch 2', + 'label' => 'Option 2', + ], + 'additional_data' => "{\"swatch_input_type\":\"text\"}", + ], + + ], + ], + ]; + } + + /** + * @magentoDataFixture Magento/Swatches/_files/configurable_product_visual_swatch_attribute.php + * + * @dataProvider expectedVisualSwatchDataProvider + * + * @param array $expectedConfig + * @param array $expectedSwatchConfig + * @return void + */ + public function testProductPageVisualSwatchAttributeView(array $expectedConfig, array $expectedSwatchConfig): void + { + $this->checkProductView($expectedConfig, $expectedSwatchConfig); + } + + /** + * @return array + */ + public function expectedVisualSwatchDataProvider(): array + { + return [ + [ + 'json_config' => [ + 'visual_swatch_attribute' => [ + 'label' => 'Visual swatch attribute', + 'options' => [ + ['label' => 'option 3', 'skus' => ['simple_option_3']], + ['label' => 'option 2', 'skus' => ['simple_option_2']], + ['label' => 'option 1', 'skus' => ['simple_option_1']], + ], + ], + ], + 'json_swatch_config' => [ + Swatch::SWATCH_INPUT_TYPE_VISUAL => [ + [ + 'type' => Swatch::SWATCH_TYPE_VISUAL_COLOR, + 'value' => '#555555', + 'label' => 'option 1', + ], + [ + 'type' => Swatch::SWATCH_TYPE_VISUAL_COLOR, + 'value' => '#aaaaaa', + 'label' => 'option 2', + ], + [ + 'type' => Swatch::SWATCH_TYPE_VISUAL_COLOR, + 'value' => '#ffffff', + 'label' => 'option 3', + ], + 'additional_data' => "{\"swatch_input_type\":\"visual\"}", + ], + ], + ], + ]; + } + + /** + * @magentoDataFixture Magento/Swatches/_files/configurable_product_two_attributes.php + * + * @dataProvider expectedTwoAttributesProvider + * + * @param array $expectedConfig + * @param array $expectedSwatchConfig + * @return void + */ + public function testProductPageTwoAttributesView(array $expectedConfig, array $expectedSwatchConfig): void + { + $this->checkProductView($expectedConfig, $expectedSwatchConfig); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @return array + */ + public function expectedTwoAttributesProvider(): array + { + return [ + [ + 'json_config' => [ + 'visual_swatch_attribute' => [ + 'label' => 'Visual swatch attribute', + 'options' => [ + [ + 'label' => 'option 3', + 'skus' => [ + 'simple_option_3_option_3', + 'simple_option_1_option_3', + 'simple_option_2_option_3', + ], + ], + [ + 'label' => 'option 2', + 'skus' => [ + 'simple_option_3_option_2', + 'simple_option_1_option_2', + 'simple_option_2_option_2', + ], + ], + [ + 'label' => 'option 1', + 'skus' => [ + 'simple_option_3_option_1', + 'simple_option_1_option_1', + 'simple_option_2_option_1', + ], + ], + ], + ], + 'text_swatch_attribute' => [ + 'label' => 'Text swatch attribute', + 'options' => [ + [ + 'label' => 'Option 3', + 'skus' => [ + 'simple_option_3_option_1', + 'simple_option_3_option_2', + 'simple_option_3_option_3', + ], + ], + [ + 'label' => 'Option 2', + 'skus' => [ + 'simple_option_2_option_1', + 'simple_option_2_option_2', + 'simple_option_2_option_3', + ], + ], + [ + 'label' => 'Option 1', + 'skus' => [ + 'simple_option_1_option_1', + 'simple_option_1_option_2', + 'simple_option_1_option_3', + ], + ], + ], + ], + + ], + 'json_swatch_config' => [ + Swatch::SWATCH_INPUT_TYPE_VISUAL => [ + [ + 'type' => Swatch::SWATCH_TYPE_VISUAL_COLOR, + 'value' => '#555555', + 'label' => 'option 1', + ], + [ + 'type' => Swatch::SWATCH_TYPE_VISUAL_COLOR, + 'value' => '#aaaaaa', + 'label' => 'option 2', + ], + [ + 'type' => Swatch::SWATCH_TYPE_VISUAL_COLOR, + 'value' => '#ffffff', + 'label' => 'option 3', + ], + 'additional_data' => "{\"swatch_input_type\":\"visual\"}", + ], + Swatch::SWATCH_INPUT_TYPE_TEXT => [ + [ + 'type' => Swatch::SWATCH_TYPE_TEXTUAL, + 'value' => 'Swatch 3', + 'label' => 'Option 3', + ], + [ + 'type' => Swatch::SWATCH_TYPE_TEXTUAL, + 'value' => 'Swatch 1', + 'label' => 'Option 1', + ], + [ + 'type' => Swatch::SWATCH_TYPE_TEXTUAL, + 'value' => 'Swatch 2', + 'label' => 'Option 2', + ], + 'additional_data' => "{\"swatch_input_type\":\"text\"}", + ], + ], + ], + ]; + } + + /** + * Check configurable product view + * + * @param $expectedConfig + * @param $expectedSwatchConfig + * @return void + */ + protected function checkProductView($expectedConfig, $expectedSwatchConfig): void + { + $actualConfig = $this->generateBlockJsonConfigData(); + $this->checkResultIsNotEmpty($actualConfig); + $this->assertConfig($actualConfig['json_config'], $expectedConfig); + $this->assertSwatchConfig($actualConfig['json_swatch_config'], $expectedSwatchConfig); + } + + /** + * Generate block config data + * + * @return array + */ + + private function generateBlockJsonConfigData(): array + { + $product = $this->productRepository->get('configurable'); + $this->block->setProduct($product); + $this->block->setTemplate($this->template); + $jsonConfig = $this->json->unserialize($this->block->getJsonConfig())['attributes'] ?? []; + $jsonSwatchConfig = $this->json->unserialize($this->block->getJsonSwatchConfig()); + + return ['json_config' => $jsonConfig, 'json_swatch_config' => $jsonSwatchConfig]; + } + + /** + * Assert that correct data was generated + * + * @param array $actualData + * @param array $expectedData + * @return void + */ + private function assertSwatchConfig(array $actualData, array $expectedData): void + { + foreach ($actualData as $actualDataItem) { + $currentType = $this->json->unserialize($actualDataItem['additional_data'])['swatch_input_type'] ?? null; + $this->assertNotNull($currentType); + $this->assertEquals($expectedData[$currentType]['additional_data'], $actualDataItem['additional_data']); + unset($actualDataItem['additional_data']); + foreach ($actualDataItem as $item) { + $this->assertContains($item, $expectedData[$currentType]); + } + } + } + + /** + * Assert that correct swatch data was generated + * + * @param array $actualData + * @param array $expectedData + * @return void + */ + private function assertConfig(array $actualData, array $expectedData): void + { + foreach ($actualData as $actualDataItem) { + $expectedItem = $expectedData[$actualDataItem['code']]; + $this->assertEquals($expectedItem['label'], $actualDataItem['label']); + $this->checkOptions($actualDataItem, $expectedItem); + } + } + + /** + * Check result is not not empty + * + * @param array $result + */ + private function checkResultIsNotEmpty(array $result): void + { + foreach ($result as $item) { + $this->assertNotEmpty($item); + } + } + + /** + * Check attribute options + * + * @param array $actualDataItem + * @param array $expectedItem + * @return void + */ + private function checkOptions(array $actualDataItem, array $expectedItem): void + { + foreach ($expectedItem['options'] as $expectedOption) { + $expectedSkus = array_values($expectedOption['skus']); + $expectedIds = array_values($this->productResource->getProductsIdsBySkus($expectedSkus)); + foreach ($actualDataItem['options'] as $option) { + if ($option['label'] === $expectedOption['label']) { + $this->assertEquals( + sort($expectedIds), + sort($option['products']), + 'Wrong product linked as option' + ); + } + } + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_text_swatch_attribute.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_text_swatch_attribute.php new file mode 100644 index 0000000000000..b5586acdfeebf --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_text_swatch_attribute.php @@ -0,0 +1,93 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductExtensionFactory; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type as ProductType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\ConfigurableProduct\Helper\Product\Options\Factory; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/product_text_swatch_attribute.php'; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductAttributeRepositoryInterface $productAttributeRepository */ +$productAttributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +$attribute = $productAttributeRepository->get('text_swatch_attribute'); +$options = $attribute->getOptions(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsite = $websiteRepository->get('base'); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$attributeValues = []; +$associatedProductIds = []; +$rootCategoryId = $baseWebsite->getDefaultStore()->getRootCategoryId(); +array_shift($options); + +foreach ($options as $option) { + $product = $productFactory->create(); + $product->setTypeId(ProductType::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsite->getId()]) + ->setName('Configurable Option ' . $option->getLabel()) + ->setSku(strtolower(str_replace(' ', '_', 'simple ' . $option->getLabel()))) + ->setPrice(150) + ->setTextSwatchAttribute($option->getValue()) + ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([$rootCategoryId]) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); + $product = $productRepository->save($product); + + $attributeValues[] = [ + 'label' => 'test', + 'attribute_id' => $attribute->getId(), + 'value_index' => $option->getValue(), + ]; + $associatedProductIds[] = $product->getId(); +} +/** @var Factory $optionsFactory */ +$optionsFactory = $objectManager->get(Factory::class); +$configurableAttributesData = [ + [ + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getStoreLabel(), + 'position' => '0', + 'values' => $attributeValues, + ], +]; +$configurableOptions = $optionsFactory->create($configurableAttributesData); + +$product = $productFactory->create(); +/** @var ProductExtensionFactory $extensionAttributesFactory */ +$extensionAttributesFactory = $objectManager->get(ProductExtensionFactory::class); +$extensionConfigurableAttributes = $product->getExtensionAttributes() ?: $extensionAttributesFactory->create(); +$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions); +$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds); +$product->setExtensionAttributes($extensionConfigurableAttributes); + +$product->setTypeId(Configurable::TYPE_CODE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsite->getId()]) + ->setName('Configurable Product') + ->setSku('configurable') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([$rootCategoryId]) + ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_text_swatch_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_text_swatch_attribute_rollback.php new file mode 100644 index 0000000000000..f5c91e255e1a3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_text_swatch_attribute_rollback.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +/** @var ProductAttributeRepositoryInterface $productAttributeRepository */ +$productAttributeRepository = $objectManager->get(ProductAttributeRepositoryInterface::class); +$attribute = $productAttributeRepository->get('text_swatch_attribute'); +$options = $attribute->getOptions(); +array_shift($options); +$productsArray = []; +foreach ($options as $option) { + $productsArray [] = strtolower(str_replace(' ', '_', 'simple ' . $option->getLabel())); +} +$productsArray[] = 'configurable'; +foreach ($productsArray as $sku) { + try { + $productRepository->deleteById($sku); + } catch (NoSuchEntityException $e) { + //Product already removed + } +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/product_text_swatch_attribute_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_two_attributes.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_two_attributes.php new file mode 100644 index 0000000000000..b0d1948ac8be2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_two_attributes.php @@ -0,0 +1,120 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductExtensionFactory; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type as ProductType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\ConfigurableProduct\Helper\Product\Options\Factory; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/product_text_swatch_attribute.php'; +require __DIR__ . '/product_visual_swatch_attribute.php'; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductAttributeRepositoryInterface $productAttributeRepository */ +$productAttributeRepository = $objectManager->get(ProductAttributeRepositoryInterface::class); +$attribute = $productAttributeRepository->get('text_swatch_attribute'); +$secondAttribute = $productAttributeRepository->get('visual_swatch_attribute'); +$options = $attribute->getOptions(); +$secondAttributeOptions = $secondAttribute->getOptions(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsite = $websiteRepository->get('base'); +/** @var ProductAttributeRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +$attributeValues = []; +$secondAttributeValues = []; +$associatedProductIds = []; +$associatedProductIdsViaSecondAttribute = []; +$attributeSetId = $installer->getAttributeSetId(Product::ENTITY, 'Default'); +$productFactory = $objectManager->get(ProductFactory::class); +$rootCategoryId = $baseWebsite->getDefaultStore()->getRootCategoryId(); +array_shift($options); +array_shift($secondAttributeOptions); + +foreach ($options as $option) { + foreach ($secondAttributeOptions as $secondAttrOption) { + $product = $productFactory->create(); + $product->setTypeId(ProductType::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsite->getId()]) + ->setName('Configurable Option ' . $option->getLabel()) + ->setSku( + strtolower( + str_replace(' ', '_', 'simple ' . $option->getLabel() . '_' . $secondAttrOption->getLabel()) + ) + ) + ->setPrice(150) + ->setTextSwatchAttribute($option->getValue()) + ->setVisualSwatchAttribute($secondAttrOption->getValue()) + ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([$rootCategoryId]) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); + $product = $productRepository->save($product, true); + $associatedProductIds[] = $product->getId(); + } + + $attributeValues[] = [ + 'label' => 'test1', + 'attribute_id' => $attribute->getId(), + 'value_index' => $option->getValue(), + ]; +} +foreach ($secondAttributeOptions as $secondAttrOption) { + $secondAttributeValues[] = [ + 'label' => 'test2', + 'attribute_id' => $secondAttribute->getId(), + 'value_index' => $secondAttrOption->getValue(), + ]; +} + +$allAttributes = [$attribute, $secondAttribute]; +$optionsFactory = $objectManager->get(Factory::class); + +foreach ($allAttributes as $attribute) { + $configurableAttributesData[] = + [ + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getStoreLabel(), + 'position' => '0', + 'values' => $attribute->getAttributeCode() === 'text_swatch_attribute' + ? $attributeValues + : $secondAttributeValues, + ]; + +} + +$configurableOptions = $optionsFactory->create($configurableAttributesData); +$product = $productFactory->create(); +/** @var ProductExtensionFactory $extensionAttributesFactory */ +$extensionAttributesFactory = $objectManager->get(ProductExtensionFactory::class); +$extensionConfigurableAttributes = $product->getExtensionAttributes() ?: $extensionAttributesFactory->create(); +$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions); +$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds); +$product->setExtensionAttributes($extensionConfigurableAttributes); + +$product->setTypeId(Configurable::TYPE_CODE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsite->getId()]) + ->setName('Configurable Product') + ->setSku('configurable') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([$rootCategoryId]) + ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_two_attributes_rollback.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_two_attributes_rollback.php new file mode 100644 index 0000000000000..c92330e49688b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_two_attributes_rollback.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +/** @var ProductAttributeRepositoryInterface $productAttributeRepository */ +$productAttributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +$options = $productAttributeRepository->get('text_swatch_attribute')->getOptions(); +$secondAttributeOptions = $productAttributeRepository->get('visual_swatch_attribute')->getOptions(); +array_shift($options); +array_shift($secondAttributeOptions); +$productsArray = []; + +foreach ($options as $option) { + foreach ($secondAttributeOptions as $secondAttrOption) { + $productsArray[] = strtolower( + str_replace(' ', '_', 'simple ' . $option->getLabel() . '_' . $secondAttrOption->getLabel()) + ); + } +} + +$productsArray[] = 'configurable'; +foreach ($productsArray as $sku) { + try { + $productRepository->deleteById($sku); + } catch (NoSuchEntityException $e) { + //Product already removed + } +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/product_text_swatch_attribute_rollback.php'; +require __DIR__ . '/product_visual_swatch_attribute_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_visual_swatch_attribute.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_visual_swatch_attribute.php new file mode 100644 index 0000000000000..c47be2717f5a8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_visual_swatch_attribute.php @@ -0,0 +1,93 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductExtensionFactory; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type as ProductType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\ConfigurableProduct\Helper\Product\Options\Factory; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/product_visual_swatch_attribute.php'; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductAttributeRepositoryInterface $productAttributeRepository */ +$productAttributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +$attribute = $productAttributeRepository->get('visual_swatch_attribute'); +$options = $attribute->getOptions(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsite = $websiteRepository->get('base'); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$attributeValues = []; +$associatedProductIds = []; +$rootCategoryId = $baseWebsite->getDefaultStore()->getRootCategoryId(); +array_shift($options); + +foreach ($options as $option) { + $product = $productFactory->create(); + $product->setTypeId(ProductType::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsite->getId()]) + ->setName('Configurable Option ' . $option->getLabel()) + ->setSku(strtolower(str_replace(' ', '_', 'simple ' . $option->getLabel()))) + ->setPrice(150) + ->setVisualSwatchAttribute($option->getValue()) + ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([$rootCategoryId]) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); + $product = $productRepository->save($product); + + $attributeValues[] = [ + 'label' => 'test', + 'attribute_id' => $attribute->getId(), + 'value_index' => $option->getValue(), + ]; + $associatedProductIds[] = $product->getId(); +} +/** @var Factory $optionsFactory */ +$optionsFactory = $objectManager->get(Factory::class); +$configurableAttributesData = [ + [ + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getStoreLabel(), + 'position' => '0', + 'values' => $attributeValues, + ], +]; +$configurableOptions = $optionsFactory->create($configurableAttributesData); + +$product = $productFactory->create(); +/** @var ProductExtensionFactory $extensionAttributesFactory */ +$extensionAttributesFactory = $objectManager->get(ProductExtensionFactory::class); +$extensionConfigurableAttributes = $product->getExtensionAttributes() ?: $extensionAttributesFactory->create(); +$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions); +$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds); +$product->setExtensionAttributes($extensionConfigurableAttributes); + +$product->setTypeId(Configurable::TYPE_CODE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsite->getId()]) + ->setName('Configurable Product') + ->setSku('configurable') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([$rootCategoryId]) + ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_visual_swatch_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_visual_swatch_attribute_rollback.php new file mode 100644 index 0000000000000..22ea728f36327 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/configurable_product_visual_swatch_attribute_rollback.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +/** @var ProductAttributeRepositoryInterface $productAttributeRepository */ +$productAttributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +$attribute = $productAttributeRepository->get('visual_swatch_attribute'); +$options = $attribute->getOptions(); +array_shift($options); +$productsArray = []; + +foreach ($options as $option) { + $productsArray [] = strtolower(str_replace(' ', '_', 'simple ' . $option->getLabel())); +} + +$productsArray[] = 'configurable'; +foreach ($productsArray as $sku) { + try { + $productRepository->deleteById($sku); + } catch (NoSuchEntityException $e) { + //Product already removed + } +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/product_visual_swatch_attribute_rollback.php'; diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/view/shipping.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/view/shipping.test.js index 46d9e1974bdb7..f6f4927aaeda2 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/view/shipping.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/view/shipping.test.js @@ -12,7 +12,7 @@ require.config({ } }); -define(['squire', 'ko', 'jquery', 'uiRegistry', 'jquery/validate'], function (Squire, ko, $, registry) { +define(['squire', 'ko', 'jquery', 'jquery/validate'], function (Squire, ko, $) { 'use strict'; var injector = new Squire(), @@ -20,6 +20,16 @@ define(['squire', 'ko', 'jquery', 'uiRegistry', 'jquery/validate'], function (Sq openModal: jasmine.createSpy(), closeModal: jasmine.createSpy() }, + country = { + /** Stub */ + on: function () {}, + + /** Stub */ + get: function () {}, + + /** Stub */ + set: function () {} + }, mocks = { 'Magento_Customer/js/model/customer': { isLoggedIn: ko.observable() @@ -28,17 +38,7 @@ define(['squire', 'ko', 'jquery', 'uiRegistry', 'jquery/validate'], function (Sq 'Magento_Checkout/js/model/address-converter': jasmine.createSpy(), 'Magento_Checkout/js/model/quote': { isVirtual: jasmine.createSpy(), - shippingMethod: ko.observable(), - - /** - * Stub - */ - shippingAddress: function () { - - return { - 'countryId': 'AD' - }; - } + shippingMethod: ko.observable() }, 'Magento_Checkout/js/action/create-shipping-address': jasmine.createSpy().and.returnValue( jasmine.createSpyObj('newShippingAddress', ['getKey']) @@ -62,7 +62,18 @@ define(['squire', 'ko', 'jquery', 'uiRegistry', 'jquery/validate'], function (Sq 'checkoutData', ['setSelectedShippingAddress', 'setNewCustomerShippingAddress', 'setSelectedShippingRate'] ), - 'Magento_Ui/js/lib/registry/registry': registry, + 'Magento_Ui/js/lib/registry/registry': { + async: jasmine.createSpy().and.returnValue(function () {}), + create: jasmine.createSpy(), + set: jasmine.createSpy(), + get: jasmine.createSpy().and.callFake(function (query) { + if (query === 'test.shippingAddress.shipping-address-fieldset.country_id') { + return country; + } else if (query === 'checkout.errors') { + return {}; + } + }) + }, 'Magento_Checkout/js/model/shipping-rate-service': jasmine.createSpy() }, obj; @@ -73,7 +84,6 @@ define(['squire', 'ko', 'jquery', 'uiRegistry', 'jquery/validate'], function (Sq obj = new Constr({ provider: 'provName', name: '', - parentName: 'test', index: '', popUpForm: { options: { @@ -174,16 +184,6 @@ define(['squire', 'ko', 'jquery', 'uiRegistry', 'jquery/validate'], function (Sq describe('"validateShippingInformation" method', function () { it('Check method call on negative cases.', function () { - /* jscs:disable */ - var country = { - on: function () {}, - get: function () {}, - set: function () {} - }; - /* jscs:enable */ - - registry.set('test.shippingAddress.shipping-address-fieldset.country_id', country); - registry.set('checkout.errors', {}); obj.source = { get: jasmine.createSpy().and.returnValue(true), set: jasmine.createSpy(), @@ -199,20 +199,10 @@ define(['squire', 'ko', 'jquery', 'uiRegistry', 'jquery/validate'], function (Sq expect(obj.validateShippingInformation()).toBeFalsy(); }); it('Check method call on positive case.', function () { - /* jscs:disable */ - var country = { - on: function () {}, - get: function () {}, - set: function () {} - }; - /* jscs:enable */ - $('body').append('<form data-role="email-with-possible-login">' + '<input type="text" name="username" />' + '</form>'); - registry.set('test.shippingAddress.shipping-address-fieldset.country_id', country); - registry.set('checkout.errors', {}); obj.source = { get: jasmine.createSpy().and.returnValue(true), set: jasmine.createSpy(), diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/ConfigurableProduct/view/frontend/js/configurable.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/ConfigurableProduct/view/frontend/js/configurable.test.js new file mode 100644 index 0000000000000..21492b20b779c --- /dev/null +++ b/dev/tests/js/jasmine/tests/app/code/Magento/ConfigurableProduct/view/frontend/js/configurable.test.js @@ -0,0 +1,69 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'jquery', + 'Magento_ConfigurableProduct/js/configurable' +], function ($, Configurable) { + 'use strict'; + + var widget, + option = '<select name=\'super_attribute[93]\'' + + 'data-selector=\'super_attribute[93]\'' + + 'data-validate=\'{required:true}\'' + + 'id=\'attribute93\'' + + 'class=\'super-attribute-select\'>' + + '<option value=\'\'></option>' + + '</select>', + selectElement = $(option); + + beforeEach(function () { + widget = new Configurable(); + widget.options = { + spConfig: { + chooseText: 'Chose an Option...', + attributes: + { + 'size': { + options: [ + { + id: '2', + value: '2' + }, + { + id: 3, + value: 'red' + + } + ] + } + }, + prices: { + finalPrice: { + amount: 12 + } + } + }, + values: { + } + }; + }); + + describe('Magento_ConfigurableProduct/js/configurable', function () { + + it('check if attribute value is possible to be set as configurable option', function () { + expect($.mage.configurable).toBeDefined(); + widget._parseQueryParams('size=2'); + expect(widget.options.values.size).toBe('2'); + }); + + it('check that attribute value is not set if provided option does not exists', function () { + expect($.mage.configurable).toBeDefined(); + widget._parseQueryParams('size=10'); + widget._fillSelect(selectElement[0]); + expect(widget.options.values.size).toBe(undefined); + }); + }); +}); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Signifyd/frontend/js/Fingerprint.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Signifyd/frontend/js/Fingerprint.test.js index 0be178c5a31f0..a9d9fe8d08047 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Signifyd/frontend/js/Fingerprint.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Signifyd/frontend/js/Fingerprint.test.js @@ -10,6 +10,16 @@ define([ /*eslint max-nested-callbacks: ["error", 5]*/ describe('Signifyd device fingerprint client script', function () { + var originalTimeout; + + beforeEach(function () { + originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; + jasmine.DEFAULT_TIMEOUT_INTERVAL = 12000; + }); + + afterEach(function () { + jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; + }); it('SIGNIFYD_GLOBAL object initialization check', function (done) { var script = document.createElement('script'); @@ -32,7 +42,6 @@ define([ expect(signifyd.scriptTagHasLoaded()).toBe(true); done(); }, 10000); - - }, 12000); + }); }); }); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/form.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/form.test.js index fa149f285c0e3..5726184df2103 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/form.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/form.test.js @@ -3,30 +3,50 @@ * See COPYING.txt for license details. */ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + define([ - 'underscore', - 'uiRegistry', - 'Magento_Ui/js/form/form' -], function (_, registry, Constr) { + 'squire' +], function (Squire) { 'use strict'; describe('Magento_Ui/js/form/form', function () { - - var obj = new Constr({ - provider: 'provName', - name: '', - index: '' - }); - - registry.set('provName', { - /** Stub */ - on: function () {}, - - /** Stub */ - get: function () {}, - - /** Stub */ - set: function () {} + var injector = new Squire(), + mocks = { + 'Magento_Ui/js/lib/registry/registry': { + /** Method stub. */ + get: function () { + return { + get: jasmine.createSpy(), + set: jasmine.createSpy() + }; + }, + options: jasmine.createSpy(), + create: jasmine.createSpy(), + set: jasmine.createSpy(), + async: jasmine.createSpy() + } + }, + obj, + dataScope = 'dataScope'; + + beforeEach(function (done) { + injector.mock(mocks); + injector.require([ + 'Magento_Ui/js/form/form' + ], function (Constr) { + obj = new Constr({ + provider: 'provName', + name: '', + index: '', + dataScope: dataScope + }); + + done(); + }); }); describe('"initAdapter" method', function () { diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/DataProvider/FilterPool.php b/lib/internal/Magento/Framework/View/Element/UiComponent/DataProvider/FilterPool.php index 24ea542649875..2c0b16f27df8b 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/DataProvider/FilterPool.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/DataProvider/FilterPool.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Framework\View\Element\UiComponent\DataProvider; @@ -10,19 +11,19 @@ use Magento\Framework\Api\Search\SearchCriteriaInterface; /** - * Class FilterPool + * Filter poll apply filters from search criteria * * @api */ class FilterPool { /** - * @var array + * @var FilterApplierInterface[] */ protected $appliers; /** - * @param array $appliers + * @param FilterApplierInterface[] $appliers */ public function __construct(array $appliers = []) { @@ -30,6 +31,8 @@ public function __construct(array $appliers = []) } /** + * Apply filters from search criteria + * * @param Collection $collection * @param SearchCriteriaInterface $criteria * @return void @@ -38,12 +41,7 @@ public function applyFilters(Collection $collection, SearchCriteriaInterface $cr { foreach ($criteria->getFilterGroups() as $filterGroup) { foreach ($filterGroup->getFilters() as $filter) { - /** @var $filterApplier FilterApplierInterface*/ - if (isset($this->appliers[$filter->getConditionType()])) { - $filterApplier = $this->appliers[$filter->getConditionType()]; - } else { - $filterApplier = $this->appliers['regular']; - } + $filterApplier = $this->appliers[$filter->getConditionType()] ?? $this->appliers['regular']; $filterApplier->apply($collection, $filter); } }