From 6421156ab897be3d2d5bb3fd884f2002b7ff3d01 Mon Sep 17 00:00:00 2001 From: Tom Erskine Date: Thu, 10 Jan 2019 11:54:18 -0600 Subject: [PATCH 001/231] MSI test MSI-1949 --- ...AfterFullInvoiceAndShipmentInAdminTest.xml | 191 ++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml diff --git a/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml b/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml new file mode 100644 index 000000000000..0a493391bcca --- /dev/null +++ b/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml @@ -0,0 +1,191 @@ + + + + + + + + + <description value="Credit memo created for whole order with Simple product on Default stock after full invoice and shipment in Admin"/> + <testCaseId value="MSI-1949"/> + <severity value="BLOCKER"/> + <group value="msi"/> + <group value="multi_mode"/> + </annotations> + + <before> + <magentoCLI stepKey="enableStockManagement" command="config:set cataloginventory/item_options/manage_stock 1"/> + <createData entity="MsiCustomer1" stepKey="createCustomer1"/> + <createData entity="BasicMsiStockWithMainWebsite1" stepKey="createStock1"/> + <createData entity="FullSource1" stepKey="createSource1"/> + <createData entity="SourceStockLinked1" stepKey="linkSourceStock1"> + <requiredEntity createDataKey="createStock1"/> + <requiredEntity createDataKey="createSource1"/> + </createData> + <createData entity="SimpleSubCategory" stepKey="simpleCategory1"/> + <createData entity="SimpleMsiProduct" stepKey="simpleProduct1"> + <!--<requiredEntity createDataKey="simpleCategory1"/>--> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <waitForPageLoad stepKey="waitForDashboardLoad"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logoutOfAdmin1"/> + <deleteData createDataKey="createCustomer1" stepKey="deleteCustomer"/> + <deleteData createDataKey="createStock1" stepKey="deleteStock"/> + <deleteData createDataKey="simpleCategory1" stepKey="deleteCategory"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct"/> + </after> + + <!--Admin - Assign Default Stock to Main Website--> + <comment userInput="Assign Default Stock to Main Website " stepKey="assignDefaultStockToMainWebsiteComment"/> + <amOnPage url="{{AdminManageStockPage.url}}" stepKey="navigateToStockListPageToAssignDefaultStockToMainWebsite"/> + <waitForPageLoad time="30" stepKey="waitForStockListPageLoad"/> + <actionGroup ref="searchAdminDataGridByKeyword" stepKey="searchDefaultStockByName"> + <argument name="keyword" value="_defaultStock.name"/> + </actionGroup> + <click selector="{{AdminGridRow.editByValue(_defaultStock.name)}}" stepKey="clickEditDefaultStock"/> + <waitForPageLoad time="30" stepKey="waitFroDefaultStockEditPageLoad"/> + <selectOption selector="{{AdminEditStockSalesChannelsSection.websites}}" userInput="Main Website" stepKey="selectDefaultWebsiteAsSalesChannelForDefaultStock"/> + <click selector="{{AdminGridMainControls.saveAndContinue}}" stepKey="saveDefaultStock"/> + + <comment userInput="Assign category to created simple product." stepKey="assignCategoryToProductComment"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForEditProduct"/> + <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findSimpleProductBySku"> + <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> + <argument name="value" value="$$simpleProduct1.sku$$"/> + </actionGroup> + <click selector="{{AdminGridColumnsControls.columns}}" stepKey="selectColumns"/> + <click selector="{{AdminGridColumnsControls.reset}}" stepKey="clickOnResetToRestoreDefaultColumns"/> + <click selector="{{AdminProductGridSection.productGridXRowYColumnButton('1', '2')}}" stepKey="openProductEditPage"/> + <scrollToTopOfPage stepKey="scrollToTopOfPage1"/> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$simpleCategory1.name$$]" requiredAction="true" stepKey="searchAndSelectCategory"/> + <fillField selector="{{AdminProductSourcesGrid.rowQty('0')}}" userInput="1000" stepKey="fillSourceQtyField"/> + <actionGroup ref="AdminFormSaveAndClose" stepKey="saveAndCloseCreatedSimpleProduct"/> + + <!--Login To storefront as Customer--> + <comment userInput="Login to storefront as customer." stepKey="loginToStorefrontComment"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefront"> + <argument name="Customer" value="$$createCustomer1$$"/> + </actionGroup> + + <!--Purchase product once logged in--> + <comment userInput="Purchase 10 simple product" stepKey="purchaseSimpleProductComment"/> + <amOnPage url="{{StorefrontCategoryPage.url($$simpleCategory1.name$$)}}" stepKey="navigateToCategoryPage"/> + <!--<actionGroup ref="StorefrontAddCategoryProductToCartWithQuantityActionGroup" stepKey="addSimpleProductToCart">--> + <!--<argument name="product" value="$$simpleProduct1$$"/>--> + <!--<argument name="quantity" value="2"/>--> + <!--<argument name="checkQuantity" value="1"/>--> + <!--</actionGroup>--> + <!--Action Group StorefrontAddCategoryProductToCartWithQuantityActionGroup explicitly included--> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($$simpleProduct1.name$$)}}" stepKey="moveMouseOverProduct" /> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$simpleProduct1.name$$)}}" stepKey="clickAddToCart" /> + <!-- @TODO: Use general message selector after MQE-694 is fixed --> + <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart($$simpleProduct1.name$$)}}" time="30" stepKey="assertMessage"/> + <waitForText userInput="1" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> + <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> + <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForViewAndEditCartVisible"/> + <clearField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" stepKey="clearField"/> + <wait time="10" stepKey="WaitForClear"/> + <fillField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" userInput="2" stepKey="setProductQtyToFiftyInMiniCart"/> + <click selector="{{StorefrontMinicartSection.itemQuantityUpdate($$simpleProduct1.name$$)}}" stepKey="updateQtyInMiniCart"/> + <!--End Action Group StorefrontAddCategoryProductToCartWithQuantityActionGroup--> + <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForPaymentSelectionPageLoad"/> + <click selector=".continue" stepKey="clickOnNext1"/> + <waitForPageLoad stepKey="waitForPageLoad19"/> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyOrderPayment"/> + <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButtonVisible"/> + <see selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{US_Address_TX.street[0]}}" stepKey="chooseBillingAddress"/> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="placeOrder"/> + <waitForPageLoad stepKey="waitUntilOrderPlaced"/> + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> + <see selector="{{CheckoutSuccessMainSection.success}}" userInput="Your order number is:" stepKey="checkOrderPlaceSuccessMessage"/> + + <!--Admin Area Check ordered quantity--> + <comment userInput="Admin - Check ordered quantity" stepKey="AdminCheckOrderedQuantity"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderListPage"/> + <waitForLoadingMaskToDisappear stepKey="waitOrderListPageLoad"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrder"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearch1"/> + <waitForLoadingMaskToDisappear stepKey="waitFilteredOrderListPageLoad"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="navigateToOrderViewPage"/> + <waitForElementVisible selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" stepKey="waitForViewOrderedQuantity"/> + <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Ordered 12" stepKey="orderedQuantity"/> + + <!--Admin Area Check source quantity and salable quantity--> + <comment userInput="Admin - Check Source quantity and salable quantity after order placed" stepKey="AdminCheckQuantityAfterOrderPlaced"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterPlaceOrder"/> + <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterPlaceOrder"> + <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> + <argument name="value" value="$$simpleProduct1.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="1000" stepKey="checkProductSourceQtyAfterPlaceOrder"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="988" stepKey="checkSalableQtyAfterPlaceOrder"/> + + <!--Admin Area Process Shipping--> + <comment userInput="Admin - Ship order" stepKey="AdminShipOrder"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToCreateShipment"/> + <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMask"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" + stepKey="searchOrderNum"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearch2"/> + <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMask"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> + <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShip"/> + <waitForLoadingMaskToDisappear stepKey="waitForShipLoadingMask"/> + <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" stepKey="submitShipment"/> + <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The shipment has been created." stepKey="seeShipmentCreateSuccess"/> + + <!--Admin Area Process Full Invoice--> + <comment userInput="Admin - Process invoice for full order" stepKey="InvoiceFullOrder"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToProcessInvoice"/> + <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMaskInvoice"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" + stepKey="searchOrderNumInvoice"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchInvoice"/> + <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMask1"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRowInvoice"/> + <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoice"/> + <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> + <waitForPageLoad time="60" stepKey="waitForPageLoad21"/> + <scrollToTopOfPage stepKey="scrollToTopMessage"/> + <waitForPageLoad stepKey="waitForPageLoad22"/> + <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="checkSuccessMessage"/> + + + <!--Admin Area Create Full Credit Memo--> + <comment userInput="Admin - Create credit memo for full order" stepKey="AdminCreateCreditMemoFullOrder"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToCreateCreditMemo"/> + <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMaskCreditMemo"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" + stepKey="searchOrderNumCreditMemo"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchCreditMemo"/> + <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMaskCreditMemo"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRowCreditMemo"/> + <click selector="{{AdminOrderDetailsMainActionsSection.creditMemo}}" stepKey="clickCreditMemo"/> + <click selector="{{AdminCreditMemoItemsSection.itemReturnToStock('1')}}" stepKey="returnToStockCheckbox"/> + <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickSubmit"/> + + + <!--Admin Area Check quantities after Credit Memo--> + <comment userInput="Admin - Check Source quantity and salable quantity after credit memo" stepKey="AdminCheckQuantityAfterCreditMemo"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterCreditMemo"/> + <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterCreditMemo"> + <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> + <argument name="value" value="$$simpleProduct1.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="1000" stepKey="checkProductSourceQtyAfterCreditMemo"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="1000" stepKey="checkSalableQtyAfterCreditMemo"/> + + </test> +</tests> + + + From 62bb1f14412622309d1f9943b4e9f9c466d53482 Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Thu, 10 Jan 2019 12:15:52 -0600 Subject: [PATCH 002/231] MSI-1949: Removing unnecessary waits --- ...oductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml b/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml index 0a493391bcca..d3a42ea56615 100644 --- a/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml @@ -92,7 +92,6 @@ <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForViewAndEditCartVisible"/> <clearField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" stepKey="clearField"/> - <wait time="10" stepKey="WaitForClear"/> <fillField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" userInput="2" stepKey="setProductQtyToFiftyInMiniCart"/> <click selector="{{StorefrontMinicartSection.itemQuantityUpdate($$simpleProduct1.name$$)}}" stepKey="updateQtyInMiniCart"/> <!--End Action Group StorefrontAddCategoryProductToCartWithQuantityActionGroup--> From 99473caa9d02ceabf5d3e702fd3cf1a87fbf4215 Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Thu, 10 Jan 2019 14:03:18 -0600 Subject: [PATCH 003/231] magento-engcom/msi#2.3-develop-MFTF-CreditMemo MSI-1949 - Credit Memo test --- ...nDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename InventoryAdminUi/Test/Mftf/Test/{CreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml => AdminCreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml} (98%) diff --git a/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml similarity index 98% rename from InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml rename to InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml index d3a42ea56615..7466a567842e 100644 --- a/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml @@ -92,7 +92,7 @@ <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForViewAndEditCartVisible"/> <clearField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" stepKey="clearField"/> - <fillField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" userInput="2" stepKey="setProductQtyToFiftyInMiniCart"/> + <fillField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" userInput="10" stepKey="setProductQtyToFiftyInMiniCart"/> <click selector="{{StorefrontMinicartSection.itemQuantityUpdate($$simpleProduct1.name$$)}}" stepKey="updateQtyInMiniCart"/> <!--End Action Group StorefrontAddCategoryProductToCartWithQuantityActionGroup--> <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> @@ -116,7 +116,7 @@ <waitForLoadingMaskToDisappear stepKey="waitFilteredOrderListPageLoad"/> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="navigateToOrderViewPage"/> <waitForElementVisible selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" stepKey="waitForViewOrderedQuantity"/> - <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Ordered 12" stepKey="orderedQuantity"/> + <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Ordered 10" stepKey="orderedQuantity"/> <!--Admin Area Check source quantity and salable quantity--> <comment userInput="Admin - Check Source quantity and salable quantity after order placed" stepKey="AdminCheckQuantityAfterOrderPlaced"/> @@ -126,7 +126,7 @@ <argument name="value" value="$$simpleProduct1.sku$$"/> </actionGroup> <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="1000" stepKey="checkProductSourceQtyAfterPlaceOrder"/> - <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="988" stepKey="checkSalableQtyAfterPlaceOrder"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="990" stepKey="checkSalableQtyAfterPlaceOrder"/> <!--Admin Area Process Shipping--> <comment userInput="Admin - Ship order" stepKey="AdminShipOrder"/> From 26b152369fd5be92c68ba64c5381dee55dd2bcd2 Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Thu, 10 Jan 2019 14:46:35 -0600 Subject: [PATCH 004/231] magento-engcom/msi#2.3-develop-MFTF-CreditMemo added MSI-1973: Credit memo test with partial refund --- ...efaultStockAfterFullInvoiceInAdminTest.xml | 179 ++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundForOrderWithSimpleProductOnDefaultStockAfterFullInvoiceInAdminTest.xml diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundForOrderWithSimpleProductOnDefaultStockAfterFullInvoiceInAdminTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundForOrderWithSimpleProductOnDefaultStockAfterFullInvoiceInAdminTest.xml new file mode 100644 index 000000000000..34fa31e164ac --- /dev/null +++ b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundForOrderWithSimpleProductOnDefaultStockAfterFullInvoiceInAdminTest.xml @@ -0,0 +1,179 @@ +<?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="AdminCreditMemoCreatedWithPartialRefundForOrderWithSimpleProductOnDefaultStockAfterFullInvoiceInAdminTest"> + <annotations> + <stories value="MSI Credit Memo"/> + <title value="Credit memo created with partial Refund for order with Simple product on Default stock after full invoice in Admin"/> + <description value="Credit memo created with partial Refund for order with Simple product on Default stock after full invoice in Admin"/> + <testCaseId value="MSI-1973"/> + <severity value="BLOCKER"/> + <group value="msi"/> + <group value="multi_mode"/> + </annotations> + + <before> + <magentoCLI stepKey="enableStockManagement" command="config:set cataloginventory/item_options/manage_stock 1"/> + <createData entity="MsiCustomer1" stepKey="createCustomer1"/> + <createData entity="BasicMsiStockWithMainWebsite1" stepKey="createStock1"/> + <createData entity="FullSource1" stepKey="createSource1"/> + <createData entity="SourceStockLinked1" stepKey="linkSourceStock1"> + <requiredEntity createDataKey="createStock1"/> + <requiredEntity createDataKey="createSource1"/> + </createData> + <createData entity="SimpleSubCategory" stepKey="simpleCategory1"/> + <createData entity="SimpleMsiProduct" stepKey="simpleProduct1"> + <!--<requiredEntity createDataKey="simpleCategory1"/>--> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <waitForPageLoad stepKey="waitForDashboardLoad"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logoutOfAdmin1"/> + <deleteData createDataKey="createCustomer1" stepKey="deleteCustomer"/> + <deleteData createDataKey="createStock1" stepKey="deleteStock"/> + <deleteData createDataKey="simpleCategory1" stepKey="deleteCategory"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct"/> + </after> + + <!--Admin - Assign Default Stock to Main Website--> + <comment userInput="Assign Default Stock to Main Website " stepKey="assignDefaultStockToMainWebsiteComment"/> + <amOnPage url="{{AdminManageStockPage.url}}" stepKey="navigateToStockListPageToAssignDefaultStockToMainWebsite"/> + <waitForPageLoad time="30" stepKey="waitForStockListPageLoad"/> + <actionGroup ref="searchAdminDataGridByKeyword" stepKey="searchDefaultStockByName"> + <argument name="keyword" value="_defaultStock.name"/> + </actionGroup> + <click selector="{{AdminGridRow.editByValue(_defaultStock.name)}}" stepKey="clickEditDefaultStock"/> + <waitForPageLoad time="30" stepKey="waitFroDefaultStockEditPageLoad"/> + <selectOption selector="{{AdminEditStockSalesChannelsSection.websites}}" userInput="Main Website" stepKey="selectDefaultWebsiteAsSalesChannelForDefaultStock"/> + <click selector="{{AdminGridMainControls.saveAndContinue}}" stepKey="saveDefaultStock"/> + + <comment userInput="Assign category to created simple product." stepKey="assignCategoryToProductComment"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForEditProduct"/> + <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findSimpleProductBySku"> + <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> + <argument name="value" value="$$simpleProduct1.sku$$"/> + </actionGroup> + <click selector="{{AdminGridColumnsControls.columns}}" stepKey="selectColumns"/> + <click selector="{{AdminGridColumnsControls.reset}}" stepKey="clickOnResetToRestoreDefaultColumns"/> + <click selector="{{AdminProductGridSection.productGridXRowYColumnButton('1', '2')}}" stepKey="openProductEditPage"/> + <scrollToTopOfPage stepKey="scrollToTopOfPage1"/> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$simpleCategory1.name$$]" requiredAction="true" stepKey="searchAndSelectCategory"/> + <fillField selector="{{AdminProductSourcesGrid.rowQty('0')}}" userInput="1000" stepKey="fillSourceQtyField"/> + <actionGroup ref="AdminFormSaveAndClose" stepKey="saveAndCloseCreatedSimpleProduct"/> + + <!--Login To storefront as Customer--> + <comment userInput="Login to storefront as customer." stepKey="loginToStorefrontComment"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefront"> + <argument name="Customer" value="$$createCustomer1$$"/> + </actionGroup> + + <!--Purchase product once logged in--> + <comment userInput="Purchase 10 simple product" stepKey="purchaseSimpleProductComment"/> + <amOnPage url="{{StorefrontCategoryPage.url($$simpleCategory1.name$$)}}" stepKey="navigateToCategoryPage"/> + <!--<actionGroup ref="StorefrontAddCategoryProductToCartWithQuantityActionGroup" stepKey="addSimpleProductToCart">--> + <!--<argument name="product" value="$$simpleProduct1$$"/>--> + <!--<argument name="quantity" value="2"/>--> + <!--<argument name="checkQuantity" value="1"/>--> + <!--</actionGroup>--> + <!--Action Group StorefrontAddCategoryProductToCartWithQuantityActionGroup explicitly included--> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($$simpleProduct1.name$$)}}" stepKey="moveMouseOverProduct" /> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$simpleProduct1.name$$)}}" stepKey="clickAddToCart" /> + <!-- @TODO: Use general message selector after MQE-694 is fixed --> + <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart($$simpleProduct1.name$$)}}" time="30" stepKey="assertMessage"/> + <waitForText userInput="1" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> + <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> + <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForViewAndEditCartVisible"/> + <clearField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" stepKey="clearField"/> + <fillField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" userInput="10" stepKey="setProductQtyToFiftyInMiniCart"/> + <click selector="{{StorefrontMinicartSection.itemQuantityUpdate($$simpleProduct1.name$$)}}" stepKey="updateQtyInMiniCart"/> + <!--End Action Group StorefrontAddCategoryProductToCartWithQuantityActionGroup--> + <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForPaymentSelectionPageLoad"/> + <click selector=".continue" stepKey="clickOnNext1"/> + <waitForPageLoad stepKey="waitForPageLoad19"/> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyOrderPayment"/> + <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButtonVisible"/> + <see selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{US_Address_TX.street[0]}}" stepKey="chooseBillingAddress"/> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="placeOrder"/> + <waitForPageLoad stepKey="waitUntilOrderPlaced"/> + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> + <see selector="{{CheckoutSuccessMainSection.success}}" userInput="Your order number is:" stepKey="checkOrderPlaceSuccessMessage"/> + + <!--Admin Area Check ordered quantity--> + <comment userInput="Admin - Check ordered quantity" stepKey="AdminCheckOrderedQuantity"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderListPage"/> + <waitForLoadingMaskToDisappear stepKey="waitOrderListPageLoad"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrder"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearch1"/> + <waitForLoadingMaskToDisappear stepKey="waitFilteredOrderListPageLoad"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="navigateToOrderViewPage"/> + <waitForElementVisible selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" stepKey="waitForViewOrderedQuantity"/> + <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Ordered 10" stepKey="orderedQuantity"/> + + <!--Admin Area Check source quantity and salable quantity--> + <comment userInput="Admin - Check Source quantity and salable quantity after order placed" stepKey="AdminCheckQuantityAfterOrderPlaced"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterPlaceOrder"/> + <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterPlaceOrder"> + <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> + <argument name="value" value="$$simpleProduct1.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="1000" stepKey="checkProductSourceQtyAfterPlaceOrder"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="990" stepKey="checkSalableQtyAfterPlaceOrder"/> + + <!--Admin Area Process Full Invoice--> + <comment userInput="Admin - Process invoice for full order" stepKey="InvoiceFullOrder"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToProcessInvoice"/> + <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMaskInvoice"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" + stepKey="searchOrderNumInvoice"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchInvoice"/> + <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMask1"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRowInvoice"/> + <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoice"/> + <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> + <waitForPageLoad time="60" stepKey="waitForPageLoad21"/> + <scrollToTopOfPage stepKey="scrollToTopMessage"/> + <waitForPageLoad stepKey="waitForPageLoad22"/> + <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="checkSuccessMessage"/> + + + <!--Admin Area Create Partial Credit Memo--> + <comment userInput="Admin - Create credit memo for one item of invoiced order" stepKey="AdminCreateCreditMemoPartialOrder"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToCreateCreditMemo"/> + <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMaskCreditMemo"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" + stepKey="searchOrderNumCreditMemo"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchCreditMemo"/> + <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMaskCreditMemo"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRowCreditMemo"/> + <click selector="{{AdminOrderDetailsMainActionsSection.creditMemo}}" stepKey="clickCreditMemo"/> + <click selector="{{AdminCreditMemoItemsSection.itemReturnToStock('1')}}" stepKey="returnToStockCheckbox"/> + <fillField selector="{{AdminCreditMemoItemsSection.itemQtyToRefund('1')}}" userInput="1" stepKey="partialRefund"/> + <click selector="{{AdminCreditMemoItemsSection.updateQty}}" stepKey="updateQuantityToRefund"/> + <waitForLoadingMaskToDisappear stepKey="updateQuantityLoadingMask"/> + <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickSubmit"/> + + + <!--Admin Area Check quantities after Credit Memo--> + <comment userInput="Admin - Check Source quantity and salable quantity after credit memo" stepKey="AdminCheckQuantityAfterCreditMemo"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterCreditMemo"/> + <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterCreditMemo"> + <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> + <argument name="value" value="$$simpleProduct1.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="1000" stepKey="checkProductSourceQtyAfterCreditMemo"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="991" stepKey="checkSalableQtyAfterCreditMemo"/> + + </test> +</tests> + + + From 26c2cc7426a123c842f2ec4192ca73e7ec68aeaa Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Thu, 10 Jan 2019 15:48:45 -0600 Subject: [PATCH 005/231] magento-engcom/msi#2.3-develop-MFTF-CreditMemo New test MSI-1974: CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest --- ...AfterFullInvoiceAndPartialShipmentTest.xml | 201 ++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest.xml diff --git a/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest.xml b/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest.xml new file mode 100644 index 000000000000..faa00ff82491 --- /dev/null +++ b/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest.xml @@ -0,0 +1,201 @@ +<?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="CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest"> + <annotations> + <stories value="MSI Credit Memo"/> + <title value="Credit Memo created with full refund with Simple product on Default stock after full invoice and partial shipment"/> + <description value="Credit Memo created with full refund with Simple product on Default stock after full invoice and partial shipment"/> + <testCaseId value="MSI-1974"/> + <severity value="BLOCKER"/> + <group value="msi"/> + <group value="multi_mode"/> + </annotations> + + <before> + <magentoCLI stepKey="enableStockManagement" command="config:set cataloginventory/item_options/manage_stock 1"/> + <createData entity="MsiCustomer1" stepKey="createCustomer1"/> + <createData entity="BasicMsiStockWithMainWebsite1" stepKey="createStock1"/> + <createData entity="FullSource1" stepKey="createSource1"/> + <createData entity="SourceStockLinked1" stepKey="linkSourceStock1"> + <requiredEntity createDataKey="createStock1"/> + <requiredEntity createDataKey="createSource1"/> + </createData> + <createData entity="SimpleSubCategory" stepKey="simpleCategory1"/> + <createData entity="SimpleMsiProduct" stepKey="simpleProduct1"> + <!--<requiredEntity createDataKey="simpleCategory1"/>--> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <waitForPageLoad stepKey="waitForDashboardLoad"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logoutOfAdmin1"/> + <deleteData createDataKey="createCustomer1" stepKey="deleteCustomer"/> + <deleteData createDataKey="createStock1" stepKey="deleteStock"/> + <deleteData createDataKey="simpleCategory1" stepKey="deleteCategory"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct"/> + </after> + + <!--Admin - Assign Default Stock to Main Website--> + <comment userInput="Assign Default Stock to Main Website " stepKey="assignDefaultStockToMainWebsiteComment"/> + <amOnPage url="{{AdminManageStockPage.url}}" stepKey="navigateToStockListPageToAssignDefaultStockToMainWebsite"/> + <waitForPageLoad time="30" stepKey="waitForStockListPageLoad"/> + <actionGroup ref="searchAdminDataGridByKeyword" stepKey="searchDefaultStockByName"> + <argument name="keyword" value="_defaultStock.name"/> + </actionGroup> + <click selector="{{AdminGridRow.editByValue(_defaultStock.name)}}" stepKey="clickEditDefaultStock"/> + <waitForPageLoad time="30" stepKey="waitFroDefaultStockEditPageLoad"/> + <selectOption selector="{{AdminEditStockSalesChannelsSection.websites}}" userInput="Main Website" stepKey="selectDefaultWebsiteAsSalesChannelForDefaultStock"/> + <click selector="{{AdminGridMainControls.saveAndContinue}}" stepKey="saveDefaultStock"/> + + <comment userInput="Assign category to created simple product." stepKey="assignCategoryToProductComment"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForEditProduct"/> + <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findSimpleProductBySku"> + <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> + <argument name="value" value="$$simpleProduct1.sku$$"/> + </actionGroup> + <click selector="{{AdminGridColumnsControls.columns}}" stepKey="selectColumns"/> + <click selector="{{AdminGridColumnsControls.reset}}" stepKey="clickOnResetToRestoreDefaultColumns"/> + <click selector="{{AdminProductGridSection.productGridXRowYColumnButton('1', '2')}}" stepKey="openProductEditPage"/> + <scrollToTopOfPage stepKey="scrollToTopOfPage1"/> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$simpleCategory1.name$$]" requiredAction="true" stepKey="searchAndSelectCategory"/> + <fillField selector="{{AdminProductSourcesGrid.rowQty('0')}}" userInput="1000" stepKey="fillSourceQtyField"/> + <actionGroup ref="AdminFormSaveAndClose" stepKey="saveAndCloseCreatedSimpleProduct"/> + + <!--Login To storefront as Customer--> + <comment userInput="Login to storefront as customer." stepKey="loginToStorefrontComment"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefront"> + <argument name="Customer" value="$$createCustomer1$$"/> + </actionGroup> + + <!--Purchase product once logged in--> + <comment userInput="Purchase 10 simple product" stepKey="purchaseSimpleProductComment"/> + <amOnPage url="{{StorefrontCategoryPage.url($$simpleCategory1.name$$)}}" stepKey="navigateToCategoryPage"/> + <!--<actionGroup ref="StorefrontAddCategoryProductToCartWithQuantityActionGroup" stepKey="addSimpleProductToCart">--> + <!--<argument name="product" value="$$simpleProduct1$$"/>--> + <!--<argument name="quantity" value="2"/>--> + <!--<argument name="checkQuantity" value="1"/>--> + <!--</actionGroup>--> + <!--Action Group StorefrontAddCategoryProductToCartWithQuantityActionGroup explicitly included--> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($$simpleProduct1.name$$)}}" stepKey="moveMouseOverProduct" /> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$simpleProduct1.name$$)}}" stepKey="clickAddToCart" /> + <!-- @TODO: Use general message selector after MQE-694 is fixed --> + <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart($$simpleProduct1.name$$)}}" time="30" stepKey="assertMessage"/> + <waitForText userInput="1" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> + <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> + <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForViewAndEditCartVisible"/> + <clearField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" stepKey="clearField"/> + <fillField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" userInput="10" stepKey="setProductQtyToFiftyInMiniCart"/> + <click selector="{{StorefrontMinicartSection.itemQuantityUpdate($$simpleProduct1.name$$)}}" stepKey="updateQtyInMiniCart"/> + <!--End Action Group StorefrontAddCategoryProductToCartWithQuantityActionGroup--> + <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForPaymentSelectionPageLoad"/> + <click selector=".continue" stepKey="clickOnNext1"/> + <waitForPageLoad stepKey="waitForPageLoad19"/> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyOrderPayment"/> + <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButtonVisible"/> + <see selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{US_Address_TX.street[0]}}" stepKey="chooseBillingAddress"/> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="placeOrder"/> + <waitForPageLoad stepKey="waitUntilOrderPlaced"/> + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> + <see selector="{{CheckoutSuccessMainSection.success}}" userInput="Your order number is:" stepKey="checkOrderPlaceSuccessMessage"/> + + <!--Admin Area Check ordered quantity--> + <comment userInput="Admin - Check ordered quantity" stepKey="AdminCheckOrderedQuantity"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderListPage"/> + <waitForLoadingMaskToDisappear stepKey="waitOrderListPageLoad"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrder"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearch1"/> + <waitForLoadingMaskToDisappear stepKey="waitFilteredOrderListPageLoad"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="navigateToOrderViewPage"/> + <waitForElementVisible selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" stepKey="waitForViewOrderedQuantity"/> + <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Ordered 10" stepKey="orderedQuantity"/> + + <!--Admin Area Check source quantity and salable quantity--> + <comment userInput="Admin - Check Source quantity and salable quantity after order placed" stepKey="AdminCheckQuantityAfterOrderPlaced"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterPlaceOrder"/> + <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterPlaceOrder"> + <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> + <argument name="value" value="$$simpleProduct1.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="1000" stepKey="checkProductSourceQtyAfterPlaceOrder"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="990" stepKey="checkSalableQtyAfterPlaceOrder"/> + + <!--Admin Area Process Full Invoice--> + <comment userInput="Admin - Process invoice for full order" stepKey="InvoiceFullOrder"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToProcessInvoice"/> + <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMaskInvoice"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" + stepKey="searchOrderNumInvoice"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchInvoice"/> + <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMask1"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRowInvoice"/> + <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoice"/> + <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> + <waitForPageLoad time="60" stepKey="waitForPageLoad21"/> + <scrollToTopOfPage stepKey="scrollToTopMessage"/> + <waitForPageLoad stepKey="waitForPageLoad22"/> + <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="checkSuccessMessage"/> + + <!--Admin Area Process Partial Shipping--> + <comment userInput="Admin - Ship partial order" stepKey="AdminShipPartialOrder"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToCreateShipment"/> + <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMask"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" + stepKey="searchOrderNum"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearch2"/> + <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMask"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> + <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShip"/> + <waitForLoadingMaskToDisappear stepKey="waitForShipLoadingMask"/> + <fillField selector="{{AdminShipmentItemsSection.itemQtyToShip('1')}}" userInput="3" stepKey="shipPartialQuantity3"/> + <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" stepKey="submitShipment"/> + <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The shipment has been created." stepKey="seeShipmentCreateSuccess"/> + + <!--Admin Area Check source quantity and salable quantity after partial shipment--> + <comment userInput="Admin - Check Source quantity and salable quantity after partial shipment" stepKey="AdminCheckQuantityAfterPartialShipment"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterPartialShipment"/> + <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterPartialShipment"> + <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> + <argument name="value" value="$$simpleProduct1.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="997" stepKey="checkProductSourceQtyAfterPartialShipment"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="990" stepKey="checkSalableQtyAfterPartialShipemnt"/> + + <!--Admin Area Create Full Credit Memo--> + <comment userInput="Admin - Create credit memo for full order" stepKey="AdminCreateCreditMemoFullOrder"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToCreateCreditMemo"/> + <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMaskCreditMemo"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" + stepKey="searchOrderNumCreditMemo"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchCreditMemo"/> + <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMaskCreditMemo"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRowCreditMemo"/> + <click selector="{{AdminOrderDetailsMainActionsSection.creditMemo}}" stepKey="clickCreditMemo"/> + <click selector="{{AdminCreditMemoItemsSection.itemReturnToStock('1')}}" stepKey="returnToStockCheckbox"/> + <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickSubmit"/> + + + <!--Admin Area Check quantities after Credit Memo--> + <comment userInput="Admin - Check Source quantity and salable quantity after credit memo" stepKey="AdminCheckQuantityAfterCreditMemo"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterCreditMemo"/> + <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterCreditMemo"> + <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> + <argument name="value" value="$$simpleProduct1.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="1000" stepKey="checkProductSourceQtyAfterCreditMemo"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="1000" stepKey="checkSalableQtyAfterCreditMemo"/> + + </test> +</tests> + + + + From b381fa68e716db20ae17eb039d0aa633a0eec838 Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Thu, 10 Jan 2019 16:00:26 -0600 Subject: [PATCH 006/231] magento-engcom/msi#1949 MSI-1949, MSI-1973, MSI-1974 Removed unneccessary comments from tests. --- ...ltStockAfterFullInvoiceAndShipmentInAdminTest.xml | 12 +----------- ...ductOnDefaultStockAfterFullInvoiceInAdminTest.xml | 12 +----------- ...ltStockAfterFullInvoiceAndPartialShipmentTest.xml | 12 +----------- 3 files changed, 3 insertions(+), 33 deletions(-) diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml index 7466a567842e..fec7b869d4b4 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml @@ -29,9 +29,7 @@ <requiredEntity createDataKey="createSource1"/> </createData> <createData entity="SimpleSubCategory" stepKey="simpleCategory1"/> - <createData entity="SimpleMsiProduct" stepKey="simpleProduct1"> - <!--<requiredEntity createDataKey="simpleCategory1"/>--> - </createData> + <createData entity="SimpleMsiProduct" stepKey="simpleProduct1"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> <waitForPageLoad stepKey="waitForDashboardLoad"/> </before> @@ -78,15 +76,8 @@ <!--Purchase product once logged in--> <comment userInput="Purchase 10 simple product" stepKey="purchaseSimpleProductComment"/> <amOnPage url="{{StorefrontCategoryPage.url($$simpleCategory1.name$$)}}" stepKey="navigateToCategoryPage"/> - <!--<actionGroup ref="StorefrontAddCategoryProductToCartWithQuantityActionGroup" stepKey="addSimpleProductToCart">--> - <!--<argument name="product" value="$$simpleProduct1$$"/>--> - <!--<argument name="quantity" value="2"/>--> - <!--<argument name="checkQuantity" value="1"/>--> - <!--</actionGroup>--> - <!--Action Group StorefrontAddCategoryProductToCartWithQuantityActionGroup explicitly included--> <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($$simpleProduct1.name$$)}}" stepKey="moveMouseOverProduct" /> <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$simpleProduct1.name$$)}}" stepKey="clickAddToCart" /> - <!-- @TODO: Use general message selector after MQE-694 is fixed --> <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart($$simpleProduct1.name$$)}}" time="30" stepKey="assertMessage"/> <waitForText userInput="1" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> @@ -94,7 +85,6 @@ <clearField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" stepKey="clearField"/> <fillField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" userInput="10" stepKey="setProductQtyToFiftyInMiniCart"/> <click selector="{{StorefrontMinicartSection.itemQuantityUpdate($$simpleProduct1.name$$)}}" stepKey="updateQtyInMiniCart"/> - <!--End Action Group StorefrontAddCategoryProductToCartWithQuantityActionGroup--> <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> <waitForPageLoad stepKey="waitForPaymentSelectionPageLoad"/> <click selector=".continue" stepKey="clickOnNext1"/> diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundForOrderWithSimpleProductOnDefaultStockAfterFullInvoiceInAdminTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundForOrderWithSimpleProductOnDefaultStockAfterFullInvoiceInAdminTest.xml index 34fa31e164ac..de8ddb6e4e42 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundForOrderWithSimpleProductOnDefaultStockAfterFullInvoiceInAdminTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundForOrderWithSimpleProductOnDefaultStockAfterFullInvoiceInAdminTest.xml @@ -29,9 +29,7 @@ <requiredEntity createDataKey="createSource1"/> </createData> <createData entity="SimpleSubCategory" stepKey="simpleCategory1"/> - <createData entity="SimpleMsiProduct" stepKey="simpleProduct1"> - <!--<requiredEntity createDataKey="simpleCategory1"/>--> - </createData> + <createData entity="SimpleMsiProduct" stepKey="simpleProduct1"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> <waitForPageLoad stepKey="waitForDashboardLoad"/> </before> @@ -78,15 +76,8 @@ <!--Purchase product once logged in--> <comment userInput="Purchase 10 simple product" stepKey="purchaseSimpleProductComment"/> <amOnPage url="{{StorefrontCategoryPage.url($$simpleCategory1.name$$)}}" stepKey="navigateToCategoryPage"/> - <!--<actionGroup ref="StorefrontAddCategoryProductToCartWithQuantityActionGroup" stepKey="addSimpleProductToCart">--> - <!--<argument name="product" value="$$simpleProduct1$$"/>--> - <!--<argument name="quantity" value="2"/>--> - <!--<argument name="checkQuantity" value="1"/>--> - <!--</actionGroup>--> - <!--Action Group StorefrontAddCategoryProductToCartWithQuantityActionGroup explicitly included--> <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($$simpleProduct1.name$$)}}" stepKey="moveMouseOverProduct" /> <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$simpleProduct1.name$$)}}" stepKey="clickAddToCart" /> - <!-- @TODO: Use general message selector after MQE-694 is fixed --> <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart($$simpleProduct1.name$$)}}" time="30" stepKey="assertMessage"/> <waitForText userInput="1" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> @@ -94,7 +85,6 @@ <clearField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" stepKey="clearField"/> <fillField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" userInput="10" stepKey="setProductQtyToFiftyInMiniCart"/> <click selector="{{StorefrontMinicartSection.itemQuantityUpdate($$simpleProduct1.name$$)}}" stepKey="updateQtyInMiniCart"/> - <!--End Action Group StorefrontAddCategoryProductToCartWithQuantityActionGroup--> <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> <waitForPageLoad stepKey="waitForPaymentSelectionPageLoad"/> <click selector=".continue" stepKey="clickOnNext1"/> diff --git a/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest.xml b/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest.xml index faa00ff82491..051caa078ed5 100644 --- a/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest.xml @@ -29,9 +29,7 @@ <requiredEntity createDataKey="createSource1"/> </createData> <createData entity="SimpleSubCategory" stepKey="simpleCategory1"/> - <createData entity="SimpleMsiProduct" stepKey="simpleProduct1"> - <!--<requiredEntity createDataKey="simpleCategory1"/>--> - </createData> + <createData entity="SimpleMsiProduct" stepKey="simpleProduct1"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> <waitForPageLoad stepKey="waitForDashboardLoad"/> </before> @@ -78,15 +76,8 @@ <!--Purchase product once logged in--> <comment userInput="Purchase 10 simple product" stepKey="purchaseSimpleProductComment"/> <amOnPage url="{{StorefrontCategoryPage.url($$simpleCategory1.name$$)}}" stepKey="navigateToCategoryPage"/> - <!--<actionGroup ref="StorefrontAddCategoryProductToCartWithQuantityActionGroup" stepKey="addSimpleProductToCart">--> - <!--<argument name="product" value="$$simpleProduct1$$"/>--> - <!--<argument name="quantity" value="2"/>--> - <!--<argument name="checkQuantity" value="1"/>--> - <!--</actionGroup>--> - <!--Action Group StorefrontAddCategoryProductToCartWithQuantityActionGroup explicitly included--> <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($$simpleProduct1.name$$)}}" stepKey="moveMouseOverProduct" /> <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$simpleProduct1.name$$)}}" stepKey="clickAddToCart" /> - <!-- @TODO: Use general message selector after MQE-694 is fixed --> <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart($$simpleProduct1.name$$)}}" time="30" stepKey="assertMessage"/> <waitForText userInput="1" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> @@ -94,7 +85,6 @@ <clearField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" stepKey="clearField"/> <fillField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" userInput="10" stepKey="setProductQtyToFiftyInMiniCart"/> <click selector="{{StorefrontMinicartSection.itemQuantityUpdate($$simpleProduct1.name$$)}}" stepKey="updateQtyInMiniCart"/> - <!--End Action Group StorefrontAddCategoryProductToCartWithQuantityActionGroup--> <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> <waitForPageLoad stepKey="waitForPaymentSelectionPageLoad"/> <click selector=".continue" stepKey="clickOnNext1"/> From 7a6c66e4c68ae8cdeefd79169359b7f4fcea5449 Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Fri, 11 Jan 2019 15:59:39 -0600 Subject: [PATCH 007/231] magento-engcom/msi#1976 MSI-1976:CreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipment --- ...tockAfterFullInvoiceAndPartialShipment.xml | 190 ++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipment.xml diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipment.xml b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipment.xml new file mode 100644 index 000000000000..82893e53c963 --- /dev/null +++ b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipment.xml @@ -0,0 +1,190 @@ +<?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="CreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipment"> + <annotations> + <stories value="MSI Credit Memo"/> + <title value="Credit Memo created with partial refund with Simple product on Default stock after full invoice and partial shipment"/> + <description value="Credit Memo created with partial refund with Simple product on Default stock after full invoice and partial shipment"/> + <testCaseId value="MSI-1976"/> + <severity value="BLOCKER"/> + <group value="msi"/> + <group value="multi_mode"/> + </annotations> + + <before> + <magentoCLI stepKey="enableStockManagement" command="config:set cataloginventory/item_options/manage_stock 1"/> + <createData entity="MsiCustomer1" stepKey="createCustomer1"/> + <createData entity="BasicMsiStockWithMainWebsite1" stepKey="createStock1"/> + <createData entity="FullSource1" stepKey="createSource1"/> + <createData entity="SourceStockLinked1" stepKey="linkSourceStock1"> + <requiredEntity createDataKey="createStock1"/> + <requiredEntity createDataKey="createSource1"/> + </createData> + <createData entity="SimpleSubCategory" stepKey="simpleCategory1"/> + <createData entity="SimpleMsiProduct" stepKey="simpleProduct1"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <waitForPageLoad stepKey="waitForDashboardLoad"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logoutOfAdmin1"/> + <deleteData createDataKey="createCustomer1" stepKey="deleteCustomer"/> + <deleteData createDataKey="createStock1" stepKey="deleteStock"/> + <deleteData createDataKey="simpleCategory1" stepKey="deleteCategory"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct"/> + </after> + + <!--Admin - Assign Default Stock to Main Website--> + <comment userInput="Assign Default Stock to Main Website " stepKey="assignDefaultStockToMainWebsiteComment"/> + <amOnPage url="{{AdminManageStockPage.url}}" stepKey="navigateToStockListPageToAssignDefaultStockToMainWebsite"/> + <waitForPageLoad time="30" stepKey="waitForStockListPageLoad"/> + <actionGroup ref="searchAdminDataGridByKeyword" stepKey="searchDefaultStockByName"> + <argument name="keyword" value="_defaultStock.name"/> + </actionGroup> + <click selector="{{AdminGridRow.editByValue(_defaultStock.name)}}" stepKey="clickEditDefaultStock"/> + <waitForPageLoad time="30" stepKey="waitFroDefaultStockEditPageLoad"/> + <selectOption selector="{{AdminEditStockSalesChannelsSection.websites}}" userInput="Main Website" stepKey="selectDefaultWebsiteAsSalesChannelForDefaultStock"/> + <click selector="{{AdminGridMainControls.saveAndContinue}}" stepKey="saveDefaultStock"/> + + <comment userInput="Assign category to created simple product." stepKey="assignCategoryToProductComment"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForEditProduct"/> + <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findSimpleProductBySku"> + <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> + <argument name="value" value="$$simpleProduct1.sku$$"/> + </actionGroup> + <click selector="{{AdminGridColumnsControls.columns}}" stepKey="selectColumns"/> + <click selector="{{AdminGridColumnsControls.reset}}" stepKey="clickOnResetToRestoreDefaultColumns"/> + <click selector="{{AdminProductGridSection.productGridXRowYColumnButton('1', '2')}}" stepKey="openProductEditPage"/> + <scrollToTopOfPage stepKey="scrollToTopOfPage1"/> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$simpleCategory1.name$$]" requiredAction="true" stepKey="searchAndSelectCategory"/> + <fillField selector="{{AdminProductSourcesGrid.rowQty('0')}}" userInput="1000" stepKey="fillSourceQtyField"/> + <actionGroup ref="AdminFormSaveAndClose" stepKey="saveAndCloseCreatedSimpleProduct"/> + + <!--Login To storefront as Customer--> + <comment userInput="Login to storefront as customer." stepKey="loginToStorefrontComment"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefront"> + <argument name="Customer" value="$$createCustomer1$$"/> + </actionGroup> + + <!--Purchase product once logged in--> + <comment userInput="Purchase 10 simple product" stepKey="purchaseSimpleProductComment"/> + <amOnPage url="{{StorefrontCategoryPage.url($$simpleCategory1.name$$)}}" stepKey="navigateToCategoryPage"/> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($$simpleProduct1.name$$)}}" stepKey="moveMouseOverProduct" /> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$simpleProduct1.name$$)}}" stepKey="clickAddToCart" /> + <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart($$simpleProduct1.name$$)}}" time="30" stepKey="assertMessage"/> + <waitForText userInput="1" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> + <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> + <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForViewAndEditCartVisible"/> + <clearField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" stepKey="clearField"/> + <fillField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" userInput="2" stepKey="setProductQtyToFiftyInMiniCart"/> + <click selector="{{StorefrontMinicartSection.itemQuantityUpdate($$simpleProduct1.name$$)}}" stepKey="updateQtyInMiniCart"/> + <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForPaymentSelectionPageLoad"/> + <click selector=".continue" stepKey="clickOnNext1"/> + <waitForPageLoad stepKey="waitForPageLoad19"/> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyOrderPayment"/> + <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButtonVisible"/> + <see selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{US_Address_TX.street[0]}}" stepKey="chooseBillingAddress"/> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="placeOrder"/> + <waitForPageLoad stepKey="waitUntilOrderPlaced"/> + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> + <see selector="{{CheckoutSuccessMainSection.success}}" userInput="Your order number is:" stepKey="checkOrderPlaceSuccessMessage"/> + + <!--Admin Area Check ordered quantity--> + <comment userInput="Admin - Check ordered quantity" stepKey="AdminCheckOrderedQuantity"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderListPage"/> + <waitForLoadingMaskToDisappear stepKey="waitOrderListPageLoad"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrder"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearch1"/> + <waitForLoadingMaskToDisappear stepKey="waitFilteredOrderListPageLoad"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="navigateToOrderViewPage"/> + <waitForElementVisible selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" stepKey="waitForViewOrderedQuantity"/> + <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Ordered 12" stepKey="orderedQuantity"/> + + <!--Admin Area Check source quantity and salable quantity--> + <comment userInput="Admin - Check Source quantity and salable quantity after order placed" stepKey="AdminCheckQuantityAfterOrderPlaced"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterPlaceOrder"/> + <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterPlaceOrder"> + <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> + <argument name="value" value="$$simpleProduct1.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="1000" stepKey="checkProductSourceQtyAfterPlaceOrder"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="988" stepKey="checkSalableQtyAfterPlaceOrder"/> + + <!--Admin Area Process Full Invoice--> + <comment userInput="Admin - Process invoice for full order" stepKey="InvoiceFullOrder"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToProcessInvoice"/> + <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMaskInvoice"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" + stepKey="searchOrderNumInvoice"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchInvoice"/> + <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMask1"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRowInvoice"/> + <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoice"/> + <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> + <waitForPageLoad time="60" stepKey="waitForPageLoad21"/> + <scrollToTopOfPage stepKey="scrollToTopMessage"/> + <waitForPageLoad stepKey="waitForPageLoad22"/> + <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="checkSuccessMessage"/> + + <!--Admin Area Process Partial Shipping--> + <comment userInput="Admin - Ship partial order" stepKey="AdminShipPartialOrder"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToCreateShipment"/> + <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMask"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" + stepKey="searchOrderNum"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearch2"/> + <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMask"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> + <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShip"/> + <waitForLoadingMaskToDisappear stepKey="waitForShipLoadingMask"/> + <fillField selector="{{AdminShipmentItemsSection.itemQtyToShip('1')}}" userInput="3" stepKey="shipPartialQuantity3"/> + <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" stepKey="submitShipment"/> + <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The shipment has been created." stepKey="seeShipmentCreateSuccess"/> + + <!--Admin Area Check source quantity and salable quantity after partial shipment--> + <comment userInput="Admin - Check Source quantity and salable quantity after partial shipment" stepKey="AdminCheckQuantityAfterPartialShipment"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterPartialShipment"/> + <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterPartialShipment"> + <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> + <argument name="value" value="$$simpleProduct1.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="997" stepKey="checkProductSourceQtyAfterPartialShipment"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="988" stepKey="checkSalableQtyAfterPartialShipemnt"/> + + <!--Admin Area Create Partial Credit Memo--> + <comment userInput="Admin - Create credit memo for one item of invoiced order" stepKey="AdminCreateCreditMemoPartialOrder"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToCreateCreditMemo"/> + <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMaskCreditMemo"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" + stepKey="searchOrderNumCreditMemo"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchCreditMemo"/> + <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMaskCreditMemo"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRowCreditMemo"/> + <click selector="{{AdminOrderDetailsMainActionsSection.creditMemo}}" stepKey="clickCreditMemo"/> + <click selector="{{AdminCreditMemoItemsSection.itemReturnToStock('1')}}" stepKey="returnToStockCheckbox"/> + <fillField selector="{{AdminCreditMemoItemsSection.itemQtyToRefund('1')}}" userInput="1" stepKey="partialRefund"/> + <click selector="{{AdminCreditMemoItemsSection.updateQty}}" stepKey="updateQuantityToRefund"/> + <waitForLoadingMaskToDisappear stepKey="updateQuantityLoadingMask"/> + <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickSubmit"/> + + + <!--Admin Area Check quantities after Credit Memo--> + <comment userInput="Admin - Check Source quantity and salable quantity after credit memo" stepKey="AdminCheckQuantityAfterCreditMemo"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterCreditMemo"/> + <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterCreditMemo"> + <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> + <argument name="value" value="$$simpleProduct1.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="997" stepKey="checkProductSourceQtyAfterCreditMemo"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="989" stepKey="checkSalableQtyAfterCreditMemo"/> + + </test> +</tests> \ No newline at end of file From b828ca17f00d7672ac427ed59285e6974ce93f16 Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Mon, 28 Jan 2019 15:55:11 -0600 Subject: [PATCH 008/231] MSI Pull Request 1975 - fixing issues based on code review --- .../Test/Mftf/Suite/msi-suite.xml | 22 ++++ ...AfterFullInvoiceAndShipmentInAdminTest.xml | 118 +++++++---------- ...efaultStockAfterFullInvoiceInAdminTest.xml | 116 +++++++---------- ...tockAfterFullInvoiceAndPartialShipment.xml | 119 +++++++---------- ...AfterFullInvoiceAndPartialShipmentTest.xml | 121 +++++++----------- 5 files changed, 201 insertions(+), 295 deletions(-) diff --git a/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml b/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml index a8eb4b6ba8df..3bb5009775ab 100644 --- a/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml +++ b/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml @@ -70,4 +70,26 @@ <magentoCLI stepKey="disableBackup" command = "config:set system/backup/functionality_enabled 0" /> </after> </suite> + <suite name="MSI_Credit_Memo"> + <before> + <magentoCLI stepKey="enableBackup" command = "config:set system/backup/functionality_enabled 1" /> + <magentoCLI stepKey="dbBackup" command="setup:backup" arguments="--db" /> + <magentoCLI stepKey="maintenanceDisable" command="maintenance:disable"/> + <magentoCLI stepKey="disableWYSYWYG" command="config:set cms/wysiwyg/enabled disabled"/> + </before> + <include> + <group name="CreditMemo"/> + </include> + <exclude> + <group name="sort_order_test"/> + <group name="skip"/> + <group name="single_mode"/> + <group name="multi_mode"/> + </exclude> + <after> + <magentoCLI stepKey="dbRollback" command="setup:rollback" arguments="--db-file%3D%24%28ls%20..%2F..%2F..%2F..%2Fvar%2Fbackups%29%20-n" /> + <magentoCLI stepKey="maintenanceDisable" command="maintenance:disable"/> + <magentoCLI stepKey="disableBackup" command = "config:set system/backup/functionality_enabled 0" /> + </after> + </suite> </suites> diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml index fec7b869d4b4..eb5e1ed1971a 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml @@ -20,75 +20,51 @@ </annotations> <before> - <magentoCLI stepKey="enableStockManagement" command="config:set cataloginventory/item_options/manage_stock 1"/> - <createData entity="MsiCustomer1" stepKey="createCustomer1"/> - <createData entity="BasicMsiStockWithMainWebsite1" stepKey="createStock1"/> - <createData entity="FullSource1" stepKey="createSource1"/> - <createData entity="SourceStockLinked1" stepKey="linkSourceStock1"> - <requiredEntity createDataKey="createStock1"/> - <requiredEntity createDataKey="createSource1"/> + <createData entity="MsiCustomer1" stepKey="createCustomer"/> + <createData entity="BasicMsiStock1" stepKey="createStock"/> + <createData entity="FullSource1" stepKey="createSource"/> + <createData entity="SourceStockLinked1" stepKey="linkSourceStock"> + <requiredEntity createDataKey="createStock"/> + <requiredEntity createDataKey="createSource"/> </createData> - <createData entity="SimpleSubCategory" stepKey="simpleCategory1"/> - <createData entity="SimpleMsiProduct" stepKey="simpleProduct1"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <createData entity="SimpleSubCategory" stepKey="simpleCategory"/> + <createData entity="SimpleProduct" stepKey="simpleProduct"> + <field key="qty">100.00</field> + <requiredEntity createDataKey="simpleCategory"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <waitForPageLoad stepKey="waitForDashboardLoad"/> </before> <after> - <actionGroup ref="logout" stepKey="logoutOfAdmin1"/> - <deleteData createDataKey="createCustomer1" stepKey="deleteCustomer"/> - <deleteData createDataKey="createStock1" stepKey="deleteStock"/> - <deleteData createDataKey="simpleCategory1" stepKey="deleteCategory"/> - <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct"/> + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createStock" stepKey="deleteStock"/> + <deleteData createDataKey="simpleCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> </after> - <!--Admin - Assign Default Stock to Main Website--> - <comment userInput="Assign Default Stock to Main Website " stepKey="assignDefaultStockToMainWebsiteComment"/> - <amOnPage url="{{AdminManageStockPage.url}}" stepKey="navigateToStockListPageToAssignDefaultStockToMainWebsite"/> - <waitForPageLoad time="30" stepKey="waitForStockListPageLoad"/> - <actionGroup ref="searchAdminDataGridByKeyword" stepKey="searchDefaultStockByName"> - <argument name="keyword" value="_defaultStock.name"/> - </actionGroup> - <click selector="{{AdminGridRow.editByValue(_defaultStock.name)}}" stepKey="clickEditDefaultStock"/> - <waitForPageLoad time="30" stepKey="waitFroDefaultStockEditPageLoad"/> - <selectOption selector="{{AdminEditStockSalesChannelsSection.websites}}" userInput="Main Website" stepKey="selectDefaultWebsiteAsSalesChannelForDefaultStock"/> - <click selector="{{AdminGridMainControls.saveAndContinue}}" stepKey="saveDefaultStock"/> - - <comment userInput="Assign category to created simple product." stepKey="assignCategoryToProductComment"/> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForEditProduct"/> - <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findSimpleProductBySku"> - <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> - <argument name="value" value="$$simpleProduct1.sku$$"/> - </actionGroup> - <click selector="{{AdminGridColumnsControls.columns}}" stepKey="selectColumns"/> - <click selector="{{AdminGridColumnsControls.reset}}" stepKey="clickOnResetToRestoreDefaultColumns"/> - <click selector="{{AdminProductGridSection.productGridXRowYColumnButton('1', '2')}}" stepKey="openProductEditPage"/> - <scrollToTopOfPage stepKey="scrollToTopOfPage1"/> - <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$simpleCategory1.name$$]" requiredAction="true" stepKey="searchAndSelectCategory"/> - <fillField selector="{{AdminProductSourcesGrid.rowQty('0')}}" userInput="1000" stepKey="fillSourceQtyField"/> - <actionGroup ref="AdminFormSaveAndClose" stepKey="saveAndCloseCreatedSimpleProduct"/> - <!--Login To storefront as Customer--> <comment userInput="Login to storefront as customer." stepKey="loginToStorefrontComment"/> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefront"> - <argument name="Customer" value="$$createCustomer1$$"/> + <argument name="Customer" value="$$createCustomer$$"/> </actionGroup> <!--Purchase product once logged in--> - <comment userInput="Purchase 10 simple product" stepKey="purchaseSimpleProductComment"/> - <amOnPage url="{{StorefrontCategoryPage.url($$simpleCategory1.name$$)}}" stepKey="navigateToCategoryPage"/> - <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($$simpleProduct1.name$$)}}" stepKey="moveMouseOverProduct" /> - <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$simpleProduct1.name$$)}}" stepKey="clickAddToCart" /> - <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart($$simpleProduct1.name$$)}}" time="30" stepKey="assertMessage"/> + <comment userInput="Purchase 5 simple product" stepKey="purchaseSimpleProductComment"/> + <amOnPage url="{{StorefrontCategoryPage.url($$simpleCategory.name$$)}}" stepKey="navigateToCategoryPage"/> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($$simpleProduct.name$$)}}" stepKey="moveMouseOverProduct" /> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$simpleProduct.name$$)}}" stepKey="clickAddToCart" /> + <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart($$simpleProduct.name$$)}}" time="30" stepKey="assertMessage"/> <waitForText userInput="1" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForViewAndEditCartVisible"/> - <clearField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" stepKey="clearField"/> - <fillField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" userInput="10" stepKey="setProductQtyToFiftyInMiniCart"/> - <click selector="{{StorefrontMinicartSection.itemQuantityUpdate($$simpleProduct1.name$$)}}" stepKey="updateQtyInMiniCart"/> + <clearField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct.name$$)}}" stepKey="clearField"/> + <fillField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct.name$$)}}" userInput="5" stepKey="setProductQtyToFiftyInMiniCart"/> + <click selector="{{StorefrontMinicartSection.itemQuantityUpdate($$simpleProduct.name$$)}}" stepKey="updateQtyInMiniCart"/> <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> <waitForPageLoad stepKey="waitForPaymentSelectionPageLoad"/> - <click selector=".continue" stepKey="clickOnNext1"/> - <waitForPageLoad stepKey="waitForPageLoad19"/> + <click selector=".continue" stepKey="clickOnNextCheckout"/> + <waitForPageLoad stepKey="waitForPageLoadCheckout"/> <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyOrderPayment"/> <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButtonVisible"/> <see selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{US_Address_TX.street[0]}}" stepKey="chooseBillingAddress"/> @@ -102,29 +78,28 @@ <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderListPage"/> <waitForLoadingMaskToDisappear stepKey="waitOrderListPageLoad"/> <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrder"/> - <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearch1"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearch"/> <waitForLoadingMaskToDisappear stepKey="waitFilteredOrderListPageLoad"/> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="navigateToOrderViewPage"/> <waitForElementVisible selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" stepKey="waitForViewOrderedQuantity"/> - <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Ordered 10" stepKey="orderedQuantity"/> + <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Ordered 5" stepKey="orderedQuantity"/> <!--Admin Area Check source quantity and salable quantity--> <comment userInput="Admin - Check Source quantity and salable quantity after order placed" stepKey="AdminCheckQuantityAfterOrderPlaced"/> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterPlaceOrder"/> <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterPlaceOrder"> <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> - <argument name="value" value="$$simpleProduct1.sku$$"/> + <argument name="value" value="$$simpleProduct.sku$$"/> </actionGroup> - <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="1000" stepKey="checkProductSourceQtyAfterPlaceOrder"/> - <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="990" stepKey="checkSalableQtyAfterPlaceOrder"/> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="100" stepKey="checkProductSourceQtyAfterPlaceOrder"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="95" stepKey="checkSalableQtyAfterPlaceOrder"/> <!--Admin Area Process Shipping--> <comment userInput="Admin - Ship order" stepKey="AdminShipOrder"/> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToCreateShipment"/> <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMask"/> - <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" - stepKey="searchOrderNum"/> - <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearch2"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNum"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchShipping"/> <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMask"/> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShip"/> @@ -136,16 +111,15 @@ <comment userInput="Admin - Process invoice for full order" stepKey="InvoiceFullOrder"/> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToProcessInvoice"/> <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMaskInvoice"/> - <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" - stepKey="searchOrderNumInvoice"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNumInvoice"/> <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchInvoice"/> - <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMask1"/> + <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMaskGridForInvoice"/> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRowInvoice"/> <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoice"/> <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> - <waitForPageLoad time="60" stepKey="waitForPageLoad21"/> + <waitForPageLoad time="60" stepKey="waitForPageLoadSubmitInvoice"/> <scrollToTopOfPage stepKey="scrollToTopMessage"/> - <waitForPageLoad stepKey="waitForPageLoad22"/> + <waitForPageLoad stepKey="waitForPageLoadSuccessMessage"/> <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="checkSuccessMessage"/> @@ -153,8 +127,7 @@ <comment userInput="Admin - Create credit memo for full order" stepKey="AdminCreateCreditMemoFullOrder"/> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToCreateCreditMemo"/> <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMaskCreditMemo"/> - <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" - stepKey="searchOrderNumCreditMemo"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNumCreditMemo"/> <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchCreditMemo"/> <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMaskCreditMemo"/> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRowCreditMemo"/> @@ -168,13 +141,10 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterCreditMemo"/> <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterCreditMemo"> <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> - <argument name="value" value="$$simpleProduct1.sku$$"/> + <argument name="value" value="$$simpleProduct.sku$$"/> </actionGroup> - <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="1000" stepKey="checkProductSourceQtyAfterCreditMemo"/> - <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="1000" stepKey="checkSalableQtyAfterCreditMemo"/> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="100" stepKey="checkProductSourceQtyAfterCreditMemo"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="100" stepKey="checkSalableQtyAfterCreditMemo"/> </test> -</tests> - - - +</tests> \ No newline at end of file diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundForOrderWithSimpleProductOnDefaultStockAfterFullInvoiceInAdminTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundForOrderWithSimpleProductOnDefaultStockAfterFullInvoiceInAdminTest.xml index de8ddb6e4e42..972a92ca4032 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundForOrderWithSimpleProductOnDefaultStockAfterFullInvoiceInAdminTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundForOrderWithSimpleProductOnDefaultStockAfterFullInvoiceInAdminTest.xml @@ -15,80 +15,57 @@ <description value="Credit memo created with partial Refund for order with Simple product on Default stock after full invoice in Admin"/> <testCaseId value="MSI-1973"/> <severity value="BLOCKER"/> - <group value="msi"/> - <group value="multi_mode"/> + <!--<group value="msi"/>--> + <!--<group value="multi_mode"/>--> + <group value="CreditMemo"/> </annotations> <before> - <magentoCLI stepKey="enableStockManagement" command="config:set cataloginventory/item_options/manage_stock 1"/> - <createData entity="MsiCustomer1" stepKey="createCustomer1"/> - <createData entity="BasicMsiStockWithMainWebsite1" stepKey="createStock1"/> - <createData entity="FullSource1" stepKey="createSource1"/> - <createData entity="SourceStockLinked1" stepKey="linkSourceStock1"> - <requiredEntity createDataKey="createStock1"/> - <requiredEntity createDataKey="createSource1"/> + <createData entity="MsiCustomer1" stepKey="createCustomer"/> + <createData entity="BasicMsiStock1" stepKey="createStock"/> + <createData entity="FullSource1" stepKey="createSource"/> + <createData entity="SourceStockLinked1" stepKey="linkSourceStock"> + <requiredEntity createDataKey="createStock"/> + <requiredEntity createDataKey="createSource"/> </createData> - <createData entity="SimpleSubCategory" stepKey="simpleCategory1"/> - <createData entity="SimpleMsiProduct" stepKey="simpleProduct1"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <createData entity="SimpleSubCategory" stepKey="simpleCategory"/> + <createData entity="SimpleProduct" stepKey="simpleProduct"> + <field key="qty">100.00</field> + <requiredEntity createDataKey="simpleCategory"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <waitForPageLoad stepKey="waitForDashboardLoad"/> </before> <after> <actionGroup ref="logout" stepKey="logoutOfAdmin1"/> - <deleteData createDataKey="createCustomer1" stepKey="deleteCustomer"/> - <deleteData createDataKey="createStock1" stepKey="deleteStock"/> - <deleteData createDataKey="simpleCategory1" stepKey="deleteCategory"/> - <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createStock" stepKey="deleteStock"/> + <deleteData createDataKey="simpleCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> </after> - <!--Admin - Assign Default Stock to Main Website--> - <comment userInput="Assign Default Stock to Main Website " stepKey="assignDefaultStockToMainWebsiteComment"/> - <amOnPage url="{{AdminManageStockPage.url}}" stepKey="navigateToStockListPageToAssignDefaultStockToMainWebsite"/> - <waitForPageLoad time="30" stepKey="waitForStockListPageLoad"/> - <actionGroup ref="searchAdminDataGridByKeyword" stepKey="searchDefaultStockByName"> - <argument name="keyword" value="_defaultStock.name"/> - </actionGroup> - <click selector="{{AdminGridRow.editByValue(_defaultStock.name)}}" stepKey="clickEditDefaultStock"/> - <waitForPageLoad time="30" stepKey="waitFroDefaultStockEditPageLoad"/> - <selectOption selector="{{AdminEditStockSalesChannelsSection.websites}}" userInput="Main Website" stepKey="selectDefaultWebsiteAsSalesChannelForDefaultStock"/> - <click selector="{{AdminGridMainControls.saveAndContinue}}" stepKey="saveDefaultStock"/> - - <comment userInput="Assign category to created simple product." stepKey="assignCategoryToProductComment"/> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForEditProduct"/> - <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findSimpleProductBySku"> - <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> - <argument name="value" value="$$simpleProduct1.sku$$"/> - </actionGroup> - <click selector="{{AdminGridColumnsControls.columns}}" stepKey="selectColumns"/> - <click selector="{{AdminGridColumnsControls.reset}}" stepKey="clickOnResetToRestoreDefaultColumns"/> - <click selector="{{AdminProductGridSection.productGridXRowYColumnButton('1', '2')}}" stepKey="openProductEditPage"/> - <scrollToTopOfPage stepKey="scrollToTopOfPage1"/> - <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$simpleCategory1.name$$]" requiredAction="true" stepKey="searchAndSelectCategory"/> - <fillField selector="{{AdminProductSourcesGrid.rowQty('0')}}" userInput="1000" stepKey="fillSourceQtyField"/> - <actionGroup ref="AdminFormSaveAndClose" stepKey="saveAndCloseCreatedSimpleProduct"/> - <!--Login To storefront as Customer--> <comment userInput="Login to storefront as customer." stepKey="loginToStorefrontComment"/> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefront"> - <argument name="Customer" value="$$createCustomer1$$"/> + <argument name="Customer" value="$$createCustomer$$"/> </actionGroup> <!--Purchase product once logged in--> - <comment userInput="Purchase 10 simple product" stepKey="purchaseSimpleProductComment"/> - <amOnPage url="{{StorefrontCategoryPage.url($$simpleCategory1.name$$)}}" stepKey="navigateToCategoryPage"/> - <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($$simpleProduct1.name$$)}}" stepKey="moveMouseOverProduct" /> - <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$simpleProduct1.name$$)}}" stepKey="clickAddToCart" /> - <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart($$simpleProduct1.name$$)}}" time="30" stepKey="assertMessage"/> + <comment userInput="Purchase 5 simple product" stepKey="purchaseSimpleProductComment"/> + <amOnPage url="{{StorefrontCategoryPage.url($$simpleCategory.name$$)}}" stepKey="navigateToCategoryPage"/> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($$simpleProduct.name$$)}}" stepKey="moveMouseOverProduct" /> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$simpleProduct.name$$)}}" stepKey="clickAddToCart" /> + <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart($$simpleProduct.name$$)}}" time="30" stepKey="assertMessage"/> <waitForText userInput="1" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForViewAndEditCartVisible"/> - <clearField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" stepKey="clearField"/> - <fillField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" userInput="10" stepKey="setProductQtyToFiftyInMiniCart"/> - <click selector="{{StorefrontMinicartSection.itemQuantityUpdate($$simpleProduct1.name$$)}}" stepKey="updateQtyInMiniCart"/> + <clearField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct.name$$)}}" stepKey="clearField"/> + <fillField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct.name$$)}}" userInput="5" stepKey="setProductQtyToFiftyInMiniCart"/> + <click selector="{{StorefrontMinicartSection.itemQuantityUpdate($$simpleProduct.name$$)}}" stepKey="updateQtyInMiniCart"/> <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> <waitForPageLoad stepKey="waitForPaymentSelectionPageLoad"/> - <click selector=".continue" stepKey="clickOnNext1"/> - <waitForPageLoad stepKey="waitForPageLoad19"/> + <click selector=".continue" stepKey="clickOnNextPaymentPage"/> + <waitForPageLoad stepKey="waitForPageLoadCheckoutSelectPayment"/> <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyOrderPayment"/> <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButtonVisible"/> <see selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{US_Address_TX.street[0]}}" stepKey="chooseBillingAddress"/> @@ -102,36 +79,35 @@ <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderListPage"/> <waitForLoadingMaskToDisappear stepKey="waitOrderListPageLoad"/> <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrder"/> - <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearch1"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchCheckOrderAfterCustomerSubmits"/> <waitForLoadingMaskToDisappear stepKey="waitFilteredOrderListPageLoad"/> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="navigateToOrderViewPage"/> <waitForElementVisible selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" stepKey="waitForViewOrderedQuantity"/> - <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Ordered 10" stepKey="orderedQuantity"/> + <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Ordered 5" stepKey="orderedQuantity"/> <!--Admin Area Check source quantity and salable quantity--> <comment userInput="Admin - Check Source quantity and salable quantity after order placed" stepKey="AdminCheckQuantityAfterOrderPlaced"/> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterPlaceOrder"/> <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterPlaceOrder"> <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> - <argument name="value" value="$$simpleProduct1.sku$$"/> + <argument name="value" value="$$simpleProduct.sku$$"/> </actionGroup> - <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="1000" stepKey="checkProductSourceQtyAfterPlaceOrder"/> - <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="990" stepKey="checkSalableQtyAfterPlaceOrder"/> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="100" stepKey="checkProductSourceQtyAfterPlaceOrder"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="95" stepKey="checkSalableQtyAfterPlaceOrder"/> <!--Admin Area Process Full Invoice--> <comment userInput="Admin - Process invoice for full order" stepKey="InvoiceFullOrder"/> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToProcessInvoice"/> <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMaskInvoice"/> - <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" - stepKey="searchOrderNumInvoice"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNumInvoice"/> <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchInvoice"/> - <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMask1"/> + <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMaskGridForInvoice"/> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRowInvoice"/> <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoice"/> <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> - <waitForPageLoad time="60" stepKey="waitForPageLoad21"/> + <waitForPageLoad time="60" stepKey="waitForPageLoadSubmitInvoice"/> <scrollToTopOfPage stepKey="scrollToTopMessage"/> - <waitForPageLoad stepKey="waitForPageLoad22"/> + <waitForPageLoad stepKey="waitForPageLoadSuccessMessage"/> <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="checkSuccessMessage"/> @@ -139,8 +115,7 @@ <comment userInput="Admin - Create credit memo for one item of invoiced order" stepKey="AdminCreateCreditMemoPartialOrder"/> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToCreateCreditMemo"/> <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMaskCreditMemo"/> - <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" - stepKey="searchOrderNumCreditMemo"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNumCreditMemo"/> <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchCreditMemo"/> <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMaskCreditMemo"/> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRowCreditMemo"/> @@ -157,13 +132,10 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterCreditMemo"/> <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterCreditMemo"> <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> - <argument name="value" value="$$simpleProduct1.sku$$"/> + <argument name="value" value="$$simpleProduct.sku$$"/> </actionGroup> - <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="1000" stepKey="checkProductSourceQtyAfterCreditMemo"/> - <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="991" stepKey="checkSalableQtyAfterCreditMemo"/> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="100" stepKey="checkProductSourceQtyAfterCreditMemo"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="96" stepKey="checkSalableQtyAfterCreditMemo"/> </test> -</tests> - - - +</tests> \ No newline at end of file diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipment.xml b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipment.xml index 82893e53c963..8a6f0354f798 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipment.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipment.xml @@ -20,75 +20,51 @@ </annotations> <before> - <magentoCLI stepKey="enableStockManagement" command="config:set cataloginventory/item_options/manage_stock 1"/> - <createData entity="MsiCustomer1" stepKey="createCustomer1"/> - <createData entity="BasicMsiStockWithMainWebsite1" stepKey="createStock1"/> - <createData entity="FullSource1" stepKey="createSource1"/> - <createData entity="SourceStockLinked1" stepKey="linkSourceStock1"> - <requiredEntity createDataKey="createStock1"/> - <requiredEntity createDataKey="createSource1"/> + <createData entity="MsiCustomer1" stepKey="createCustomer"/> + <createData entity="BasicMsiStock1" stepKey="createStock"/> + <createData entity="FullSource1" stepKey="createSource"/> + <createData entity="SourceStockLinked1" stepKey="linkSourceStock"> + <requiredEntity createDataKey="createStock"/> + <requiredEntity createDataKey="createSource"/> </createData> - <createData entity="SimpleSubCategory" stepKey="simpleCategory1"/> - <createData entity="SimpleMsiProduct" stepKey="simpleProduct1"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <createData entity="SimpleSubCategory" stepKey="simpleCategory"/> + <createData entity="SimpleProduct" stepKey="simpleProduct"> + <field key="qty">100.00</field> + <requiredEntity createDataKey="simpleCategory"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <waitForPageLoad stepKey="waitForDashboardLoad"/> </before> <after> - <actionGroup ref="logout" stepKey="logoutOfAdmin1"/> - <deleteData createDataKey="createCustomer1" stepKey="deleteCustomer"/> - <deleteData createDataKey="createStock1" stepKey="deleteStock"/> - <deleteData createDataKey="simpleCategory1" stepKey="deleteCategory"/> - <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct"/> + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createStock" stepKey="deleteStock"/> + <deleteData createDataKey="simpleCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> </after> - <!--Admin - Assign Default Stock to Main Website--> - <comment userInput="Assign Default Stock to Main Website " stepKey="assignDefaultStockToMainWebsiteComment"/> - <amOnPage url="{{AdminManageStockPage.url}}" stepKey="navigateToStockListPageToAssignDefaultStockToMainWebsite"/> - <waitForPageLoad time="30" stepKey="waitForStockListPageLoad"/> - <actionGroup ref="searchAdminDataGridByKeyword" stepKey="searchDefaultStockByName"> - <argument name="keyword" value="_defaultStock.name"/> - </actionGroup> - <click selector="{{AdminGridRow.editByValue(_defaultStock.name)}}" stepKey="clickEditDefaultStock"/> - <waitForPageLoad time="30" stepKey="waitFroDefaultStockEditPageLoad"/> - <selectOption selector="{{AdminEditStockSalesChannelsSection.websites}}" userInput="Main Website" stepKey="selectDefaultWebsiteAsSalesChannelForDefaultStock"/> - <click selector="{{AdminGridMainControls.saveAndContinue}}" stepKey="saveDefaultStock"/> - - <comment userInput="Assign category to created simple product." stepKey="assignCategoryToProductComment"/> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForEditProduct"/> - <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findSimpleProductBySku"> - <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> - <argument name="value" value="$$simpleProduct1.sku$$"/> - </actionGroup> - <click selector="{{AdminGridColumnsControls.columns}}" stepKey="selectColumns"/> - <click selector="{{AdminGridColumnsControls.reset}}" stepKey="clickOnResetToRestoreDefaultColumns"/> - <click selector="{{AdminProductGridSection.productGridXRowYColumnButton('1', '2')}}" stepKey="openProductEditPage"/> - <scrollToTopOfPage stepKey="scrollToTopOfPage1"/> - <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$simpleCategory1.name$$]" requiredAction="true" stepKey="searchAndSelectCategory"/> - <fillField selector="{{AdminProductSourcesGrid.rowQty('0')}}" userInput="1000" stepKey="fillSourceQtyField"/> - <actionGroup ref="AdminFormSaveAndClose" stepKey="saveAndCloseCreatedSimpleProduct"/> - <!--Login To storefront as Customer--> <comment userInput="Login to storefront as customer." stepKey="loginToStorefrontComment"/> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefront"> - <argument name="Customer" value="$$createCustomer1$$"/> + <argument name="Customer" value="$$createCustomer$$"/> </actionGroup> <!--Purchase product once logged in--> - <comment userInput="Purchase 10 simple product" stepKey="purchaseSimpleProductComment"/> - <amOnPage url="{{StorefrontCategoryPage.url($$simpleCategory1.name$$)}}" stepKey="navigateToCategoryPage"/> - <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($$simpleProduct1.name$$)}}" stepKey="moveMouseOverProduct" /> - <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$simpleProduct1.name$$)}}" stepKey="clickAddToCart" /> - <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart($$simpleProduct1.name$$)}}" time="30" stepKey="assertMessage"/> + <comment userInput="Purchase 5 simple product" stepKey="purchaseSimpleProductComment"/> + <amOnPage url="{{StorefrontCategoryPage.url($$simpleCategory.name$$)}}" stepKey="navigateToCategoryPage"/> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($$simpleProduct.name$$)}}" stepKey="moveMouseOverProduct" /> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$simpleProduct.name$$)}}" stepKey="clickAddToCart" /> + <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart($$simpleProduct.name$$)}}" time="30" stepKey="assertMessage"/> <waitForText userInput="1" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForViewAndEditCartVisible"/> - <clearField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" stepKey="clearField"/> - <fillField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" userInput="2" stepKey="setProductQtyToFiftyInMiniCart"/> - <click selector="{{StorefrontMinicartSection.itemQuantityUpdate($$simpleProduct1.name$$)}}" stepKey="updateQtyInMiniCart"/> + <clearField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct.name$$)}}" stepKey="clearField"/> + <fillField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct.name$$)}}" userInput="5" stepKey="setProductQtyToFiftyInMiniCart"/> + <click selector="{{StorefrontMinicartSection.itemQuantityUpdate($$simpleProduct.name$$)}}" stepKey="updateQtyInMiniCart"/> <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> <waitForPageLoad stepKey="waitForPaymentSelectionPageLoad"/> - <click selector=".continue" stepKey="clickOnNext1"/> - <waitForPageLoad stepKey="waitForPageLoad19"/> + <click selector=".continue" stepKey="clickOnNextCheckout"/> + <waitForPageLoad stepKey="waitForPageLoadCheckout"/> <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyOrderPayment"/> <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButtonVisible"/> <see selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{US_Address_TX.street[0]}}" stepKey="chooseBillingAddress"/> @@ -102,45 +78,43 @@ <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderListPage"/> <waitForLoadingMaskToDisappear stepKey="waitOrderListPageLoad"/> <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrder"/> - <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearch1"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearch"/> <waitForLoadingMaskToDisappear stepKey="waitFilteredOrderListPageLoad"/> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="navigateToOrderViewPage"/> <waitForElementVisible selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" stepKey="waitForViewOrderedQuantity"/> - <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Ordered 12" stepKey="orderedQuantity"/> + <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Ordered 5" stepKey="orderedQuantity"/> <!--Admin Area Check source quantity and salable quantity--> <comment userInput="Admin - Check Source quantity and salable quantity after order placed" stepKey="AdminCheckQuantityAfterOrderPlaced"/> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterPlaceOrder"/> <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterPlaceOrder"> <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> - <argument name="value" value="$$simpleProduct1.sku$$"/> + <argument name="value" value="$$simpleProduct.sku$$"/> </actionGroup> - <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="1000" stepKey="checkProductSourceQtyAfterPlaceOrder"/> - <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="988" stepKey="checkSalableQtyAfterPlaceOrder"/> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="100" stepKey="checkProductSourceQtyAfterPlaceOrder"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="995" stepKey="checkSalableQtyAfterPlaceOrder"/> <!--Admin Area Process Full Invoice--> <comment userInput="Admin - Process invoice for full order" stepKey="InvoiceFullOrder"/> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToProcessInvoice"/> <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMaskInvoice"/> - <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" - stepKey="searchOrderNumInvoice"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNumInvoice"/> <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchInvoice"/> - <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMask1"/> + <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMaskGridForInvoice"/> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRowInvoice"/> <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoice"/> <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> - <waitForPageLoad time="60" stepKey="waitForPageLoad21"/> + <waitForPageLoad time="60" stepKey="waitForPageLoadSubmitInvoice"/> <scrollToTopOfPage stepKey="scrollToTopMessage"/> - <waitForPageLoad stepKey="waitForPageLoad22"/> + <waitForPageLoad stepKey="waitForPageLoadSuccessMessage"/> <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="checkSuccessMessage"/> <!--Admin Area Process Partial Shipping--> <comment userInput="Admin - Ship partial order" stepKey="AdminShipPartialOrder"/> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToCreateShipment"/> <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMask"/> - <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" - stepKey="searchOrderNum"/> - <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearch2"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNum"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchShipping"/> <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMask"/> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShip"/> @@ -154,17 +128,16 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterPartialShipment"/> <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterPartialShipment"> <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> - <argument name="value" value="$$simpleProduct1.sku$$"/> + <argument name="value" value="$$simpleProduct.sku$$"/> </actionGroup> - <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="997" stepKey="checkProductSourceQtyAfterPartialShipment"/> - <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="988" stepKey="checkSalableQtyAfterPartialShipemnt"/> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="97" stepKey="checkProductSourceQtyAfterPartialShipment"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="95" stepKey="checkSalableQtyAfterPartialShipment"/> <!--Admin Area Create Partial Credit Memo--> <comment userInput="Admin - Create credit memo for one item of invoiced order" stepKey="AdminCreateCreditMemoPartialOrder"/> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToCreateCreditMemo"/> <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMaskCreditMemo"/> - <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" - stepKey="searchOrderNumCreditMemo"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNumCreditMemo"/> <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchCreditMemo"/> <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMaskCreditMemo"/> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRowCreditMemo"/> @@ -181,10 +154,10 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterCreditMemo"/> <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterCreditMemo"> <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> - <argument name="value" value="$$simpleProduct1.sku$$"/> + <argument name="value" value="$$simpleProduct.sku$$"/> </actionGroup> - <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="997" stepKey="checkProductSourceQtyAfterCreditMemo"/> - <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="989" stepKey="checkSalableQtyAfterCreditMemo"/> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="97" stepKey="checkProductSourceQtyAfterCreditMemo"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="96" stepKey="checkSalableQtyAfterCreditMemo"/> </test> </tests> \ No newline at end of file diff --git a/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest.xml b/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest.xml index 051caa078ed5..763f48210ed8 100644 --- a/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest.xml @@ -20,75 +20,51 @@ </annotations> <before> - <magentoCLI stepKey="enableStockManagement" command="config:set cataloginventory/item_options/manage_stock 1"/> - <createData entity="MsiCustomer1" stepKey="createCustomer1"/> - <createData entity="BasicMsiStockWithMainWebsite1" stepKey="createStock1"/> - <createData entity="FullSource1" stepKey="createSource1"/> - <createData entity="SourceStockLinked1" stepKey="linkSourceStock1"> - <requiredEntity createDataKey="createStock1"/> - <requiredEntity createDataKey="createSource1"/> + <createData entity="MsiCustomer1" stepKey="createCustomer"/> + <createData entity="BasicMsiStock1" stepKey="createStock"/> + <createData entity="FullSource1" stepKey="createSource"/> + <createData entity="SourceStockLinked1" stepKey="linkSourceStock"> + <requiredEntity createDataKey="createStock"/> + <requiredEntity createDataKey="createSource"/> </createData> - <createData entity="SimpleSubCategory" stepKey="simpleCategory1"/> - <createData entity="SimpleMsiProduct" stepKey="simpleProduct1"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <createData entity="SimpleSubCategory" stepKey="simpleCategory"/> + <createData entity="SimpleProduct" stepKey="simpleProduct"> + <field key="qty">100.00</field> + <requiredEntity createDataKey="simpleCategory"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <waitForPageLoad stepKey="waitForDashboardLoad"/> </before> <after> - <actionGroup ref="logout" stepKey="logoutOfAdmin1"/> - <deleteData createDataKey="createCustomer1" stepKey="deleteCustomer"/> - <deleteData createDataKey="createStock1" stepKey="deleteStock"/> - <deleteData createDataKey="simpleCategory1" stepKey="deleteCategory"/> - <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct"/> + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createStock" stepKey="deleteStock"/> + <deleteData createDataKey="simpleCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> </after> - - <!--Admin - Assign Default Stock to Main Website--> - <comment userInput="Assign Default Stock to Main Website " stepKey="assignDefaultStockToMainWebsiteComment"/> - <amOnPage url="{{AdminManageStockPage.url}}" stepKey="navigateToStockListPageToAssignDefaultStockToMainWebsite"/> - <waitForPageLoad time="30" stepKey="waitForStockListPageLoad"/> - <actionGroup ref="searchAdminDataGridByKeyword" stepKey="searchDefaultStockByName"> - <argument name="keyword" value="_defaultStock.name"/> - </actionGroup> - <click selector="{{AdminGridRow.editByValue(_defaultStock.name)}}" stepKey="clickEditDefaultStock"/> - <waitForPageLoad time="30" stepKey="waitFroDefaultStockEditPageLoad"/> - <selectOption selector="{{AdminEditStockSalesChannelsSection.websites}}" userInput="Main Website" stepKey="selectDefaultWebsiteAsSalesChannelForDefaultStock"/> - <click selector="{{AdminGridMainControls.saveAndContinue}}" stepKey="saveDefaultStock"/> - - <comment userInput="Assign category to created simple product." stepKey="assignCategoryToProductComment"/> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForEditProduct"/> - <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findSimpleProductBySku"> - <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> - <argument name="value" value="$$simpleProduct1.sku$$"/> - </actionGroup> - <click selector="{{AdminGridColumnsControls.columns}}" stepKey="selectColumns"/> - <click selector="{{AdminGridColumnsControls.reset}}" stepKey="clickOnResetToRestoreDefaultColumns"/> - <click selector="{{AdminProductGridSection.productGridXRowYColumnButton('1', '2')}}" stepKey="openProductEditPage"/> - <scrollToTopOfPage stepKey="scrollToTopOfPage1"/> - <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$simpleCategory1.name$$]" requiredAction="true" stepKey="searchAndSelectCategory"/> - <fillField selector="{{AdminProductSourcesGrid.rowQty('0')}}" userInput="1000" stepKey="fillSourceQtyField"/> - <actionGroup ref="AdminFormSaveAndClose" stepKey="saveAndCloseCreatedSimpleProduct"/> - + <!--Login To storefront as Customer--> <comment userInput="Login to storefront as customer." stepKey="loginToStorefrontComment"/> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefront"> - <argument name="Customer" value="$$createCustomer1$$"/> + <argument name="Customer" value="$$createCustomer$$"/> </actionGroup> <!--Purchase product once logged in--> - <comment userInput="Purchase 10 simple product" stepKey="purchaseSimpleProductComment"/> - <amOnPage url="{{StorefrontCategoryPage.url($$simpleCategory1.name$$)}}" stepKey="navigateToCategoryPage"/> - <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($$simpleProduct1.name$$)}}" stepKey="moveMouseOverProduct" /> - <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$simpleProduct1.name$$)}}" stepKey="clickAddToCart" /> - <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart($$simpleProduct1.name$$)}}" time="30" stepKey="assertMessage"/> + <comment userInput="Purchase 5 simple product" stepKey="purchaseSimpleProductComment"/> + <amOnPage url="{{StorefrontCategoryPage.url($$simpleCategory.name$$)}}" stepKey="navigateToCategoryPage"/> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($$simpleProduct.name$$)}}" stepKey="moveMouseOverProduct" /> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$simpleProduct.name$$)}}" stepKey="clickAddToCart" /> + <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart($$simpleProduct.name$$)}}" time="30" stepKey="assertMessage"/> <waitForText userInput="1" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForViewAndEditCartVisible"/> - <clearField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" stepKey="clearField"/> - <fillField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct1.name$$)}}" userInput="10" stepKey="setProductQtyToFiftyInMiniCart"/> - <click selector="{{StorefrontMinicartSection.itemQuantityUpdate($$simpleProduct1.name$$)}}" stepKey="updateQtyInMiniCart"/> + <clearField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct.name$$)}}" stepKey="clearField"/> + <fillField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct.name$$)}}" userInput="5" stepKey="setProductQtyToFiftyInMiniCart"/> + <click selector="{{StorefrontMinicartSection.itemQuantityUpdate($$simpleProduct.name$$)}}" stepKey="updateQtyInMiniCart"/> <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> <waitForPageLoad stepKey="waitForPaymentSelectionPageLoad"/> - <click selector=".continue" stepKey="clickOnNext1"/> - <waitForPageLoad stepKey="waitForPageLoad19"/> + <click selector=".continue" stepKey="clickOnNextCheckout"/> + <waitForPageLoad stepKey="waitForPageLoadCheckout"/> <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyOrderPayment"/> <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButtonVisible"/> <see selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{US_Address_TX.street[0]}}" stepKey="chooseBillingAddress"/> @@ -106,41 +82,39 @@ <waitForLoadingMaskToDisappear stepKey="waitFilteredOrderListPageLoad"/> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="navigateToOrderViewPage"/> <waitForElementVisible selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" stepKey="waitForViewOrderedQuantity"/> - <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Ordered 10" stepKey="orderedQuantity"/> + <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Ordered 5" stepKey="orderedQuantity"/> <!--Admin Area Check source quantity and salable quantity--> <comment userInput="Admin - Check Source quantity and salable quantity after order placed" stepKey="AdminCheckQuantityAfterOrderPlaced"/> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterPlaceOrder"/> <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterPlaceOrder"> <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> - <argument name="value" value="$$simpleProduct1.sku$$"/> + <argument name="value" value="$$simpleProduct.sku$$"/> </actionGroup> - <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="1000" stepKey="checkProductSourceQtyAfterPlaceOrder"/> - <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="990" stepKey="checkSalableQtyAfterPlaceOrder"/> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="100" stepKey="checkProductSourceQtyAfterPlaceOrder"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="95" stepKey="checkSalableQtyAfterPlaceOrder"/> <!--Admin Area Process Full Invoice--> <comment userInput="Admin - Process invoice for full order" stepKey="InvoiceFullOrder"/> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToProcessInvoice"/> <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMaskInvoice"/> - <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" - stepKey="searchOrderNumInvoice"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNumInvoice"/> <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchInvoice"/> - <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMask1"/> + <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMaskGridForInvoice"/> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRowInvoice"/> <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoice"/> <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> - <waitForPageLoad time="60" stepKey="waitForPageLoad21"/> + <waitForPageLoad time="60" stepKey="waitForPageLoadSubmitInvoice"/> <scrollToTopOfPage stepKey="scrollToTopMessage"/> - <waitForPageLoad stepKey="waitForPageLoad22"/> + <waitForPageLoad stepKey="waitForPageLoadSuccessMessage"/> <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="checkSuccessMessage"/> <!--Admin Area Process Partial Shipping--> <comment userInput="Admin - Ship partial order" stepKey="AdminShipPartialOrder"/> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToCreateShipment"/> <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMask"/> - <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" - stepKey="searchOrderNum"/> - <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearch2"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNum"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchShipping"/> <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMask"/> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShip"/> @@ -154,17 +128,16 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterPartialShipment"/> <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterPartialShipment"> <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> - <argument name="value" value="$$simpleProduct1.sku$$"/> + <argument name="value" value="$$simpleProduct.sku$$"/> </actionGroup> - <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="997" stepKey="checkProductSourceQtyAfterPartialShipment"/> - <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="990" stepKey="checkSalableQtyAfterPartialShipemnt"/> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="97" stepKey="checkProductSourceQtyAfterPartialShipment"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="90" stepKey="checkSalableQtyAfterPartialShipment"/> <!--Admin Area Create Full Credit Memo--> <comment userInput="Admin - Create credit memo for full order" stepKey="AdminCreateCreditMemoFullOrder"/> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToCreateCreditMemo"/> <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMaskCreditMemo"/> - <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" - stepKey="searchOrderNumCreditMemo"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNumCreditMemo"/> <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchCreditMemo"/> <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMaskCreditMemo"/> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRowCreditMemo"/> @@ -178,14 +151,10 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterCreditMemo"/> <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterCreditMemo"> <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> - <argument name="value" value="$$simpleProduct1.sku$$"/> + <argument name="value" value="$$simpleProduct.sku$$"/> </actionGroup> <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="1000" stepKey="checkProductSourceQtyAfterCreditMemo"/> <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="1000" stepKey="checkSalableQtyAfterCreditMemo"/> </test> -</tests> - - - - +</tests> \ No newline at end of file From 349466e3f24b3f95b2dbfd0630ab41d7f6e7d22f Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Tue, 29 Jan 2019 16:19:32 -0600 Subject: [PATCH 009/231] Modifying groups and suite file to remove CreditMemo group used for local verification --- .../Test/Mftf/Suite/msi-suite.xml | 22 ------------------- ...efaultStockAfterFullInvoiceInAdminTest.xml | 5 ++--- 2 files changed, 2 insertions(+), 25 deletions(-) diff --git a/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml b/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml index 3bb5009775ab..a8eb4b6ba8df 100644 --- a/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml +++ b/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml @@ -70,26 +70,4 @@ <magentoCLI stepKey="disableBackup" command = "config:set system/backup/functionality_enabled 0" /> </after> </suite> - <suite name="MSI_Credit_Memo"> - <before> - <magentoCLI stepKey="enableBackup" command = "config:set system/backup/functionality_enabled 1" /> - <magentoCLI stepKey="dbBackup" command="setup:backup" arguments="--db" /> - <magentoCLI stepKey="maintenanceDisable" command="maintenance:disable"/> - <magentoCLI stepKey="disableWYSYWYG" command="config:set cms/wysiwyg/enabled disabled"/> - </before> - <include> - <group name="CreditMemo"/> - </include> - <exclude> - <group name="sort_order_test"/> - <group name="skip"/> - <group name="single_mode"/> - <group name="multi_mode"/> - </exclude> - <after> - <magentoCLI stepKey="dbRollback" command="setup:rollback" arguments="--db-file%3D%24%28ls%20..%2F..%2F..%2F..%2Fvar%2Fbackups%29%20-n" /> - <magentoCLI stepKey="maintenanceDisable" command="maintenance:disable"/> - <magentoCLI stepKey="disableBackup" command = "config:set system/backup/functionality_enabled 0" /> - </after> - </suite> </suites> diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundForOrderWithSimpleProductOnDefaultStockAfterFullInvoiceInAdminTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundForOrderWithSimpleProductOnDefaultStockAfterFullInvoiceInAdminTest.xml index 972a92ca4032..ef337d1f6139 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundForOrderWithSimpleProductOnDefaultStockAfterFullInvoiceInAdminTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundForOrderWithSimpleProductOnDefaultStockAfterFullInvoiceInAdminTest.xml @@ -15,9 +15,8 @@ <description value="Credit memo created with partial Refund for order with Simple product on Default stock after full invoice in Admin"/> <testCaseId value="MSI-1973"/> <severity value="BLOCKER"/> - <!--<group value="msi"/>--> - <!--<group value="multi_mode"/>--> - <group value="CreditMemo"/> + <group value="msi"/> + <group value="multi_mode"/> </annotations> <before> From b3ac3ec546bf415a0191cd6098da793e3538d822 Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Wed, 30 Jan 2019 14:28:06 -0600 Subject: [PATCH 010/231] Due to UI changes - updates to test and Section on selectors for ProductGrid/QuantityBySource --- ...derWithConfigurableProductFromCustomSourceTest.xml | 2 +- ...tForOrderWithSimpleProductFromCustomSourceTest.xml | 2 +- ...derWithConfigurableProductFromCustomSourceTest.xml | 2 +- ...holeOrderWithSimpleProductFromCustomSourceTest.xml | 11 +++++------ ...ultStockAfterFullInvoiceAndShipmentInAdminTest.xml | 1 - ...ctItemsInShipmentForOrderWithSimpleProductTest.xml | 8 ++++---- ...wnloadableProductOnCustomStockFromHomepageTest.xml | 2 +- ...nloadableProductOnDefaultStockFromHomepageTest.xml | 2 +- .../Test/Mftf/Section/AdminProductGridSection.xml | 2 +- 9 files changed, 15 insertions(+), 17 deletions(-) diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminCreatePartialShipmentForOrderWithConfigurableProductFromCustomSourceTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminCreatePartialShipmentForOrderWithConfigurableProductFromCustomSourceTest.xml index 2e0436285c41..f74481478b5a 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminCreatePartialShipmentForOrderWithConfigurableProductFromCustomSourceTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminCreatePartialShipmentForOrderWithConfigurableProductFromCustomSourceTest.xml @@ -218,7 +218,7 @@ <see selector="{{AdminGridRow.rowOne}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeProductNameInGrid"/> <see selector="{{AdminGridRow.rowOne}}" userInput="100" stepKey="seeProductPriceInGrid"/> - <see selector="{{AdminGridRow.rowOne}}" userInput="$$createSource1.source[name]$$ : 97" + <see selector="{{AdminGridRow.rowOne}}" userInput="$$createSource1.source[name]$$ (97)" stepKey="seeProductQuantityInGridSouce1"/> <see selector="{{AdminGridRow.rowOne}}" userInput="$$createStock.stock[name]$$ : 95" stepKey="seeProductSalableQuantityInGridSouce"/> diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminCreatePartialShipmentForOrderWithSimpleProductFromCustomSourceTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminCreatePartialShipmentForOrderWithSimpleProductFromCustomSourceTest.xml index ad729a579d11..741d80d228e7 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminCreatePartialShipmentForOrderWithSimpleProductFromCustomSourceTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminCreatePartialShipmentForOrderWithSimpleProductFromCustomSourceTest.xml @@ -141,7 +141,7 @@ <see selector="{{AdminGridRow.rowOne}}" userInput="{{SimpleMsiProduct.name}}" stepKey="seeProductNameInGrid"/> <see selector="{{AdminGridRow.rowOne}}" userInput="{{SimpleMsiProduct.sku}}" stepKey="seeProductSkuInGrid"/> <see selector="{{AdminGridRow.rowOne}}" userInput="{{SimpleMsiProduct.price}}" stepKey="seeProductPriceInGrid"/> - <see selector="{{AdminGridRow.rowOne}}" userInput="$$createSource.source[name]$$ : 97" stepKey="seeProductQuantityInGrid"/> + <see selector="{{AdminGridRow.rowOne}}" userInput="$$createSource.source[name]$$ (97)" stepKey="seeProductQuantityInGrid"/> <see selector="{{AdminGridRow.rowOne}}" userInput="$$createStock.stock[name]$$ : 95" stepKey="seeProductSalableQuantityInGrid"/> <see selector="{{AdminGridRow.rowOne}}" userInput="Enabled" stepKey="seeProductStatusInGrid"/> <see selector="{{AdminGridRow.rowOne}}" userInput="Main Website" stepKey="seeProductWebsiteInGrid"/> diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminCreateShipmentForWholeOrderWithConfigurableProductFromCustomSourceTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminCreateShipmentForWholeOrderWithConfigurableProductFromCustomSourceTest.xml index 39dd03641b52..a6d46b7979e4 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminCreateShipmentForWholeOrderWithConfigurableProductFromCustomSourceTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminCreateShipmentForWholeOrderWithConfigurableProductFromCustomSourceTest.xml @@ -227,7 +227,7 @@ <see selector="{{AdminGridRow.rowOne}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeProductNameInGrid"/> <see selector="{{AdminGridRow.rowOne}}" userInput="100" stepKey="seeProductPriceInGrid"/> - <see selector="{{AdminGridRow.rowOne}}" userInput="$$createSource1.source[name]$$ : 95" + <see selector="{{AdminGridRow.rowOne}}" userInput="$$createSource1.source[name]$$ (95)" stepKey="seeProductQuantityInGridSouce1"/> <see selector="{{AdminGridRow.rowOne}}" userInput="$$createStock.stock[name]$$ : 95" stepKey="seeProductSalableQuantityInGridSouce"/> diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminCreateShipmentForWholeOrderWithSimpleProductFromCustomSourceTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminCreateShipmentForWholeOrderWithSimpleProductFromCustomSourceTest.xml index 71a9ba031ca4..da1766931410 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminCreateShipmentForWholeOrderWithSimpleProductFromCustomSourceTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminCreateShipmentForWholeOrderWithSimpleProductFromCustomSourceTest.xml @@ -175,18 +175,17 @@ <see selector="{{AdminGridRow.rowOne}}" userInput="{{SimpleMsiProduct.name}}" stepKey="seeProductNameInGrid"/> <see selector="{{AdminGridRow.rowOne}}" userInput="{{SimpleMsiProduct.sku}}" stepKey="seeProductSkuInGrid"/> <see selector="{{AdminGridRow.rowOne}}" userInput="{{SimpleMsiProduct.price}}" stepKey="seeProductPriceInGrid"/> - <see selector="{{AdminGridRow.rowOne}}" userInput="$$createSource1.source[name]$$ : 95" + <see selector="{{AdminGridRow.rowOne}}" userInput="$$createSource1.source[name]$$ (95)" stepKey="seeProductQuantityInGridSouce1"/> - <see selector="{{AdminGridRow.rowOne}}" userInput="$$createSource2.source[name]$$ : 100" + <see selector="{{AdminGridRow.rowOne}}" userInput="$$createSource2.source[name]$$ (100)" stepKey="seeProductQuantityInGridSouce2"/> - <see selector="{{AdminGridRow.rowOne}}" userInput="$$createSource3.source[name]$$ : 100" + <see selector="{{AdminGridRow.rowOne}}" userInput="$$createSource3.source[name]$$ (100)" stepKey="seeProductQuantityInGridSouce3"/> - <see selector="{{AdminGridRow.rowOne}}" userInput="$$createSource4.source[name]$$ : 100" + <see selector="{{AdminGridRow.rowOne}}" userInput="$$createSource4.source[name]$$ (100)" stepKey="seeProductQuantityInGridSouce4"/> <see selector="{{AdminGridRow.rowOne}}" userInput="$$createStock.stock[name]$$ : 395" stepKey="seeProductSalableQuantityInGridSouce"/> <see selector="{{AdminGridRow.rowOne}}" userInput="Enabled" stepKey="seeProductStatusInGrid"/> <see selector="{{AdminGridRow.rowOne}}" userInput="Main Website" stepKey="seeProductWebsiteInGrid"/> </test> -</tests> - +</tests> \ No newline at end of file diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml index eb5e1ed1971a..d6d393fd4150 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedForWholeOrderWithSimpleProductOnDefaultStockAfterFullInvoiceAndShipmentInAdminTest.xml @@ -145,6 +145,5 @@ </actionGroup> <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="100" stepKey="checkProductSourceQtyAfterCreditMemo"/> <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="100" stepKey="checkSalableQtyAfterCreditMemo"/> - </test> </tests> \ No newline at end of file diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminManualSelectingOfSourcesToDeductItemsInShipmentForOrderWithSimpleProductTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminManualSelectingOfSourcesToDeductItemsInShipmentForOrderWithSimpleProductTest.xml index 61c2baf56c95..4dd0e465ec53 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminManualSelectingOfSourcesToDeductItemsInShipmentForOrderWithSimpleProductTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminManualSelectingOfSourcesToDeductItemsInShipmentForOrderWithSimpleProductTest.xml @@ -183,10 +183,10 @@ <see selector="{{AdminGridRow.rowOne}}" userInput="{{SimpleMsiProduct.name}}" stepKey="seeProductNameInGrid"/> <see selector="{{AdminGridRow.rowOne}}" userInput="{{SimpleMsiProduct.sku}}" stepKey="seeProductSkuInGrid"/> <see selector="{{AdminGridRow.rowOne}}" userInput="{{SimpleMsiProduct.price}}" stepKey="seeProductPriceInGrid"/> - <see selector="{{AdminGridRow.rowOne}}" userInput="$$createSource1.source[name]$$ : 100" stepKey="seeProductQuantityInGridSouce1"/> - <see selector="{{AdminGridRow.rowOne}}" userInput="$$createSource2.source[name]$$ : 95" stepKey="seeProductQuantityInGridSouce2"/> - <see selector="{{AdminGridRow.rowOne}}" userInput="$$createSource3.source[name]$$ : 100" stepKey="seeProductQuantityInGridSouce3"/> - <see selector="{{AdminGridRow.rowOne}}" userInput="$$createSource4.source[name]$$ : 100" stepKey="seeProductQuantityInGridSouce4"/> + <see selector="{{AdminGridRow.rowOne}}" userInput="$$createSource1.source[name]$$ (100)" stepKey="seeProductQuantityInGridSouce1"/> + <see selector="{{AdminGridRow.rowOne}}" userInput="$$createSource2.source[name]$$ (95)" stepKey="seeProductQuantityInGridSouce2"/> + <see selector="{{AdminGridRow.rowOne}}" userInput="$$createSource3.source[name]$$ (100)" stepKey="seeProductQuantityInGridSouce3"/> + <see selector="{{AdminGridRow.rowOne}}" userInput="$$createSource4.source[name]$$ (100)" stepKey="seeProductQuantityInGridSouce4"/> <see selector="{{AdminGridRow.rowOne}}" userInput="$$createStock.stock[name]$$ : 395" stepKey="seeProductSalableQuantityInGridSouce"/> <see selector="{{AdminGridRow.rowOne}}" userInput="Enabled" stepKey="seeProductStatusInGrid"/> <see selector="{{AdminGridRow.rowOne}}" userInput="Main Website" stepKey="seeProductWebsiteInGrid"/> diff --git a/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnCustomStockFromHomepageTest.xml b/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnCustomStockFromHomepageTest.xml index ca209af79cab..8dc95841bd54 100644 --- a/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnCustomStockFromHomepageTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnCustomStockFromHomepageTest.xml @@ -196,7 +196,7 @@ <see selector="{{AdminGridRow.rowOne}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeProductNameInGrid"/> <see selector="{{AdminGridRow.rowOne}}" userInput="$100.00" stepKey="seeProductPriceInGrid"/> - <see selector="{{AdminGridRow.rowOne}}" userInput="$$createSource.source[name]$$ : 100" + <see selector="{{AdminGridRow.rowOne}}" userInput="$$createSource.source[name]$$ (100)" stepKey="seeProductQuantityInGrid"/> <see selector="{{AdminGridRow.rowOne}}" userInput="$$createStock.stock[name]$$ : 95" stepKey="seeProductSalableQuantityInGrid"/> diff --git a/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnDefaultStockFromHomepageTest.xml b/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnDefaultStockFromHomepageTest.xml index 79f448ef6cdd..1ec3c5387c12 100644 --- a/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnDefaultStockFromHomepageTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnDefaultStockFromHomepageTest.xml @@ -194,7 +194,7 @@ <see selector="{{AdminGridRow.rowOne}}" userInput="{{colorProductAttribute1.name}}" stepKey="seeProductNameInGrid"/> <see selector="{{AdminGridRow.rowOne}}" userInput="$100.00" stepKey="seeProductPriceInGrid"/> - <see selector="{{AdminGridRow.rowOne}}" userInput="{{_defaultSource.name}} : 100" + <see selector="{{AdminGridRow.rowOne}}" userInput="{{_defaultSource.name}} (100)" stepKey="seeProductQuantityInGrid"/> <see selector="{{AdminGridRow.rowOne}}" userInput="{{_defaultStock.name}} : 95" stepKey="seeProductSalableQuantityInGrid"/> diff --git a/InventoryCatalogAdminUi/Test/Mftf/Section/AdminProductGridSection.xml b/InventoryCatalogAdminUi/Test/Mftf/Section/AdminProductGridSection.xml index cf889291b971..710dacc873af 100644 --- a/InventoryCatalogAdminUi/Test/Mftf/Section/AdminProductGridSection.xml +++ b/InventoryCatalogAdminUi/Test/Mftf/Section/AdminProductGridSection.xml @@ -9,7 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductGridSection"> - <element name="productQtyPerSource" type="text" selector="//tr[{{row}}]//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., 'Quantity per Source')]/preceding-sibling::th) +1 ]//strong[text()='{{sourceName}}']/following-sibling::span" parameterized="true"/> + <element name="productQtyPerSource" type="text" selector="//tr[{{row}}]//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., 'Quantity per Source')]/preceding-sibling::th) +1 ]//*[text()='{{sourceName}}']/following-sibling::span" parameterized="true"/> <element name="productSalableQty" type="text" selector="//tr[{{row}}]//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., 'Salable Quantity')]/preceding-sibling::th) +1 ]//strong[text()='{{stockName}}']/following-sibling::span" parameterized="true"/> </section> </sections> From 8574ab877f87c33e5e1036bb226a4388a3ed81e2 Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Thu, 31 Jan 2019 09:16:53 -0600 Subject: [PATCH 011/231] CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest - fixed incorrect source and stock values in final assert --- ...ctOnDefaultStockAfterFullInvoiceAndPartialShipmentTest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest.xml b/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest.xml index 763f48210ed8..e0eac8c68536 100644 --- a/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest.xml @@ -153,8 +153,8 @@ <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> <argument name="value" value="$$simpleProduct.sku$$"/> </actionGroup> - <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="1000" stepKey="checkProductSourceQtyAfterCreditMemo"/> - <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="1000" stepKey="checkSalableQtyAfterCreditMemo"/> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="100" stepKey="checkProductSourceQtyAfterCreditMemo"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="100" stepKey="checkSalableQtyAfterCreditMemo"/> </test> </tests> \ No newline at end of file From 17f1cb20842515b86a4ad40bb4f619f65698ce5f Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Thu, 31 Jan 2019 15:38:44 -0600 Subject: [PATCH 012/231] Removing CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest.xml --- ...AfterFullInvoiceAndPartialShipmentTest.xml | 160 ------------------ 1 file changed, 160 deletions(-) delete mode 100644 InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest.xml diff --git a/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest.xml b/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest.xml deleted file mode 100644 index e0eac8c68536..000000000000 --- a/InventoryAdminUi/Test/Mftf/Test/CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest.xml +++ /dev/null @@ -1,160 +0,0 @@ -<?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="CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest"> - <annotations> - <stories value="MSI Credit Memo"/> - <title value="Credit Memo created with full refund with Simple product on Default stock after full invoice and partial shipment"/> - <description value="Credit Memo created with full refund with Simple product on Default stock after full invoice and partial shipment"/> - <testCaseId value="MSI-1974"/> - <severity value="BLOCKER"/> - <group value="msi"/> - <group value="multi_mode"/> - </annotations> - - <before> - <createData entity="MsiCustomer1" stepKey="createCustomer"/> - <createData entity="BasicMsiStock1" stepKey="createStock"/> - <createData entity="FullSource1" stepKey="createSource"/> - <createData entity="SourceStockLinked1" stepKey="linkSourceStock"> - <requiredEntity createDataKey="createStock"/> - <requiredEntity createDataKey="createSource"/> - </createData> - <createData entity="SimpleSubCategory" stepKey="simpleCategory"/> - <createData entity="SimpleProduct" stepKey="simpleProduct"> - <field key="qty">100.00</field> - <requiredEntity createDataKey="simpleCategory"/> - </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <waitForPageLoad stepKey="waitForDashboardLoad"/> - </before> - <after> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> - <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <deleteData createDataKey="createStock" stepKey="deleteStock"/> - <deleteData createDataKey="simpleCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> - </after> - - <!--Login To storefront as Customer--> - <comment userInput="Login to storefront as customer." stepKey="loginToStorefrontComment"/> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefront"> - <argument name="Customer" value="$$createCustomer$$"/> - </actionGroup> - - <!--Purchase product once logged in--> - <comment userInput="Purchase 5 simple product" stepKey="purchaseSimpleProductComment"/> - <amOnPage url="{{StorefrontCategoryPage.url($$simpleCategory.name$$)}}" stepKey="navigateToCategoryPage"/> - <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($$simpleProduct.name$$)}}" stepKey="moveMouseOverProduct" /> - <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$simpleProduct.name$$)}}" stepKey="clickAddToCart" /> - <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart($$simpleProduct.name$$)}}" time="30" stepKey="assertMessage"/> - <waitForText userInput="1" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> - <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> - <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForViewAndEditCartVisible"/> - <clearField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct.name$$)}}" stepKey="clearField"/> - <fillField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct.name$$)}}" userInput="5" stepKey="setProductQtyToFiftyInMiniCart"/> - <click selector="{{StorefrontMinicartSection.itemQuantityUpdate($$simpleProduct.name$$)}}" stepKey="updateQtyInMiniCart"/> - <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForPaymentSelectionPageLoad"/> - <click selector=".continue" stepKey="clickOnNextCheckout"/> - <waitForPageLoad stepKey="waitForPageLoadCheckout"/> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyOrderPayment"/> - <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButtonVisible"/> - <see selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{US_Address_TX.street[0]}}" stepKey="chooseBillingAddress"/> - <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="placeOrder"/> - <waitForPageLoad stepKey="waitUntilOrderPlaced"/> - <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> - <see selector="{{CheckoutSuccessMainSection.success}}" userInput="Your order number is:" stepKey="checkOrderPlaceSuccessMessage"/> - - <!--Admin Area Check ordered quantity--> - <comment userInput="Admin - Check ordered quantity" stepKey="AdminCheckOrderedQuantity"/> - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderListPage"/> - <waitForLoadingMaskToDisappear stepKey="waitOrderListPageLoad"/> - <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrder"/> - <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearch1"/> - <waitForLoadingMaskToDisappear stepKey="waitFilteredOrderListPageLoad"/> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="navigateToOrderViewPage"/> - <waitForElementVisible selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" stepKey="waitForViewOrderedQuantity"/> - <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Ordered 5" stepKey="orderedQuantity"/> - - <!--Admin Area Check source quantity and salable quantity--> - <comment userInput="Admin - Check Source quantity and salable quantity after order placed" stepKey="AdminCheckQuantityAfterOrderPlaced"/> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterPlaceOrder"/> - <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterPlaceOrder"> - <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> - <argument name="value" value="$$simpleProduct.sku$$"/> - </actionGroup> - <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="100" stepKey="checkProductSourceQtyAfterPlaceOrder"/> - <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="95" stepKey="checkSalableQtyAfterPlaceOrder"/> - - <!--Admin Area Process Full Invoice--> - <comment userInput="Admin - Process invoice for full order" stepKey="InvoiceFullOrder"/> - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToProcessInvoice"/> - <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMaskInvoice"/> - <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNumInvoice"/> - <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchInvoice"/> - <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMaskGridForInvoice"/> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRowInvoice"/> - <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoice"/> - <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> - <waitForPageLoad time="60" stepKey="waitForPageLoadSubmitInvoice"/> - <scrollToTopOfPage stepKey="scrollToTopMessage"/> - <waitForPageLoad stepKey="waitForPageLoadSuccessMessage"/> - <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="checkSuccessMessage"/> - - <!--Admin Area Process Partial Shipping--> - <comment userInput="Admin - Ship partial order" stepKey="AdminShipPartialOrder"/> - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToCreateShipment"/> - <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMask"/> - <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNum"/> - <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchShipping"/> - <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMask"/> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> - <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShip"/> - <waitForLoadingMaskToDisappear stepKey="waitForShipLoadingMask"/> - <fillField selector="{{AdminShipmentItemsSection.itemQtyToShip('1')}}" userInput="3" stepKey="shipPartialQuantity3"/> - <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" stepKey="submitShipment"/> - <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The shipment has been created." stepKey="seeShipmentCreateSuccess"/> - - <!--Admin Area Check source quantity and salable quantity after partial shipment--> - <comment userInput="Admin - Check Source quantity and salable quantity after partial shipment" stepKey="AdminCheckQuantityAfterPartialShipment"/> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterPartialShipment"/> - <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterPartialShipment"> - <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> - <argument name="value" value="$$simpleProduct.sku$$"/> - </actionGroup> - <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="97" stepKey="checkProductSourceQtyAfterPartialShipment"/> - <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="90" stepKey="checkSalableQtyAfterPartialShipment"/> - - <!--Admin Area Create Full Credit Memo--> - <comment userInput="Admin - Create credit memo for full order" stepKey="AdminCreateCreditMemoFullOrder"/> - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToCreateCreditMemo"/> - <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMaskCreditMemo"/> - <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNumCreditMemo"/> - <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchCreditMemo"/> - <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMaskCreditMemo"/> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRowCreditMemo"/> - <click selector="{{AdminOrderDetailsMainActionsSection.creditMemo}}" stepKey="clickCreditMemo"/> - <click selector="{{AdminCreditMemoItemsSection.itemReturnToStock('1')}}" stepKey="returnToStockCheckbox"/> - <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickSubmit"/> - - - <!--Admin Area Check quantities after Credit Memo--> - <comment userInput="Admin - Check Source quantity and salable quantity after credit memo" stepKey="AdminCheckQuantityAfterCreditMemo"/> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterCreditMemo"/> - <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterCreditMemo"> - <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> - <argument name="value" value="$$simpleProduct.sku$$"/> - </actionGroup> - <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="100" stepKey="checkProductSourceQtyAfterCreditMemo"/> - <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="100" stepKey="checkSalableQtyAfterCreditMemo"/> - - </test> -</tests> \ No newline at end of file From 0f648d9cec183a3eeb0cb4a02520f38f6be094b9 Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Fri, 1 Feb 2019 13:51:17 -0600 Subject: [PATCH 013/231] Remove whitespace --- .../Test/Mftf/Suite/msi-suite.xml | 22 +++++++++++++++++++ .../Mftf/Section/AdminProductGridSection.xml | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml b/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml index a8eb4b6ba8df..2dcb0c65fc06 100644 --- a/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml +++ b/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml @@ -70,4 +70,26 @@ <magentoCLI stepKey="disableBackup" command = "config:set system/backup/functionality_enabled 0" /> </after> </suite> + <suite name="JoshCarter"> + <before> + <magentoCLI stepKey="enableBackup" command = "config:set system/backup/functionality_enabled 1" /> + <magentoCLI stepKey="dbBackup" command="setup:backup" arguments="--db" /> + <magentoCLI stepKey="maintenanceDisable" command="maintenance:disable"/> + <magentoCLI stepKey="disableWYSYWYG" command="config:set cms/wysiwyg/enabled disabled"/> + </before> + <include> + <group name="JoshCarter"/> + </include> + <exclude> + <group name="sort_order_test"/> + <group name="skip"/> + <group name="single_mode"/> + <group name="multi_mode"/> + </exclude> + <after> + <magentoCLI stepKey="dbRollback" command="setup:rollback" arguments="--db-file%3D%24%28ls%20..%2F..%2F..%2F..%2Fvar%2Fbackups%29%20-n" /> + <magentoCLI stepKey="maintenanceDisable" command="maintenance:disable"/> + <magentoCLI stepKey="disableBackup" command = "config:set system/backup/functionality_enabled 0" /> + </after> + </suite> </suites> diff --git a/InventoryCatalogAdminUi/Test/Mftf/Section/AdminProductGridSection.xml b/InventoryCatalogAdminUi/Test/Mftf/Section/AdminProductGridSection.xml index 710dacc873af..5a3b3708d788 100644 --- a/InventoryCatalogAdminUi/Test/Mftf/Section/AdminProductGridSection.xml +++ b/InventoryCatalogAdminUi/Test/Mftf/Section/AdminProductGridSection.xml @@ -12,4 +12,4 @@ <element name="productQtyPerSource" type="text" selector="//tr[{{row}}]//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., 'Quantity per Source')]/preceding-sibling::th) +1 ]//*[text()='{{sourceName}}']/following-sibling::span" parameterized="true"/> <element name="productSalableQty" type="text" selector="//tr[{{row}}]//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., 'Salable Quantity')]/preceding-sibling::th) +1 ]//strong[text()='{{stockName}}']/following-sibling::span" parameterized="true"/> </section> -</sections> +</sections> \ No newline at end of file From 49e2fc0837c30f61c00e07843c3f173886ce576a Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Fri, 1 Feb 2019 14:10:02 -0600 Subject: [PATCH 014/231] Updating Selector to remove strong tag --- .../Test/Mftf/Section/AdminProductGridSection.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InventoryCatalogAdminUi/Test/Mftf/Section/AdminProductGridSection.xml b/InventoryCatalogAdminUi/Test/Mftf/Section/AdminProductGridSection.xml index 5a3b3708d788..6354b8f4538a 100644 --- a/InventoryCatalogAdminUi/Test/Mftf/Section/AdminProductGridSection.xml +++ b/InventoryCatalogAdminUi/Test/Mftf/Section/AdminProductGridSection.xml @@ -10,6 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductGridSection"> <element name="productQtyPerSource" type="text" selector="//tr[{{row}}]//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., 'Quantity per Source')]/preceding-sibling::th) +1 ]//*[text()='{{sourceName}}']/following-sibling::span" parameterized="true"/> - <element name="productSalableQty" type="text" selector="//tr[{{row}}]//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., 'Salable Quantity')]/preceding-sibling::th) +1 ]//strong[text()='{{stockName}}']/following-sibling::span" parameterized="true"/> + <element name="productSalableQty" type="text" selector="//tr[{{row}}]//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., 'Salable Quantity')]/preceding-sibling::th) +1 ]//*[text()='{{stockName}}']/following-sibling::span" parameterized="true"/> </section> </sections> \ No newline at end of file From 434dcf0a00f09ef5d72308269b54799187a17edb Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Fri, 1 Feb 2019 15:34:41 -0600 Subject: [PATCH 015/231] Fixing suite file which had local testing suites --- .../Test/Mftf/Suite/msi-suite.xml | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml b/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml index 2dcb0c65fc06..a8eb4b6ba8df 100644 --- a/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml +++ b/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml @@ -70,26 +70,4 @@ <magentoCLI stepKey="disableBackup" command = "config:set system/backup/functionality_enabled 0" /> </after> </suite> - <suite name="JoshCarter"> - <before> - <magentoCLI stepKey="enableBackup" command = "config:set system/backup/functionality_enabled 1" /> - <magentoCLI stepKey="dbBackup" command="setup:backup" arguments="--db" /> - <magentoCLI stepKey="maintenanceDisable" command="maintenance:disable"/> - <magentoCLI stepKey="disableWYSYWYG" command="config:set cms/wysiwyg/enabled disabled"/> - </before> - <include> - <group name="JoshCarter"/> - </include> - <exclude> - <group name="sort_order_test"/> - <group name="skip"/> - <group name="single_mode"/> - <group name="multi_mode"/> - </exclude> - <after> - <magentoCLI stepKey="dbRollback" command="setup:rollback" arguments="--db-file%3D%24%28ls%20..%2F..%2F..%2F..%2Fvar%2Fbackups%29%20-n" /> - <magentoCLI stepKey="maintenanceDisable" command="maintenance:disable"/> - <magentoCLI stepKey="disableBackup" command = "config:set system/backup/functionality_enabled 0" /> - </after> - </suite> </suites> From d9cbb66e6311d8b283b01899486d21cf773ed931 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Sun, 3 Feb 2019 18:06:47 +0200 Subject: [PATCH 016/231] MSI-1912: Get sku from order configurable product plugin added --- .../Plugin/Sales/GetSkuFromOrderItem.php | 40 +++++++++++++++++++ InventoryConfigurableProduct/etc/di.xml | 4 ++ 2 files changed, 44 insertions(+) create mode 100644 InventoryConfigurableProduct/Plugin/Sales/GetSkuFromOrderItem.php diff --git a/InventoryConfigurableProduct/Plugin/Sales/GetSkuFromOrderItem.php b/InventoryConfigurableProduct/Plugin/Sales/GetSkuFromOrderItem.php new file mode 100644 index 000000000000..6a4041a6d2b3 --- /dev/null +++ b/InventoryConfigurableProduct/Plugin/Sales/GetSkuFromOrderItem.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryConfigurableProduct\Plugin\Sales; + +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\InventorySalesApi\Model\GetSkuFromOrderItemInterface; +use Magento\Sales\Api\Data\OrderItemInterface; + +/** + * Get simple product SKU from configurable order item + */ +class GetSkuFromOrderItem +{ + /** + * @param GetSkuFromOrderItemInterface $subject + * @param callable $proceed + * @param OrderItemInterface $orderItem + * @return string + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function aroundExecute( + GetSkuFromOrderItemInterface $subject, + callable $proceed, + OrderItemInterface $orderItem + ): string { + if ($orderItem->getProductType() !== Configurable::TYPE_CODE) { + return $proceed($orderItem); + } + + $orderItemOptions = $orderItem->getProductOptions(); + $sku = $orderItemOptions['simple_sku']; + + return $sku; + } +} diff --git a/InventoryConfigurableProduct/etc/di.xml b/InventoryConfigurableProduct/etc/di.xml index 9be2ba9af8ea..a8b4f776a2fe 100644 --- a/InventoryConfigurableProduct/etc/di.xml +++ b/InventoryConfigurableProduct/etc/di.xml @@ -14,4 +14,8 @@ </argument> </arguments> </type> + <type name="Magento\InventorySalesApi\Model\GetSkuFromOrderItemInterface"> + <plugin name="get_configurable_option_sku_from_order" + type="Magento\InventoryConfigurableProduct\Plugin\Sales\GetSkuFromOrderItem"/> + </type> </config> From c06c129fc2f37313908c99b15ff8e2ca1ff25e71 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Mon, 4 Feb 2019 08:56:22 +0200 Subject: [PATCH 017/231] MSI-1912: fixed dependencies --- InventoryConfigurableProduct/composer.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/InventoryConfigurableProduct/composer.json b/InventoryConfigurableProduct/composer.json index d3b07082cf65..e96804e65815 100644 --- a/InventoryConfigurableProduct/composer.json +++ b/InventoryConfigurableProduct/composer.json @@ -9,7 +9,9 @@ "magento/module-inventory-catalog-api": "*", "magento/module-inventory-indexer": "*", "magento/module-store": "*", - "magento/module-catalog-inventory": "*" + "magento/module-catalog-inventory": "*", + "magento/module-sales": "*", + "magento/module-configurable-product": "*" }, "suggest": { "magento/module-configurable-product": "*" From 8fc67874408aef69492b94ee50258ecfa746c473 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Wed, 20 Feb 2019 14:25:00 +0200 Subject: [PATCH 018/231] MSI-2043: disabled source selection when disabled manage stock --- .../Block/Adminhtml/Order/View/ShipButton.php | 15 +++++++++-- .../Observer/NewShipmentLoadBefore.php | 25 +++++++++++++++---- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/InventoryShippingAdminUi/Block/Adminhtml/Order/View/ShipButton.php b/InventoryShippingAdminUi/Block/Adminhtml/Order/View/ShipButton.php index a070fd5b5503..bf0e9e6bd7c0 100644 --- a/InventoryShippingAdminUi/Block/Adminhtml/Order/View/ShipButton.php +++ b/InventoryShippingAdminUi/Block/Adminhtml/Order/View/ShipButton.php @@ -9,6 +9,8 @@ use Magento\Backend\Block\Widget\Container; use Magento\Backend\Block\Widget\Context; +use Magento\CatalogInventory\Api\StockConfigurationInterface; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Registry; use Magento\InventoryShippingAdminUi\Model\IsWebsiteInMultiSourceMode; @@ -29,21 +31,30 @@ class ShipButton extends Container */ private $isWebsiteInMultiSourceMode; + /** + * @var StockConfigurationInterface + */ + private $stockConfiguration; + /** * @param Context $context * @param Registry $registry * @param IsWebsiteInMultiSourceMode $isWebsiteInMultiSourceMode * @param array $data + * @param StockConfigurationInterface $stockConfiguration */ public function __construct( Context $context, Registry $registry, IsWebsiteInMultiSourceMode $isWebsiteInMultiSourceMode, - array $data = [] + array $data = [], + StockConfigurationInterface $stockConfiguration = null ) { parent::__construct($context, $data); $this->registry = $registry; $this->isWebsiteInMultiSourceMode = $isWebsiteInMultiSourceMode; + $this->stockConfiguration = $stockConfiguration ?? + ObjectManager::getInstance()->get(StockConfigurationInterface::class); } /** @@ -55,7 +66,7 @@ protected function _prepareLayout() $order = $this->registry->registry('current_order'); $websiteId = (int)$order->getStore()->getWebsiteId(); - if ($this->isWebsiteInMultiSourceMode->execute($websiteId)) { + if ($this->isWebsiteInMultiSourceMode->execute($websiteId) && $this->stockConfiguration->getManageStock()) { $this->buttonList->update( 'order_ship', 'onclick', diff --git a/InventoryShippingAdminUi/Observer/NewShipmentLoadBefore.php b/InventoryShippingAdminUi/Observer/NewShipmentLoadBefore.php index c8dd6ac5fb66..8eb46b0b679e 100644 --- a/InventoryShippingAdminUi/Observer/NewShipmentLoadBefore.php +++ b/InventoryShippingAdminUi/Observer/NewShipmentLoadBefore.php @@ -7,13 +7,15 @@ namespace Magento\InventoryShippingAdminUi\Observer; -use Magento\Framework\Event\ObserverInterface; -use Magento\Framework\Event\Observer as EventObserver; +use Magento\CatalogInventory\Api\StockConfigurationInterface; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\Response\RedirectInterface; -use Magento\Sales\Model\OrderRepository; -use Magento\InventoryShippingAdminUi\Model\IsWebsiteInMultiSourceMode; +use Magento\Framework\Event\Observer as EventObserver; +use Magento\Framework\Event\ObserverInterface; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\InventoryShippingAdminUi\Model\IsWebsiteInMultiSourceMode; +use Magento\Sales\Model\OrderRepository; /** * Redirect to source selection page @@ -35,19 +37,28 @@ class NewShipmentLoadBefore implements ObserverInterface */ private $redirect; + /** + * @var StockConfigurationInterface + */ + private $stockConfiguration; + /** * @param OrderRepository $orderRepository * @param IsWebsiteInMultiSourceMode $isWebsiteInMultiSourceMode * @param RedirectInterface $redirect + * @param StockConfigurationInterface $stockConfiguration */ public function __construct( OrderRepository $orderRepository, IsWebsiteInMultiSourceMode $isWebsiteInMultiSourceMode, - RedirectInterface $redirect + RedirectInterface $redirect, + StockConfigurationInterface $stockConfiguration = null ) { $this->orderRepository = $orderRepository; $this->isWebsiteInMultiSourceMode = $isWebsiteInMultiSourceMode; $this->redirect = $redirect; + $this->stockConfiguration = $stockConfiguration ?? + ObjectManager::getInstance()->get(StockConfigurationInterface::class); } /** @@ -56,6 +67,10 @@ public function __construct( */ public function execute(EventObserver $observer) { + if (!$this->stockConfiguration->getManageStock()) { + return; + } + $request = $observer->getEvent()->getRequest(); $controller = $observer->getEvent()->getControllerAction(); From 9d79813c1fcba0d969ad3ed349c8000c4853c423 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Wed, 20 Feb 2019 20:06:29 +0200 Subject: [PATCH 019/231] MSI-2043: fixed static tests --- .../Block/Adminhtml/Order/View/ShipButton.php | 1 + InventoryShippingAdminUi/composer.json | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/InventoryShippingAdminUi/Block/Adminhtml/Order/View/ShipButton.php b/InventoryShippingAdminUi/Block/Adminhtml/Order/View/ShipButton.php index bf0e9e6bd7c0..ff17adbed7c2 100644 --- a/InventoryShippingAdminUi/Block/Adminhtml/Order/View/ShipButton.php +++ b/InventoryShippingAdminUi/Block/Adminhtml/Order/View/ShipButton.php @@ -80,6 +80,7 @@ protected function _prepareLayout() * Source Selection URL getter * * @return string + * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) */ public function getSourceSelectionUrl() { diff --git a/InventoryShippingAdminUi/composer.json b/InventoryShippingAdminUi/composer.json index d3cd28df10b8..4d81bf864de1 100644 --- a/InventoryShippingAdminUi/composer.json +++ b/InventoryShippingAdminUi/composer.json @@ -11,7 +11,8 @@ "magento/module-inventory-source-selection-api": "*", "magento/module-sales": "*", "magento/module-shipping": "*", - "magento/module-ui": "*" + "magento/module-ui": "*", + "magento/module-catalog-inventory": "*" }, "type": "magento2-module", "license": [ From cc44ccccfa05f2b5532fce782ebc0982e5becbce Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Wed, 6 Mar 2019 01:06:54 +0200 Subject: [PATCH 020/231] MSI-2062: InventoryExportStock module initial + InventoryExportStockAPI module --- InventoryExportStock/LICENSE.txt | 48 +++++++++++++++++++ InventoryExportStock/LICENSE_AFL.txt | 48 +++++++++++++++++++ InventoryExportStock/README.md | 0 InventoryExportStock/composer.json | 22 +++++++++ InventoryExportStock/etc/module.xml | 10 ++++ InventoryExportStock/registration.php | 12 +++++ .../ExportStockDataSearchResultInterface.php | 32 +++++++++++++ .../Api/ExportStockDataInterface.php | 27 +++++++++++ InventoryExportStockApi/LICENSE.txt | 48 +++++++++++++++++++ InventoryExportStockApi/LICENSE_AFL.txt | 48 +++++++++++++++++++ InventoryExportStockApi/README.md | 0 InventoryExportStockApi/composer.json | 21 ++++++++ InventoryExportStockApi/etc/module.xml | 10 ++++ InventoryExportStockApi/registration.php | 12 +++++ 14 files changed, 338 insertions(+) create mode 100644 InventoryExportStock/LICENSE.txt create mode 100644 InventoryExportStock/LICENSE_AFL.txt create mode 100644 InventoryExportStock/README.md create mode 100644 InventoryExportStock/composer.json create mode 100644 InventoryExportStock/etc/module.xml create mode 100644 InventoryExportStock/registration.php create mode 100644 InventoryExportStockApi/Api/Data/ExportStockDataSearchResultInterface.php create mode 100644 InventoryExportStockApi/Api/ExportStockDataInterface.php create mode 100644 InventoryExportStockApi/LICENSE.txt create mode 100644 InventoryExportStockApi/LICENSE_AFL.txt create mode 100644 InventoryExportStockApi/README.md create mode 100644 InventoryExportStockApi/composer.json create mode 100644 InventoryExportStockApi/etc/module.xml create mode 100644 InventoryExportStockApi/registration.php diff --git a/InventoryExportStock/LICENSE.txt b/InventoryExportStock/LICENSE.txt new file mode 100644 index 000000000000..49525fd99da9 --- /dev/null +++ b/InventoryExportStock/LICENSE.txt @@ -0,0 +1,48 @@ + +Open Software License ("OSL") v. 3.0 + +This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Open Software License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/InventoryExportStock/LICENSE_AFL.txt b/InventoryExportStock/LICENSE_AFL.txt new file mode 100644 index 000000000000..f39d641b18a1 --- /dev/null +++ b/InventoryExportStock/LICENSE_AFL.txt @@ -0,0 +1,48 @@ + +Academic Free License ("AFL") v. 3.0 + +This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Academic Free License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/InventoryExportStock/README.md b/InventoryExportStock/README.md new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/InventoryExportStock/composer.json b/InventoryExportStock/composer.json new file mode 100644 index 000000000000..cd854d1e47c1 --- /dev/null +++ b/InventoryExportStock/composer.json @@ -0,0 +1,22 @@ +{ + "name": "magento/module-inventory-export-stock", + "description": "N/A", + "require": { + "php": "~7.1.3||~7.2.0", + "magento/module-inventory-api": "*", + "magento/module-inventory-export-stock-api": "*" + }, + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\InventoryExportStock\\": "" + } + } +} diff --git a/InventoryExportStock/etc/module.xml b/InventoryExportStock/etc/module.xml new file mode 100644 index 000000000000..68059a339174 --- /dev/null +++ b/InventoryExportStock/etc/module.xml @@ -0,0 +1,10 @@ +<?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:Module/etc/module.xsd"> + <module name="Magento_InventoryExportStock" setup_version="1.0.0"/> +</config> diff --git a/InventoryExportStock/registration.php b/InventoryExportStock/registration.php new file mode 100644 index 000000000000..587a5f886339 --- /dev/null +++ b/InventoryExportStock/registration.php @@ -0,0 +1,12 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +\Magento\Framework\Component\ComponentRegistrar::register( + \Magento\Framework\Component\ComponentRegistrar::MODULE, + 'Magento_InventoryExportStock', + __DIR__ +); diff --git a/InventoryExportStockApi/Api/Data/ExportStockDataSearchResultInterface.php b/InventoryExportStockApi/Api/Data/ExportStockDataSearchResultInterface.php new file mode 100644 index 000000000000..083e8e114e57 --- /dev/null +++ b/InventoryExportStockApi/Api/Data/ExportStockDataSearchResultInterface.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryExportStockApi\Api\Data; + +use Magento\Framework\Api\SearchResultsInterface; + +/** + * Interface for ExportStockDataSearchResult + * @api + */ +interface ExportStockDataSearchResultInterface extends SearchResultsInterface +{ + /** + * Get stock data array + * + * @return array + */ + public function getItems(): array; + + /** + * Set stock data array + * + * @param array $items + * @return $this + */ + public function setItems(array $items): ExportStockDataSearchResultInterface; +} diff --git a/InventoryExportStockApi/Api/ExportStockDataInterface.php b/InventoryExportStockApi/Api/ExportStockDataInterface.php new file mode 100644 index 000000000000..46fff5c656e8 --- /dev/null +++ b/InventoryExportStockApi/Api/ExportStockDataInterface.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryExportStockApi\Api; + +use Magento\Framework\Api\SearchCriteriaInterface; +use Magento\InventoryExportStockApi\Api\Data\ExportStockDataSearchResultInterface; + +/** + * Interface for ExportStockData + * @api + */ +interface ExportStockDataInterface +{ + /** + * Provides stock export data + * + * @param SearchCriteriaInterface $searchCriteria + * @param int $qtyForNotManageStock + * @return ExportStockDataSearchResultInterface + */ + public function execute(SearchCriteriaInterface $searchCriteria, int $qtyForNotManageStock): ExportStockDataSearchResultInterface; +} diff --git a/InventoryExportStockApi/LICENSE.txt b/InventoryExportStockApi/LICENSE.txt new file mode 100644 index 000000000000..49525fd99da9 --- /dev/null +++ b/InventoryExportStockApi/LICENSE.txt @@ -0,0 +1,48 @@ + +Open Software License ("OSL") v. 3.0 + +This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Open Software License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/InventoryExportStockApi/LICENSE_AFL.txt b/InventoryExportStockApi/LICENSE_AFL.txt new file mode 100644 index 000000000000..f39d641b18a1 --- /dev/null +++ b/InventoryExportStockApi/LICENSE_AFL.txt @@ -0,0 +1,48 @@ + +Academic Free License ("AFL") v. 3.0 + +This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Academic Free License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/InventoryExportStockApi/README.md b/InventoryExportStockApi/README.md new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/InventoryExportStockApi/composer.json b/InventoryExportStockApi/composer.json new file mode 100644 index 000000000000..8b36ac109ebb --- /dev/null +++ b/InventoryExportStockApi/composer.json @@ -0,0 +1,21 @@ +{ + "name": "magento/module-inventory-export-stock-api", + "description": "N/A", + "require": { + "php": "~7.1.3||~7.2.0", + "magento/module-inventory-api": "*", + }, + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\InventoryExportStockApi\\": "" + } + } +} diff --git a/InventoryExportStockApi/etc/module.xml b/InventoryExportStockApi/etc/module.xml new file mode 100644 index 000000000000..7d6f4cd1c561 --- /dev/null +++ b/InventoryExportStockApi/etc/module.xml @@ -0,0 +1,10 @@ +<?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:Module/etc/module.xsd"> + <module name="Magento_InventoryExportStockApi" setup_version="1.0.0"/> +</config> diff --git a/InventoryExportStockApi/registration.php b/InventoryExportStockApi/registration.php new file mode 100644 index 000000000000..daad5737aa1e --- /dev/null +++ b/InventoryExportStockApi/registration.php @@ -0,0 +1,12 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +\Magento\Framework\Component\ComponentRegistrar::register( + \Magento\Framework\Component\ComponentRegistrar::MODULE, + 'Magento_InventoryExportStockApi', + __DIR__ +); From 5460277e7a6e4ee18a10408fdf90190371acfc5f Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Wed, 13 Mar 2019 15:43:49 +0200 Subject: [PATCH 021/231] MSI-2043: check ech order item for manageability --- .../Block/Adminhtml/Order/View/ShipButton.php | 16 ++-- .../Model/IsOrderStockManageable.php | 81 +++++++++++++++++++ .../Observer/NewShipmentLoadBefore.php | 18 ++--- 3 files changed, 98 insertions(+), 17 deletions(-) create mode 100644 InventoryShippingAdminUi/Model/IsOrderStockManageable.php diff --git a/InventoryShippingAdminUi/Block/Adminhtml/Order/View/ShipButton.php b/InventoryShippingAdminUi/Block/Adminhtml/Order/View/ShipButton.php index ff17adbed7c2..998d64f7dd0c 100644 --- a/InventoryShippingAdminUi/Block/Adminhtml/Order/View/ShipButton.php +++ b/InventoryShippingAdminUi/Block/Adminhtml/Order/View/ShipButton.php @@ -9,9 +9,9 @@ use Magento\Backend\Block\Widget\Container; use Magento\Backend\Block\Widget\Context; -use Magento\CatalogInventory\Api\StockConfigurationInterface; use Magento\Framework\App\ObjectManager; use Magento\Framework\Registry; +use Magento\InventoryShippingAdminUi\Model\IsOrderStockManageable; use Magento\InventoryShippingAdminUi\Model\IsWebsiteInMultiSourceMode; /** @@ -32,29 +32,29 @@ class ShipButton extends Container private $isWebsiteInMultiSourceMode; /** - * @var StockConfigurationInterface + * @var IsOrderStockManageable */ - private $stockConfiguration; + private $isOrderStockManageable; /** * @param Context $context * @param Registry $registry * @param IsWebsiteInMultiSourceMode $isWebsiteInMultiSourceMode * @param array $data - * @param StockConfigurationInterface $stockConfiguration + * @param IsOrderStockManageable $isOrderStockManageable */ public function __construct( Context $context, Registry $registry, IsWebsiteInMultiSourceMode $isWebsiteInMultiSourceMode, array $data = [], - StockConfigurationInterface $stockConfiguration = null + IsOrderStockManageable $isOrderStockManageable = null ) { parent::__construct($context, $data); $this->registry = $registry; $this->isWebsiteInMultiSourceMode = $isWebsiteInMultiSourceMode; - $this->stockConfiguration = $stockConfiguration ?? - ObjectManager::getInstance()->get(StockConfigurationInterface::class); + $this->isOrderStockManageable = $isOrderStockManageable ?? + ObjectManager::getInstance()->get(IsOrderStockManageable::class); } /** @@ -66,7 +66,7 @@ protected function _prepareLayout() $order = $this->registry->registry('current_order'); $websiteId = (int)$order->getStore()->getWebsiteId(); - if ($this->isWebsiteInMultiSourceMode->execute($websiteId) && $this->stockConfiguration->getManageStock()) { + if ($this->isWebsiteInMultiSourceMode->execute($websiteId) && $this->isOrderStockManageable->execute($order)) { $this->buttonList->update( 'order_ship', 'onclick', diff --git a/InventoryShippingAdminUi/Model/IsOrderStockManageable.php b/InventoryShippingAdminUi/Model/IsOrderStockManageable.php new file mode 100644 index 000000000000..5fa4e1f83fb9 --- /dev/null +++ b/InventoryShippingAdminUi/Model/IsOrderStockManageable.php @@ -0,0 +1,81 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryShippingAdminUi\Model; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\InventoryApi\Api\Data\StockInterface; +use Magento\InventoryApi\Api\StockRepositoryInterface; +use Magento\InventoryConfiguration\Model\GetStockItemConfiguration; +use Magento\Sales\Api\Data\OrderInterface; + +/** + * Is stock manageable for certain order + */ +class IsOrderStockManageable +{ + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var GetStockItemConfiguration + */ + private $getStockItemConfiguration; + + /** + * @var StockRepositoryInterface + */ + private $stockRepository; + + /** + * @param ProductRepositoryInterface $productRepository + * @param GetStockItemConfiguration $getStockItemConfiguration + * @param StockRepositoryInterface $stockRepository + */ + public function __construct( + ProductRepositoryInterface $productRepository, + GetStockItemConfiguration $getStockItemConfiguration, + StockRepositoryInterface $stockRepository + ) { + $this->productRepository = $productRepository; + $this->getStockItemConfiguration = $getStockItemConfiguration; + $this->stockRepository = $stockRepository; + } + + /** + * Check if stock manageable for certain order + * + * @param OrderInterface $order + * @return bool + */ + public function execute(OrderInterface $order): bool + { + $stocks = $this->stockRepository->getList(); + $orderItems = $order->getItems(); + foreach ($orderItems as $orderItem) { + $productId = $orderItem->getProductId(); + /** @var Product $product */ + $product = $this->productRepository->getById($productId); + /** @var StockInterface $stock */ + foreach ($stocks as $stock) { + $inventoryConfiguration = $this->getStockItemConfiguration->execute( + $product->getSku(), + $stock->getStockId() + ); + + if ($inventoryConfiguration->isManageStock()) { + return true; + } + } + } + + return false; + } +} diff --git a/InventoryShippingAdminUi/Observer/NewShipmentLoadBefore.php b/InventoryShippingAdminUi/Observer/NewShipmentLoadBefore.php index 8eb46b0b679e..eb5617bf7024 100644 --- a/InventoryShippingAdminUi/Observer/NewShipmentLoadBefore.php +++ b/InventoryShippingAdminUi/Observer/NewShipmentLoadBefore.php @@ -14,6 +14,7 @@ use Magento\Framework\Event\ObserverInterface; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\InventoryShippingAdminUi\Model\IsOrderStockManageable; use Magento\InventoryShippingAdminUi\Model\IsWebsiteInMultiSourceMode; use Magento\Sales\Model\OrderRepository; @@ -40,25 +41,25 @@ class NewShipmentLoadBefore implements ObserverInterface /** * @var StockConfigurationInterface */ - private $stockConfiguration; + private $isOrderStockManageable; /** * @param OrderRepository $orderRepository * @param IsWebsiteInMultiSourceMode $isWebsiteInMultiSourceMode * @param RedirectInterface $redirect - * @param StockConfigurationInterface $stockConfiguration + * @param StockConfigurationInterface $isOrderStockManageable */ public function __construct( OrderRepository $orderRepository, IsWebsiteInMultiSourceMode $isWebsiteInMultiSourceMode, RedirectInterface $redirect, - StockConfigurationInterface $stockConfiguration = null + IsOrderStockManageable $isOrderStockManageable = null ) { $this->orderRepository = $orderRepository; $this->isWebsiteInMultiSourceMode = $isWebsiteInMultiSourceMode; $this->redirect = $redirect; - $this->stockConfiguration = $stockConfiguration ?? - ObjectManager::getInstance()->get(StockConfigurationInterface::class); + $this->isOrderStockManageable = $isOrderStockManageable ?? + ObjectManager::getInstance()->get(IsOrderStockManageable::class); } /** @@ -67,10 +68,6 @@ public function __construct( */ public function execute(EventObserver $observer) { - if (!$this->stockConfiguration->getManageStock()) { - return; - } - $request = $observer->getEvent()->getRequest(); $controller = $observer->getEvent()->getControllerAction(); @@ -82,6 +79,9 @@ public function execute(EventObserver $observer) try { $orderId = $request->getParam('order_id'); $order = $this->orderRepository->get($orderId); + if (!$this->isOrderStockManageable->execute($order)) { + return; + } $websiteId = (int)$order->getStore()->getWebsiteId(); if ($this->isWebsiteInMultiSourceMode->execute($websiteId)) { $this->redirect->redirect( From 78f6e0d27ca6cefd21ed50c3dd414da2f029f633 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Wed, 13 Mar 2019 16:59:03 +0200 Subject: [PATCH 022/231] MSI-2043: fixed product types --- .../Block/Adminhtml/Order/View/ShipButton.php | 16 ++++++------- ...geable.php => IsOrderSourceManageable.php} | 24 +++++++++++++++---- .../Observer/NewShipmentLoadBefore.php | 17 +++++++------ 3 files changed, 35 insertions(+), 22 deletions(-) rename InventoryShippingAdminUi/Model/{IsOrderStockManageable.php => IsOrderSourceManageable.php} (68%) diff --git a/InventoryShippingAdminUi/Block/Adminhtml/Order/View/ShipButton.php b/InventoryShippingAdminUi/Block/Adminhtml/Order/View/ShipButton.php index 998d64f7dd0c..5e47d3f646f7 100644 --- a/InventoryShippingAdminUi/Block/Adminhtml/Order/View/ShipButton.php +++ b/InventoryShippingAdminUi/Block/Adminhtml/Order/View/ShipButton.php @@ -11,7 +11,7 @@ use Magento\Backend\Block\Widget\Context; use Magento\Framework\App\ObjectManager; use Magento\Framework\Registry; -use Magento\InventoryShippingAdminUi\Model\IsOrderStockManageable; +use Magento\InventoryShippingAdminUi\Model\IsOrderSourceManageable; use Magento\InventoryShippingAdminUi\Model\IsWebsiteInMultiSourceMode; /** @@ -32,29 +32,29 @@ class ShipButton extends Container private $isWebsiteInMultiSourceMode; /** - * @var IsOrderStockManageable + * @var IsOrderSourceManageable */ - private $isOrderStockManageable; + private $isOrderSourceManageable; /** * @param Context $context * @param Registry $registry * @param IsWebsiteInMultiSourceMode $isWebsiteInMultiSourceMode * @param array $data - * @param IsOrderStockManageable $isOrderStockManageable + * @param IsOrderSourceManageable $isOrderSourceManageable */ public function __construct( Context $context, Registry $registry, IsWebsiteInMultiSourceMode $isWebsiteInMultiSourceMode, array $data = [], - IsOrderStockManageable $isOrderStockManageable = null + IsOrderSourceManageable $isOrderSourceManageable = null ) { parent::__construct($context, $data); $this->registry = $registry; $this->isWebsiteInMultiSourceMode = $isWebsiteInMultiSourceMode; - $this->isOrderStockManageable = $isOrderStockManageable ?? - ObjectManager::getInstance()->get(IsOrderStockManageable::class); + $this->isOrderSourceManageable = $isOrderSourceManageable ?? + ObjectManager::getInstance()->get(IsOrderSourceManageable::class); } /** @@ -66,7 +66,7 @@ protected function _prepareLayout() $order = $this->registry->registry('current_order'); $websiteId = (int)$order->getStore()->getWebsiteId(); - if ($this->isWebsiteInMultiSourceMode->execute($websiteId) && $this->isOrderStockManageable->execute($order)) { + if ($this->isWebsiteInMultiSourceMode->execute($websiteId) && $this->isOrderSourceManageable->execute($order)) { $this->buttonList->update( 'order_ship', 'onclick', diff --git a/InventoryShippingAdminUi/Model/IsOrderStockManageable.php b/InventoryShippingAdminUi/Model/IsOrderSourceManageable.php similarity index 68% rename from InventoryShippingAdminUi/Model/IsOrderStockManageable.php rename to InventoryShippingAdminUi/Model/IsOrderSourceManageable.php index 5fa4e1f83fb9..e09bc1ce844b 100644 --- a/InventoryShippingAdminUi/Model/IsOrderStockManageable.php +++ b/InventoryShippingAdminUi/Model/IsOrderSourceManageable.php @@ -12,12 +12,13 @@ use Magento\InventoryApi\Api\Data\StockInterface; use Magento\InventoryApi\Api\StockRepositoryInterface; use Magento\InventoryConfiguration\Model\GetStockItemConfiguration; +use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForProductTypeInterface; use Magento\Sales\Api\Data\OrderInterface; /** - * Is stock manageable for certain order + * Is source inventory of certain order is manageable */ -class IsOrderStockManageable +class IsOrderSourceManageable { /** * @var ProductRepositoryInterface @@ -34,35 +35,48 @@ class IsOrderStockManageable */ private $stockRepository; + /** + * @var IsSourceItemManagementAllowedForProductTypeInterface + */ + private $isSourceItemManagementAllowedForProductType; + /** * @param ProductRepositoryInterface $productRepository * @param GetStockItemConfiguration $getStockItemConfiguration * @param StockRepositoryInterface $stockRepository + * @param IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemManagementAllowedForProductType */ public function __construct( ProductRepositoryInterface $productRepository, GetStockItemConfiguration $getStockItemConfiguration, - StockRepositoryInterface $stockRepository + StockRepositoryInterface $stockRepository, + IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemManagementAllowedForProductType ) { $this->productRepository = $productRepository; $this->getStockItemConfiguration = $getStockItemConfiguration; $this->stockRepository = $stockRepository; + $this->isSourceItemManagementAllowedForProductType = $isSourceItemManagementAllowedForProductType; } /** - * Check if stock manageable for certain order + * Check if source manageable for certain order * * @param OrderInterface $order * @return bool */ public function execute(OrderInterface $order): bool { - $stocks = $this->stockRepository->getList(); + $stocks = $this->stockRepository->getList()->getItems(); $orderItems = $order->getItems(); foreach ($orderItems as $orderItem) { + if (!$this->isSourceItemManagementAllowedForProductType->execute($orderItem->getProductType())) { + continue; + } + $productId = $orderItem->getProductId(); /** @var Product $product */ $product = $this->productRepository->getById($productId); + /** @var StockInterface $stock */ foreach ($stocks as $stock) { $inventoryConfiguration = $this->getStockItemConfiguration->execute( diff --git a/InventoryShippingAdminUi/Observer/NewShipmentLoadBefore.php b/InventoryShippingAdminUi/Observer/NewShipmentLoadBefore.php index eb5617bf7024..e79f659cf11f 100644 --- a/InventoryShippingAdminUi/Observer/NewShipmentLoadBefore.php +++ b/InventoryShippingAdminUi/Observer/NewShipmentLoadBefore.php @@ -7,14 +7,13 @@ namespace Magento\InventoryShippingAdminUi\Observer; -use Magento\CatalogInventory\Api\StockConfigurationInterface; use Magento\Framework\App\ObjectManager; use Magento\Framework\App\Response\RedirectInterface; use Magento\Framework\Event\Observer as EventObserver; use Magento\Framework\Event\ObserverInterface; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\NoSuchEntityException; -use Magento\InventoryShippingAdminUi\Model\IsOrderStockManageable; +use Magento\InventoryShippingAdminUi\Model\IsOrderSourceManageable; use Magento\InventoryShippingAdminUi\Model\IsWebsiteInMultiSourceMode; use Magento\Sales\Model\OrderRepository; @@ -39,27 +38,27 @@ class NewShipmentLoadBefore implements ObserverInterface private $redirect; /** - * @var StockConfigurationInterface + * @var IsOrderSourceManageable */ - private $isOrderStockManageable; + private $orderSourceManageable; /** * @param OrderRepository $orderRepository * @param IsWebsiteInMultiSourceMode $isWebsiteInMultiSourceMode * @param RedirectInterface $redirect - * @param StockConfigurationInterface $isOrderStockManageable + * @param IsOrderSourceManageable $isOrderSourceManageable */ public function __construct( OrderRepository $orderRepository, IsWebsiteInMultiSourceMode $isWebsiteInMultiSourceMode, RedirectInterface $redirect, - IsOrderStockManageable $isOrderStockManageable = null + IsOrderSourceManageable $isOrderSourceManageable = null ) { $this->orderRepository = $orderRepository; $this->isWebsiteInMultiSourceMode = $isWebsiteInMultiSourceMode; $this->redirect = $redirect; - $this->isOrderStockManageable = $isOrderStockManageable ?? - ObjectManager::getInstance()->get(IsOrderStockManageable::class); + $this->orderSourceManageable = $isOrderSourceManageable ?? + ObjectManager::getInstance()->get(IsOrderSourceManageable::class); } /** @@ -79,7 +78,7 @@ public function execute(EventObserver $observer) try { $orderId = $request->getParam('order_id'); $order = $this->orderRepository->get($orderId); - if (!$this->isOrderStockManageable->execute($order)) { + if (!$this->orderSourceManageable->execute($order)) { return; } $websiteId = (int)$order->getStore()->getWebsiteId(); From 5c9bd2ebdca18f2106ea1ebfb42ec0f2b0865c37 Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Wed, 27 Feb 2019 18:52:30 +0100 Subject: [PATCH 023/231] WIP: Test with async legacy inventory calculation --- .../AsyncSetDataToLegacyCatalogInventory.php | 92 +++++++++++++++++++ .../Consumer.php | 87 ++++++++++++++++++ ...atalogInventoryAtSourceItemsSavePlugin.php | 17 +++- InventoryCatalog/etc/communication.xml | 13 +++ InventoryCatalog/etc/queue_consumer.xml | 16 ++++ InventoryCatalog/etc/queue_publisher.xml | 14 +++ InventoryCatalog/etc/queue_topology.xml | 19 ++++ 7 files changed, 256 insertions(+), 2 deletions(-) create mode 100644 InventoryCatalog/Model/SourceItemsSaveSynchronization/AsyncSetDataToLegacyCatalogInventory.php create mode 100644 InventoryCatalog/Model/SourceItemsSaveSynchronization/AsyncSetDataToLegacyCatalogInventory/Consumer.php create mode 100644 InventoryCatalog/etc/communication.xml create mode 100644 InventoryCatalog/etc/queue_consumer.xml create mode 100644 InventoryCatalog/etc/queue_publisher.xml create mode 100644 InventoryCatalog/etc/queue_topology.xml diff --git a/InventoryCatalog/Model/SourceItemsSaveSynchronization/AsyncSetDataToLegacyCatalogInventory.php b/InventoryCatalog/Model/SourceItemsSaveSynchronization/AsyncSetDataToLegacyCatalogInventory.php new file mode 100644 index 000000000000..cc290cc38d6f --- /dev/null +++ b/InventoryCatalog/Model/SourceItemsSaveSynchronization/AsyncSetDataToLegacyCatalogInventory.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalog\Model\SourceItemsSaveSynchronization; + +use Magento\AsynchronousOperations\Api\Data\OperationInterface; +use Magento\AsynchronousOperations\Api\Data\OperationInterfaceFactory; +use Magento\AsynchronousOperations\Model\MassSchedule; +use Magento\Framework\Bulk\BulkManagementInterface; +use Magento\Framework\DataObject\IdentityGeneratorInterface; +use Magento\Framework\Serialize\SerializerInterface; + +/** + * Set Qty and status for legacy CatalogInventory Stock Item table + */ +class AsyncSetDataToLegacyCatalogInventory +{ + private const BATCH_SIZE = 100; // TODO: use di.xml to define the batch size + private const TOPIC_NAME = 'inventory.catalog.product.legacy_inventory.set_data'; + + /** + * @var BulkManagementInterface + */ + private $bulkManagement; + + /** + * @var IdentityGeneratorInterface + */ + private $identityService; + + /** + * @var SerializerInterface + */ + private $serializer; + + /** + * @var OperationInterfaceFactory + */ + private $operationInterfaceFactory; + + /** + * AsyncSetDataToLegacyCatalogInventory constructor. + * @param BulkManagementInterface $bulkManagement + * @param SerializerInterface $serializer + * @param IdentityGeneratorInterface $identityService + * @param OperationInterfaceFactory $operationInterfaceFactory + */ + public function __construct( + BulkManagementInterface $bulkManagement, + SerializerInterface $serializer, + IdentityGeneratorInterface $identityService, + OperationInterfaceFactory $operationInterfaceFactory + ) { + $this->bulkManagement = $bulkManagement; + $this->identityService = $identityService; + $this->serializer = $serializer; + $this->operationInterfaceFactory = $operationInterfaceFactory; + } + + /** + * @param array $skus + * @return void + */ + public function execute(array $skus): void + { + $operations = []; + + $bulkUuid = $this->identityService->generateId(); + + $chunks = array_chunk($skus, self::BATCH_SIZE); + foreach ($chunks as $chunk) { + $data = [ + 'data' => [ + 'bulk_uuid' => $bulkUuid, + 'topic_name' => self::TOPIC_NAME, + 'serialized_data' => $this->serializer->serialize($chunk), + 'status' => OperationInterface::STATUS_TYPE_OPEN, + ] + ]; + + /** @var \Magento\AsynchronousOperations\Api\Data\OperationInterface $operation */ + $operation = $this->operationInterfaceFactory->create($data); + $operations[] = $operation; + } + + $this->bulkManagement->scheduleBulk($bulkUuid, $operations, __('Set legacy stock data')); + } +} diff --git a/InventoryCatalog/Model/SourceItemsSaveSynchronization/AsyncSetDataToLegacyCatalogInventory/Consumer.php b/InventoryCatalog/Model/SourceItemsSaveSynchronization/AsyncSetDataToLegacyCatalogInventory/Consumer.php new file mode 100644 index 000000000000..f79e6fd113c2 --- /dev/null +++ b/InventoryCatalog/Model/SourceItemsSaveSynchronization/AsyncSetDataToLegacyCatalogInventory/Consumer.php @@ -0,0 +1,87 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalog\Model\SourceItemsSaveSynchronization\AsyncSetDataToLegacyCatalogInventory; + +use Magento\AsynchronousOperations\Api\Data\OperationInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Inventory\Model\SourceItemRepository; +use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryApi\Api\SourceItemRepositoryInterface; +use Magento\InventoryCatalog\Model\SourceItemsSaveSynchronization\SetDataToLegacyCatalogInventory; +use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; + +class Consumer +{ + /** + * @var SerializerInterface + */ + private $serializer; + + /** + * @var SourceItemRepository + */ + private $sourceItemRepository; + + /** + * @var SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + + /** + * @var DefaultSourceProviderInterface + */ + private $defaultSourceProvider; + + /** + * @var SetDataToLegacyCatalogInventory + */ + private $setDataToLegacyCatalogInventory; + + /** + * Consumer constructor. + * @param SerializerInterface $serializer + * @param SourceItemRepositoryInterface $sourceItemRepository + * @param DefaultSourceProviderInterface $defaultSourceProvider + * @param SearchCriteriaBuilder $searchCriteriaBuilder + * @param SetDataToLegacyCatalogInventory $setDataToLegacyCatalogInventory + * @SuppressWarnings(PHPMD.LongVariable) + */ + public function __construct( + SerializerInterface $serializer, + SourceItemRepositoryInterface $sourceItemRepository, + DefaultSourceProviderInterface $defaultSourceProvider, + SearchCriteriaBuilder $searchCriteriaBuilder, + SetDataToLegacyCatalogInventory $setDataToLegacyCatalogInventory + ) { + $this->serializer = $serializer; + $this->sourceItemRepository = $sourceItemRepository; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->defaultSourceProvider = $defaultSourceProvider; + $this->setDataToLegacyCatalogInventory = $setDataToLegacyCatalogInventory; + } + + /** + * Processing batch operations for legacy stock synchronization + * + * @param OperationInterface $operation + * @return void + */ + public function processOperations(OperationInterface $operation): void + { + $skus = $this->serializer->unserialize($operation->getSerializedData()); + + $searchCriteria = $this->searchCriteriaBuilder + ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) + ->addFilter(SourceItemInterface::SKU, $skus, 'in') + ->create(); + + $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); + $this->setDataToLegacyCatalogInventory->execute($sourceItems); + } +} diff --git a/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php b/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php index 21cd22f44e6f..b2a2bde652c3 100644 --- a/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php +++ b/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php @@ -10,6 +10,7 @@ use Magento\Framework\Exception\InputException; use Magento\InventoryApi\Api\Data\SourceItemInterface; use Magento\InventoryApi\Api\SourceItemsSaveInterface; +use Magento\InventoryCatalog\Model\SourceItemsSaveSynchronization\AsyncSetDataToLegacyCatalogInventory; use Magento\InventoryCatalog\Model\SourceItemsSaveSynchronization\SetDataToLegacyCatalogInventory; use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; use Magento\InventoryCatalogApi\Model\GetProductTypesBySkusInterface; @@ -40,22 +41,31 @@ class SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin */ private $setDataToLegacyCatalogInventory; + /** + * @var AsyncSetDataToLegacyCatalogInventory + */ + private $asyncSetDataToLegacyCatalogInventory; + /** * @param DefaultSourceProviderInterface $defaultSourceProvider * @param IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemsAllowedForProductType * @param GetProductTypesBySkusInterface $getProductTypeBySku * @param SetDataToLegacyCatalogInventory $setDataToLegacyCatalogInventory + * @param AsyncSetDataToLegacyCatalogInventory $asyncSetDataToLegacyCatalogInventory + * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( DefaultSourceProviderInterface $defaultSourceProvider, IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemsAllowedForProductType, GetProductTypesBySkusInterface $getProductTypeBySku, - SetDataToLegacyCatalogInventory $setDataToLegacyCatalogInventory + SetDataToLegacyCatalogInventory $setDataToLegacyCatalogInventory, + AsyncSetDataToLegacyCatalogInventory $asyncSetDataToLegacyCatalogInventory ) { $this->defaultSourceProvider = $defaultSourceProvider; $this->isSourceItemsAllowedForProductType = $isSourceItemsAllowedForProductType; $this->getProductTypeBySku = $getProductTypeBySku; $this->setDataToLegacyCatalogInventory = $setDataToLegacyCatalogInventory; + $this->asyncSetDataToLegacyCatalogInventory = $asyncSetDataToLegacyCatalogInventory; } /** @@ -68,6 +78,7 @@ public function __construct( public function afterExecute(SourceItemsSaveInterface $subject, $result, array $sourceItems): void { $sourceItemsForSynchronization = []; + $skuToSynchronize = []; foreach ($sourceItems as $sourceItem) { if ($sourceItem->getSourceCode() !== $this->defaultSourceProvider->getCode()) { continue; @@ -87,8 +98,10 @@ public function afterExecute(SourceItemsSaveInterface $subject, $result, array $ } $sourceItemsForSynchronization[] = $sourceItem; + $skuToSynchronize[] = $sourceItem->getSku(); } - $this->setDataToLegacyCatalogInventory->execute($sourceItemsForSynchronization); + $this->asyncSetDataToLegacyCatalogInventory->execute($skuToSynchronize); +// $this->setDataToLegacyCatalogInventory->execute($sourceItemsForSynchronization); } } diff --git a/InventoryCatalog/etc/communication.xml b/InventoryCatalog/etc/communication.xml new file mode 100644 index 000000000000..04432d25d827 --- /dev/null +++ b/InventoryCatalog/etc/communication.xml @@ -0,0 +1,13 @@ +<?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:Communication/etc/communication.xsd"> + <topic name="inventory.catalog.product.legacy_inventory.set_data" + request="Magento\AsynchronousOperations\Api\Data\OperationInterface"> + </topic> +</config> diff --git a/InventoryCatalog/etc/queue_consumer.xml b/InventoryCatalog/etc/queue_consumer.xml new file mode 100644 index 000000000000..0d743332bc7c --- /dev/null +++ b/InventoryCatalog/etc/queue_consumer.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:framework-message-queue:etc/queue_consumer.xsd"> + <consumer + name="inventoryCatalogProductLegacyInventorySetData" + queue="inventory_catalog_product_legacy_inventory_set_data_queue" + connection="amqp" + handler="Magento\InventoryCatalog\Model\SourceItemsSaveSynchronization\AsyncSetDataToLegacyCatalogInventory\Consumer::processOperations"/> +</config> \ No newline at end of file diff --git a/InventoryCatalog/etc/queue_publisher.xml b/InventoryCatalog/etc/queue_publisher.xml new file mode 100644 index 000000000000..61e96de6dbfe --- /dev/null +++ b/InventoryCatalog/etc/queue_publisher.xml @@ -0,0 +1,14 @@ +<?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-message-queue:etc/queue_publisher.xsd"> + <publisher topic="inventory.catalog.product.legacy_inventory.set_data"> + <connection name="amqp" exchange="inventory.catalog.product.legacy_inventory.set_data.exchange" /> + </publisher> +</config> \ No newline at end of file diff --git a/InventoryCatalog/etc/queue_topology.xml b/InventoryCatalog/etc/queue_topology.xml new file mode 100644 index 000000000000..4cf88b59880b --- /dev/null +++ b/InventoryCatalog/etc/queue_topology.xml @@ -0,0 +1,19 @@ +<?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-message-queue:etc/queue_topology.xsd"> + + <exchange name="inventory.catalog.product.legacy_inventory.set_data.exchange" type="topic" connection="amqp"> + <binding + id="inventoryCatalogProductLegacyInventorySetDataExchangeBinding" + topic="inventory.catalog.product.legacy_inventory.set_data" + destinationType="queue" + destination="inventory_catalog_product_legacy_inventory_set_data_queue"/> + </exchange> +</config> \ No newline at end of file From a235baebbb381ab20a9dde7ef92c3d35fef17d1f Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Wed, 6 Mar 2019 15:18:57 +0100 Subject: [PATCH 024/231] WIP synchronize legacy stock with default source item --- .../Model/GetDefaultSourceItemsBySkus.php | 65 +++++++++ .../AlignLegacyCatalogInventoryByProducts.php | 60 +++++++++ .../AlignSourceItemsByProducts.php | 16 +++ ...ronousSetDataToLegacyCatalogInventory.php} | 18 ++- ...etDataToLegacyCatalogInventoryConsumer.php | 51 ++++++++ .../GetLegacyStockItemsByProductIds.php | 75 +++++++++++ .../IsAsyncLegacyAlignment.php | 38 ++++++ ...hronousSetDataToLegacyCatalogInventory.php | 123 ++++++++++++++++++ .../Consumer.php | 87 ------------- .../SetDataToLegacyCatalogInventory.php | 107 +++------------ ...atalogInventoryAtSourceItemsSavePlugin.php | 27 ++-- InventoryCatalog/etc/communication.xml | 3 +- InventoryCatalog/etc/config.xml | 3 + InventoryCatalog/etc/di.xml | 6 + InventoryCatalog/etc/queue_consumer.xml | 2 +- .../etc/adminhtml/system.xml | 10 ++ 16 files changed, 487 insertions(+), 204 deletions(-) create mode 100644 InventoryCatalog/Model/GetDefaultSourceItemsBySkus.php create mode 100644 InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AlignLegacyCatalogInventoryByProducts.php create mode 100644 InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AlignSourceItemsByProducts.php rename InventoryCatalog/Model/{SourceItemsSaveSynchronization/AsyncSetDataToLegacyCatalogInventory.php => LegacyCatalogInventorySynchronization/AsynchronousSetDataToLegacyCatalogInventory.php} (86%) create mode 100644 InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AsynchronousSetDataToLegacyCatalogInventoryConsumer.php create mode 100644 InventoryCatalog/Model/LegacyCatalogInventorySynchronization/GetLegacyStockItemsByProductIds.php create mode 100644 InventoryCatalog/Model/LegacyCatalogInventorySynchronization/IsAsyncLegacyAlignment.php create mode 100644 InventoryCatalog/Model/LegacyCatalogInventorySynchronization/SynchronousSetDataToLegacyCatalogInventory.php delete mode 100644 InventoryCatalog/Model/SourceItemsSaveSynchronization/AsyncSetDataToLegacyCatalogInventory/Consumer.php diff --git a/InventoryCatalog/Model/GetDefaultSourceItemsBySkus.php b/InventoryCatalog/Model/GetDefaultSourceItemsBySkus.php new file mode 100644 index 000000000000..f9666303d644 --- /dev/null +++ b/InventoryCatalog/Model/GetDefaultSourceItemsBySkus.php @@ -0,0 +1,65 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalog\Model; + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryApi\Api\SourceItemRepositoryInterface; +use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; + +/** + * Get a list of default source items for a given SKU list + */ +class GetDefaultSourceItemsBySkus +{ + /** + * @var SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + + /** + * @var DefaultSourceProviderInterface + */ + private $defaultSourceProvider; + + /** + * @var SourceItemRepositoryInterface + */ + private $sourceItemRepository; + + /** + * GetDefaultSourceItemsBySkus constructor. + * @param SearchCriteriaBuilder $searchCriteriaBuilder + * @param DefaultSourceProviderInterface $defaultSourceProvider + * @param SourceItemRepositoryInterface $sourceItemRepository + * @SuppressWarnings(PHPMD.LongVariable) + */ + public function __construct( + SearchCriteriaBuilder $searchCriteriaBuilder, + DefaultSourceProviderInterface $defaultSourceProvider, + SourceItemRepositoryInterface $sourceItemRepository + ) { + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->defaultSourceProvider = $defaultSourceProvider; + $this->sourceItemRepository = $sourceItemRepository; + } + + /** + * @param array $skus + * @return SourceItemInterface[] + */ + public function execute(array $skus): array + { + $searchCriteria = $this->searchCriteriaBuilder + ->addFilter(SourceItemInterface::SKU, ['in' => $skus]) + ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) + ->create(); + + return $this->sourceItemRepository->getList($searchCriteria)->getItems(); + } +} diff --git a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AlignLegacyCatalogInventoryByProducts.php b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AlignLegacyCatalogInventoryByProducts.php new file mode 100644 index 000000000000..d44f9b3f9264 --- /dev/null +++ b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AlignLegacyCatalogInventoryByProducts.php @@ -0,0 +1,60 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization; + +/** + * Set Qty and status for legacy CatalogInventory Stock Item table + */ +class AlignLegacyCatalogInventoryByProducts +{ + /** + * @var IsAsyncLegacyAlignment + */ + private $isAsyncLegacyAlignment; + + /** + * @var SynchronousSetDataToLegacyCatalogInventory + */ + private $synchronousSetDataToLegacyCatalogInventory; + + /** + * @var AsynchronousSetDataToLegacyCatalogInventory + */ + private $asynchronousSetDataToLegacyCatalogInventory; + + /** + * AlignLegacyCatalogInventoryByProducts constructor. + * @param IsAsyncLegacyAlignment $isAsyncLegacyAlignment + * @param SynchronousSetDataToLegacyCatalogInventory $synchronousSetDataToLegacyCatalogInventory + * @param AsynchronousSetDataToLegacyCatalogInventory $asynchronousSetDataToLegacyCatalogInventory + * @SuppressWarnings(PHPMD.LongVariable) + */ + public function __construct( + IsAsyncLegacyAlignment $isAsyncLegacyAlignment, + SynchronousSetDataToLegacyCatalogInventory $synchronousSetDataToLegacyCatalogInventory, + AsynchronousSetDataToLegacyCatalogInventory $asynchronousSetDataToLegacyCatalogInventory + ) { + $this->isAsyncLegacyAlignment = $isAsyncLegacyAlignment; + $this->synchronousSetDataToLegacyCatalogInventory = $synchronousSetDataToLegacyCatalogInventory; + $this->asynchronousSetDataToLegacyCatalogInventory = $asynchronousSetDataToLegacyCatalogInventory; + } + + /** + * @param array $skus + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function execute(array $skus): void + { + if ($this->isAsyncLegacyAlignment->execute()) { + $this->asynchronousSetDataToLegacyCatalogInventory->execute($skus); + } else { + $this->synchronousSetDataToLegacyCatalogInventory->execute($skus); + } + } +} diff --git a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AlignSourceItemsByProducts.php b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AlignSourceItemsByProducts.php new file mode 100644 index 000000000000..f20c017bc303 --- /dev/null +++ b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AlignSourceItemsByProducts.php @@ -0,0 +1,16 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization; + +/** + * Set source item from legacy CatalogInventory Stock Item table + */ +class AlignSourceItemsByProducts +{ + +} diff --git a/InventoryCatalog/Model/SourceItemsSaveSynchronization/AsyncSetDataToLegacyCatalogInventory.php b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AsynchronousSetDataToLegacyCatalogInventory.php similarity index 86% rename from InventoryCatalog/Model/SourceItemsSaveSynchronization/AsyncSetDataToLegacyCatalogInventory.php rename to InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AsynchronousSetDataToLegacyCatalogInventory.php index cc290cc38d6f..c74810a1797d 100644 --- a/InventoryCatalog/Model/SourceItemsSaveSynchronization/AsyncSetDataToLegacyCatalogInventory.php +++ b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AsynchronousSetDataToLegacyCatalogInventory.php @@ -5,11 +5,10 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Model\SourceItemsSaveSynchronization; +namespace Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization; use Magento\AsynchronousOperations\Api\Data\OperationInterface; use Magento\AsynchronousOperations\Api\Data\OperationInterfaceFactory; -use Magento\AsynchronousOperations\Model\MassSchedule; use Magento\Framework\Bulk\BulkManagementInterface; use Magento\Framework\DataObject\IdentityGeneratorInterface; use Magento\Framework\Serialize\SerializerInterface; @@ -17,9 +16,8 @@ /** * Set Qty and status for legacy CatalogInventory Stock Item table */ -class AsyncSetDataToLegacyCatalogInventory +class AsynchronousSetDataToLegacyCatalogInventory { - private const BATCH_SIZE = 100; // TODO: use di.xml to define the batch size private const TOPIC_NAME = 'inventory.catalog.product.legacy_inventory.set_data'; /** @@ -42,23 +40,31 @@ class AsyncSetDataToLegacyCatalogInventory */ private $operationInterfaceFactory; + /** + * @var int + */ + private $batchSize; + /** * AsyncSetDataToLegacyCatalogInventory constructor. * @param BulkManagementInterface $bulkManagement * @param SerializerInterface $serializer * @param IdentityGeneratorInterface $identityService * @param OperationInterfaceFactory $operationInterfaceFactory + * @param int $batchSize */ public function __construct( BulkManagementInterface $bulkManagement, SerializerInterface $serializer, IdentityGeneratorInterface $identityService, - OperationInterfaceFactory $operationInterfaceFactory + OperationInterfaceFactory $operationInterfaceFactory, + int $batchSize ) { $this->bulkManagement = $bulkManagement; $this->identityService = $identityService; $this->serializer = $serializer; $this->operationInterfaceFactory = $operationInterfaceFactory; + $this->batchSize = $batchSize; } /** @@ -71,7 +77,7 @@ public function execute(array $skus): void $bulkUuid = $this->identityService->generateId(); - $chunks = array_chunk($skus, self::BATCH_SIZE); + $chunks = array_chunk($skus, $this->batchSize); foreach ($chunks as $chunk) { $data = [ 'data' => [ diff --git a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AsynchronousSetDataToLegacyCatalogInventoryConsumer.php b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AsynchronousSetDataToLegacyCatalogInventoryConsumer.php new file mode 100644 index 000000000000..3c8675e47d89 --- /dev/null +++ b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AsynchronousSetDataToLegacyCatalogInventoryConsumer.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization; + +use Magento\AsynchronousOperations\Api\Data\OperationInterface; +use Magento\Framework\Serialize\SerializerInterface; + +class AsynchronousSetDataToLegacyCatalogInventoryConsumer +{ + /** + * @var SerializerInterface + */ + private $serializer; + + /** + * @var SynchronousSetDataToLegacyCatalogInventory + */ + private $synchronousSetDataToLegacyCatalogInventory; + + /** + * Consumer constructor. + * @param SerializerInterface $serializer + * @param SynchronousSetDataToLegacyCatalogInventory $synchronousSetDataToLegacyCatalogInventory + * @SuppressWarnings(PHPMD.LongVariable) + */ + public function __construct( + SerializerInterface $serializer, + SynchronousSetDataToLegacyCatalogInventory $synchronousSetDataToLegacyCatalogInventory + ) { + $this->serializer = $serializer; + $this->synchronousSetDataToLegacyCatalogInventory = $synchronousSetDataToLegacyCatalogInventory; + } + + /** + * Processing batch operations for legacy stock synchronization + * + * @param OperationInterface $operation + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function processOperations(OperationInterface $operation): void + { + $skus = $this->serializer->unserialize($operation->getSerializedData()); + $this->synchronousSetDataToLegacyCatalogInventory->execute($skus); + } +} diff --git a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/GetLegacyStockItemsByProductIds.php b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/GetLegacyStockItemsByProductIds.php new file mode 100644 index 000000000000..4ea085464a96 --- /dev/null +++ b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/GetLegacyStockItemsByProductIds.php @@ -0,0 +1,75 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization; + +use Magento\CatalogInventory\Api\Data\StockItemInterface; +use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory; +use Magento\CatalogInventory\Api\StockItemRepositoryInterface; +use Magento\CatalogInventory\Model\Stock;; +use Magento\Framework\Exception\LocalizedException; + +/** + * Get a list of legacy stock items by products ids + */ +class GetLegacyStockItemsByProductIds +{ + /** + * @var StockItemRepositoryInterface + */ + private $stockItemRepository; + + /** + * @var StockItemCriteriaInterfaceFactory + */ + private $stockItemCriteriaFactory; + + /** + * GetLegacyStockItemsByProductIds constructor. + * @param StockItemRepositoryInterface $stockItemRepository + * @param StockItemCriteriaInterfaceFactory $stockItemCriteriaFactory + * @SuppressWarnings(PHPMD.LongVariable) + */ + public function __construct( + StockItemRepositoryInterface $stockItemRepository, + StockItemCriteriaInterfaceFactory $stockItemCriteriaFactory + ) { + $this->stockItemRepository = $stockItemRepository; + $this->stockItemCriteriaFactory = $stockItemCriteriaFactory; + } + + /** + * @param array $productIds + * @return StockItemInterface[] + * @throws LocalizedException + */ + public function execute(array $productIds): array + { + $searchCriteria = $this->stockItemCriteriaFactory->create(); + + $searchCriteria->addFilter( + StockItemInterface::PRODUCT_ID, + StockItemInterface::PRODUCT_ID, + [ + 'in' => $productIds + ] + ); + $searchCriteria->addFilter( + StockItemInterface::STOCK_ID, + StockItemInterface::STOCK_ID, + Stock::DEFAULT_STOCK_ID + ); + + $stockItems = $this->stockItemRepository->getList($searchCriteria)->getItems(); + $productIdsIndex = []; + foreach ($stockItems as $stockItem) { + $productIdsIndex[] = (int) $stockItem->getProductId(); + } + + return array_combine($productIdsIndex, $stockItems); + } +} diff --git a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/IsAsyncLegacyAlignment.php b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/IsAsyncLegacyAlignment.php new file mode 100644 index 000000000000..aeef115367c9 --- /dev/null +++ b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/IsAsyncLegacyAlignment.php @@ -0,0 +1,38 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization; + +use Magento\Framework\App\Config\ScopeConfigInterface; + +class IsAsyncLegacyAlignment +{ + private const XML_PATH = 'cataloginventory/legacy_stock/async'; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * IsAsyncLegacyAlignment constructor. + * @param ScopeConfigInterface $scopeConfig + */ + public function __construct(ScopeConfigInterface $scopeConfig) + { + $this->scopeConfig = $scopeConfig; + } + + /** + * Return true if legacy inventory is aligned asynchronously + * @return bool + */ + public function execute(): bool + { + return (bool) $this->scopeConfig->getValue(self::XML_PATH); + } +} diff --git a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/SynchronousSetDataToLegacyCatalogInventory.php b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/SynchronousSetDataToLegacyCatalogInventory.php new file mode 100644 index 000000000000..fb1beea7d6b3 --- /dev/null +++ b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/SynchronousSetDataToLegacyCatalogInventory.php @@ -0,0 +1,123 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization; + +use Magento\Catalog\Model\ResourceModel\Product; +use Magento\CatalogInventory\Model\Indexer\Stock\Processor; +use Magento\CatalogInventory\Model\Spi\StockStateProviderInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\InventoryCatalog\Model\GetDefaultSourceItemsBySkus; +use Magento\InventoryCatalog\Model\ResourceModel\SetDataToLegacyStockItem; + +class SynchronousSetDataToLegacyCatalogInventory +{ + /** + * @var SetDataToLegacyStockItem + */ + private $setDataToLegacyStockItem; + + /** + * @var Product + */ + private $productResourceModel; + + /** + * @var GetDefaultSourceItemsBySkus + */ + private $getDefaultSourceItemsBySkus; + + /** + * @var GetLegacyStockItemsByProductIds + */ + private $getLegacyStockItemsByProductIds; + + /** + * @var StockStateProviderInterface + */ + private $stockStateProvider; + + /** + * @var Processor + */ + private $indexerProcessor; + + /** + * SetDataToLegacyCatalogInventory constructor. + * @param GetDefaultSourceItemsBySkus $getDefaultSourceItemsBySkus + * @param SetDataToLegacyStockItem $setDataToLegacyStockItem + * @param GetLegacyStockItemsByProductIds $getLegacyStockItemsByProductIds + * @param StockStateProviderInterface $stockStateProvider + * @param Processor $indexerProcessor + * @param Product $productResourceModel + * @SuppressWarnings(PHPMD.LongVariable) + */ + public function __construct( + GetDefaultSourceItemsBySkus $getDefaultSourceItemsBySkus, + SetDataToLegacyStockItem $setDataToLegacyStockItem, + GetLegacyStockItemsByProductIds $getLegacyStockItemsByProductIds, + StockStateProviderInterface $stockStateProvider, + Processor $indexerProcessor, + Product $productResourceModel + ) { + $this->setDataToLegacyStockItem = $setDataToLegacyStockItem; + $this->productResourceModel = $productResourceModel; + $this->getDefaultSourceItemsBySkus = $getDefaultSourceItemsBySkus; + $this->getLegacyStockItemsByProductIds = $getLegacyStockItemsByProductIds; + $this->stockStateProvider = $stockStateProvider; + $this->indexerProcessor = $indexerProcessor; + } + + /** + * Synchronously execute legacy alignment + * + * @param array $skus + * @throws LocalizedException + */ + public function execute(array $skus): void + { + $sourceItems = $this->getDefaultSourceItemsBySkus->execute($skus); + $productIds = $this->productResourceModel->getProductsIdsBySkus($skus); + $legacyStockItems = $this->getLegacyStockItemsByProductIds->execute($productIds); + + foreach ($sourceItems as $sourceItem) { + $sku = $sourceItem->getSku(); + + if (!isset($productIds[$sku])) { + continue; // This product does not exist anymore + } + + $productId = (int) $productIds[$sku]; + + if (!isset($legacyStockItems[$productId])) { + continue; + } + + $legacyStockItem = $legacyStockItems[$productId]; + $isInStock = (int) $sourceItem->getStatus(); + + if ($legacyStockItem->getManageStock()) { + $legacyStockItem->setIsInStock($isInStock); + $legacyStockItem->setQty((float)$sourceItem->getQuantity()); + + if (false === $this->stockStateProvider->verifyStock($legacyStockItem)) { + $isInStock = 0; + } + } + + $this->setDataToLegacyStockItem->execute( + (string) $sourceItem->getSku(), + (float) $sourceItem->getQuantity(), + $isInStock + ); + } + + if (!empty($productIds)) { + $this->indexerProcessor->reindexList($productIds); + } + } +} diff --git a/InventoryCatalog/Model/SourceItemsSaveSynchronization/AsyncSetDataToLegacyCatalogInventory/Consumer.php b/InventoryCatalog/Model/SourceItemsSaveSynchronization/AsyncSetDataToLegacyCatalogInventory/Consumer.php deleted file mode 100644 index f79e6fd113c2..000000000000 --- a/InventoryCatalog/Model/SourceItemsSaveSynchronization/AsyncSetDataToLegacyCatalogInventory/Consumer.php +++ /dev/null @@ -1,87 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryCatalog\Model\SourceItemsSaveSynchronization\AsyncSetDataToLegacyCatalogInventory; - -use Magento\AsynchronousOperations\Api\Data\OperationInterface; -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\Serialize\SerializerInterface; -use Magento\Inventory\Model\SourceItemRepository; -use Magento\InventoryApi\Api\Data\SourceItemInterface; -use Magento\InventoryApi\Api\SourceItemRepositoryInterface; -use Magento\InventoryCatalog\Model\SourceItemsSaveSynchronization\SetDataToLegacyCatalogInventory; -use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; - -class Consumer -{ - /** - * @var SerializerInterface - */ - private $serializer; - - /** - * @var SourceItemRepository - */ - private $sourceItemRepository; - - /** - * @var SearchCriteriaBuilder - */ - private $searchCriteriaBuilder; - - /** - * @var DefaultSourceProviderInterface - */ - private $defaultSourceProvider; - - /** - * @var SetDataToLegacyCatalogInventory - */ - private $setDataToLegacyCatalogInventory; - - /** - * Consumer constructor. - * @param SerializerInterface $serializer - * @param SourceItemRepositoryInterface $sourceItemRepository - * @param DefaultSourceProviderInterface $defaultSourceProvider - * @param SearchCriteriaBuilder $searchCriteriaBuilder - * @param SetDataToLegacyCatalogInventory $setDataToLegacyCatalogInventory - * @SuppressWarnings(PHPMD.LongVariable) - */ - public function __construct( - SerializerInterface $serializer, - SourceItemRepositoryInterface $sourceItemRepository, - DefaultSourceProviderInterface $defaultSourceProvider, - SearchCriteriaBuilder $searchCriteriaBuilder, - SetDataToLegacyCatalogInventory $setDataToLegacyCatalogInventory - ) { - $this->serializer = $serializer; - $this->sourceItemRepository = $sourceItemRepository; - $this->searchCriteriaBuilder = $searchCriteriaBuilder; - $this->defaultSourceProvider = $defaultSourceProvider; - $this->setDataToLegacyCatalogInventory = $setDataToLegacyCatalogInventory; - } - - /** - * Processing batch operations for legacy stock synchronization - * - * @param OperationInterface $operation - * @return void - */ - public function processOperations(OperationInterface $operation): void - { - $skus = $this->serializer->unserialize($operation->getSerializedData()); - - $searchCriteria = $this->searchCriteriaBuilder - ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) - ->addFilter(SourceItemInterface::SKU, $skus, 'in') - ->create(); - - $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); - $this->setDataToLegacyCatalogInventory->execute($sourceItems); - } -} diff --git a/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php b/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php index 90ccd780947a..43e5156b1d77 100644 --- a/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php +++ b/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php @@ -13,45 +13,23 @@ use Magento\CatalogInventory\Model\Indexer\Stock\Processor; use Magento\CatalogInventory\Model\Spi\StockStateProviderInterface; use Magento\CatalogInventory\Model\Stock; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\AlignLegacyCatalogInventoryByProducts; use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface; use Magento\InventoryCatalog\Model\ResourceModel\SetDataToLegacyStockItem; -use Magento\InventoryCatalogApi\Model\SourceItemsSaveSynchronizationInterface; /** * Set Qty and status for legacy CatalogInventory Stock Item table + * @deprecated + * @see \Magento\InventoryCatalog\Model\SourceItemsSaveSynchronization\SetDataToLegacyCatalogInventory */ class SetDataToLegacyCatalogInventory { /** - * @var SetDataToLegacyStockItem + * @var AlignLegacyCatalogInventoryByProducts */ - private $setDataToLegacyStockItem; - - /** - * @var StockItemCriteriaInterfaceFactory - */ - private $legacyStockItemCriteriaFactory; - - /** - * @var StockItemRepositoryInterface - */ - private $legacyStockItemRepository; - - /** - * @var GetProductIdsBySkusInterface - */ - private $getProductIdsBySkus; - - /** - * @var StockStateProviderInterface - */ - private $stockStateProvider; - - /** - * @var Processor - */ - private $indexerProcessor; + private $alignLegacyCatalogInventoryByProducts; /** * @param SetDataToLegacyStockItem $setDataToLegacyStockItem @@ -60,6 +38,9 @@ class SetDataToLegacyCatalogInventory * @param GetProductIdsBySkusInterface $getProductIdsBySkus * @param StockStateProviderInterface $stockStateProvider * @param Processor $indexerProcessor + * @param AlignLegacyCatalogInventoryByProducts $alignLegacyCatalogInventoryByProducts + * @SupressWarnings(PHPMD.UnusedFormalParameter) + * @SupressWarnings(PHPMD.LongVariable) */ public function __construct( SetDataToLegacyStockItem $setDataToLegacyStockItem, @@ -67,14 +48,11 @@ public function __construct( StockItemRepositoryInterface $legacyStockItemRepository, GetProductIdsBySkusInterface $getProductIdsBySkus, StockStateProviderInterface $stockStateProvider, - Processor $indexerProcessor + Processor $indexerProcessor, + AlignLegacyCatalogInventoryByProducts $alignLegacyCatalogInventoryByProducts = null ) { - $this->setDataToLegacyStockItem = $setDataToLegacyStockItem; - $this->legacyStockItemCriteriaFactory = $legacyStockItemCriteriaFactory; - $this->legacyStockItemRepository = $legacyStockItemRepository; - $this->getProductIdsBySkus = $getProductIdsBySkus; - $this->stockStateProvider = $stockStateProvider; - $this->indexerProcessor = $indexerProcessor; + $this->alignLegacyCatalogInventoryByProducts = $alignLegacyCatalogInventoryByProducts ?: + ObjectManager::getInstance()->get(AlignLegacyCatalogInventoryByProducts::class); } /** @@ -83,64 +61,11 @@ public function __construct( */ public function execute(array $sourceItems): void { - $productIds = []; + $skus = []; foreach ($sourceItems as $sourceItem) { - $sku = $sourceItem->getSku(); - - try { - $productId = (int)$this->getProductIdsBySkus->execute([$sku])[$sku]; - } catch (NoSuchEntityException $e) { - // Skip synchronization of for not existed product - continue; - } - - $legacyStockItem = $this->getLegacyStockItem($productId); - if (null === $legacyStockItem) { - continue; - } - - $isInStock = (int)$sourceItem->getStatus(); - - if ($legacyStockItem->getManageStock()) { - $legacyStockItem->setIsInStock($isInStock); - $legacyStockItem->setQty((float)$sourceItem->getQuantity()); - - if (false === $this->stockStateProvider->verifyStock($legacyStockItem)) { - $isInStock = 0; - } - } - - $this->setDataToLegacyStockItem->execute( - (string)$sourceItem->getSku(), - (float)$sourceItem->getQuantity(), - $isInStock - ); - $productIds[] = $productId; - } - - if ($productIds) { - $this->indexerProcessor->reindexList($productIds); - } - } - - /** - * @param int $productId - * @return null|StockItemInterface - */ - private function getLegacyStockItem(int $productId): ?StockItemInterface - { - $searchCriteria = $this->legacyStockItemCriteriaFactory->create(); - - $searchCriteria->addFilter(StockItemInterface::PRODUCT_ID, StockItemInterface::PRODUCT_ID, $productId); - $searchCriteria->addFilter(StockItemInterface::STOCK_ID, StockItemInterface::STOCK_ID, Stock::DEFAULT_STOCK_ID); - - $stockItemCollection = $this->legacyStockItemRepository->getList($searchCriteria); - if ($stockItemCollection->getTotalCount() === 0) { - return null; + $skus[] = $sourceItem->getSku(); } - $stockItems = $stockItemCollection->getItems(); - $stockItem = reset($stockItems); - return $stockItem; + $this->alignLegacyCatalogInventoryByProducts->execute($skus); } } diff --git a/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php b/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php index b2a2bde652c3..b59f9a7274e2 100644 --- a/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php +++ b/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php @@ -7,10 +7,10 @@ namespace Magento\InventoryCatalog\Plugin\InventoryApi; -use Magento\Framework\Exception\InputException; +use Magento\Framework\App\ObjectManager; use Magento\InventoryApi\Api\Data\SourceItemInterface; use Magento\InventoryApi\Api\SourceItemsSaveInterface; -use Magento\InventoryCatalog\Model\SourceItemsSaveSynchronization\AsyncSetDataToLegacyCatalogInventory; +use Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\AlignLegacyCatalogInventoryByProducts; use Magento\InventoryCatalog\Model\SourceItemsSaveSynchronization\SetDataToLegacyCatalogInventory; use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; use Magento\InventoryCatalogApi\Model\GetProductTypesBySkusInterface; @@ -37,35 +37,31 @@ class SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin private $getProductTypeBySku; /** - * @var SetDataToLegacyCatalogInventory + * @var AlignLegacyCatalogInventoryByProducts|null */ - private $setDataToLegacyCatalogInventory; - - /** - * @var AsyncSetDataToLegacyCatalogInventory - */ - private $asyncSetDataToLegacyCatalogInventory; + private $alignLegacyCatalogInventoryByProducts; /** * @param DefaultSourceProviderInterface $defaultSourceProvider * @param IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemsAllowedForProductType * @param GetProductTypesBySkusInterface $getProductTypeBySku * @param SetDataToLegacyCatalogInventory $setDataToLegacyCatalogInventory - * @param AsyncSetDataToLegacyCatalogInventory $asyncSetDataToLegacyCatalogInventory + * @param AlignLegacyCatalogInventoryByProducts|null $alignLegacyCatalogInventoryByProducts * @SuppressWarnings(PHPMD.LongVariable) + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( DefaultSourceProviderInterface $defaultSourceProvider, IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemsAllowedForProductType, GetProductTypesBySkusInterface $getProductTypeBySku, SetDataToLegacyCatalogInventory $setDataToLegacyCatalogInventory, - AsyncSetDataToLegacyCatalogInventory $asyncSetDataToLegacyCatalogInventory + AlignLegacyCatalogInventoryByProducts $alignLegacyCatalogInventoryByProducts = null ) { $this->defaultSourceProvider = $defaultSourceProvider; $this->isSourceItemsAllowedForProductType = $isSourceItemsAllowedForProductType; $this->getProductTypeBySku = $getProductTypeBySku; - $this->setDataToLegacyCatalogInventory = $setDataToLegacyCatalogInventory; - $this->asyncSetDataToLegacyCatalogInventory = $asyncSetDataToLegacyCatalogInventory; + $this->alignLegacyCatalogInventoryByProducts = $alignLegacyCatalogInventoryByProducts ?: + ObjectManager::getInstance()->get(AlignLegacyCatalogInventoryByProducts::class); } /** @@ -77,7 +73,6 @@ public function __construct( */ public function afterExecute(SourceItemsSaveInterface $subject, $result, array $sourceItems): void { - $sourceItemsForSynchronization = []; $skuToSynchronize = []; foreach ($sourceItems as $sourceItem) { if ($sourceItem->getSourceCode() !== $this->defaultSourceProvider->getCode()) { @@ -97,11 +92,9 @@ public function afterExecute(SourceItemsSaveInterface $subject, $result, array $ continue; } - $sourceItemsForSynchronization[] = $sourceItem; $skuToSynchronize[] = $sourceItem->getSku(); } - $this->asyncSetDataToLegacyCatalogInventory->execute($skuToSynchronize); -// $this->setDataToLegacyCatalogInventory->execute($sourceItemsForSynchronization); + $this->alignLegacyCatalogInventoryByProducts->execute($skuToSynchronize); } } diff --git a/InventoryCatalog/etc/communication.xml b/InventoryCatalog/etc/communication.xml index 04432d25d827..56370407e36f 100644 --- a/InventoryCatalog/etc/communication.xml +++ b/InventoryCatalog/etc/communication.xml @@ -8,6 +8,5 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Communication/etc/communication.xsd"> <topic name="inventory.catalog.product.legacy_inventory.set_data" - request="Magento\AsynchronousOperations\Api\Data\OperationInterface"> - </topic> + request="Magento\AsynchronousOperations\Api\Data\OperationInterface" /> </config> diff --git a/InventoryCatalog/etc/config.xml b/InventoryCatalog/etc/config.xml index 1e763271d00e..3c1403a83eff 100644 --- a/InventoryCatalog/etc/config.xml +++ b/InventoryCatalog/etc/config.xml @@ -9,6 +9,9 @@ xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> <default> <cataloginventory> + <legacy_stock> + <async>0</async> + </legacy_stock> <bulk_operations> <async>0</async> <batch_size>100</batch_size> diff --git a/InventoryCatalog/etc/di.xml b/InventoryCatalog/etc/di.xml index beab754f1e6f..19c8b7e1618d 100644 --- a/InventoryCatalog/etc/di.xml +++ b/InventoryCatalog/etc/di.xml @@ -140,4 +140,10 @@ <plugin name="adapt_verify_stock_to_negative_min_qty" type="Magento\InventoryCatalog\Plugin\CatalogInventory\Model\Spi\StockStateProvider\AdaptVerifyStockToNegativeMinQtyPlugin"/> </type> + + <type name="Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\AsynchronousSetDataToLegacyCatalogInventory"> + <arguments> + <argument name="batchSize" xsi:type="number">100</argument> + </arguments> + </type> </config> diff --git a/InventoryCatalog/etc/queue_consumer.xml b/InventoryCatalog/etc/queue_consumer.xml index 0d743332bc7c..00b5db2b71b1 100644 --- a/InventoryCatalog/etc/queue_consumer.xml +++ b/InventoryCatalog/etc/queue_consumer.xml @@ -12,5 +12,5 @@ name="inventoryCatalogProductLegacyInventorySetData" queue="inventory_catalog_product_legacy_inventory_set_data_queue" connection="amqp" - handler="Magento\InventoryCatalog\Model\SourceItemsSaveSynchronization\AsyncSetDataToLegacyCatalogInventory\Consumer::processOperations"/> + handler="Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\AsynchronousSetDataToLegacyCatalogInventoryConsumer::processOperations"/> </config> \ No newline at end of file diff --git a/InventoryCatalogAdminUi/etc/adminhtml/system.xml b/InventoryCatalogAdminUi/etc/adminhtml/system.xml index 81c8b669f201..61b5ae912cf2 100755 --- a/InventoryCatalogAdminUi/etc/adminhtml/system.xml +++ b/InventoryCatalogAdminUi/etc/adminhtml/system.xml @@ -10,6 +10,16 @@ <system> <section id="cataloginventory"> + <group id="legacy_stock" translate="label" type="text" sortOrder="600" showInDefault="1" + showInWebsite="1" showInStore="1"> + <label>Legacy stock alignment</label> + <field id="async" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" + showInStore="0" canRestore="1"> + <label>Run asynchronously</label> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <comment>An asynchronous queue manager must be configured</comment> + </field> + </group> <group id="bulk_operations" translate="label" type="text" sortOrder="600" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Admin bulk operations</label> From be42cea0a05aacf26b8389cabd0ded220228a1de Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Fri, 8 Mar 2019 13:03:10 +0100 Subject: [PATCH 025/231] WIP legacy synchronization --- .../Model/GetDefaultSourceItemsBySkus.php | 2 +- .../AlignLegacyCatalogInventoryByProducts.php | 60 ----------------- .../AlignSourceItemsByProducts.php | 16 ----- ...nventoryConsumer.php => AsyncConsumer.php} | 19 +++--- .../GetLegacyStockItemsByProductIds.php | 9 +-- .../SetDataToDestination.php | 44 ++++++++++++ ...cyCatalogInventory.php => Synchronize.php} | 57 ++++++++++++++-- .../ToInventory/SetDataToSourceItem.php | 65 ++++++++++++++++++ .../SetDataToLegacyInventory.php} | 5 +- .../SetDataToLegacyCatalogInventory.php | 34 +++++----- ...eSourceItemAtLegacyStockItemSavePlugin.php | 23 ++++--- ...atalogInventoryAtSourceItemsSavePlugin.php | 24 +++---- ...ToLegacyStockItemAtSourceItemsSaveTest.php | 67 ++++++++++++++++++- InventoryCatalog/etc/di.xml | 2 +- InventoryCatalog/etc/queue_consumer.xml | 4 +- InventoryCatalog/etc/queue_topology.xml | 2 +- 16 files changed, 290 insertions(+), 143 deletions(-) delete mode 100644 InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AlignLegacyCatalogInventoryByProducts.php delete mode 100644 InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AlignSourceItemsByProducts.php rename InventoryCatalog/Model/LegacyCatalogInventorySynchronization/{AsynchronousSetDataToLegacyCatalogInventoryConsumer.php => AsyncConsumer.php} (61%) create mode 100644 InventoryCatalog/Model/LegacyCatalogInventorySynchronization/SetDataToDestination.php rename InventoryCatalog/Model/LegacyCatalogInventorySynchronization/{AsynchronousSetDataToLegacyCatalogInventory.php => Synchronize.php} (63%) create mode 100644 InventoryCatalog/Model/LegacyCatalogInventorySynchronization/ToInventory/SetDataToSourceItem.php rename InventoryCatalog/Model/LegacyCatalogInventorySynchronization/{SynchronousSetDataToLegacyCatalogInventory.php => ToLegacyCatalogInventory/SetDataToLegacyInventory.php} (95%) diff --git a/InventoryCatalog/Model/GetDefaultSourceItemsBySkus.php b/InventoryCatalog/Model/GetDefaultSourceItemsBySkus.php index f9666303d644..cc9d87d261bf 100644 --- a/InventoryCatalog/Model/GetDefaultSourceItemsBySkus.php +++ b/InventoryCatalog/Model/GetDefaultSourceItemsBySkus.php @@ -56,7 +56,7 @@ public function __construct( public function execute(array $skus): array { $searchCriteria = $this->searchCriteriaBuilder - ->addFilter(SourceItemInterface::SKU, ['in' => $skus]) + ->addFilter(SourceItemInterface::SKU, $skus, 'in') ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) ->create(); diff --git a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AlignLegacyCatalogInventoryByProducts.php b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AlignLegacyCatalogInventoryByProducts.php deleted file mode 100644 index d44f9b3f9264..000000000000 --- a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AlignLegacyCatalogInventoryByProducts.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization; - -/** - * Set Qty and status for legacy CatalogInventory Stock Item table - */ -class AlignLegacyCatalogInventoryByProducts -{ - /** - * @var IsAsyncLegacyAlignment - */ - private $isAsyncLegacyAlignment; - - /** - * @var SynchronousSetDataToLegacyCatalogInventory - */ - private $synchronousSetDataToLegacyCatalogInventory; - - /** - * @var AsynchronousSetDataToLegacyCatalogInventory - */ - private $asynchronousSetDataToLegacyCatalogInventory; - - /** - * AlignLegacyCatalogInventoryByProducts constructor. - * @param IsAsyncLegacyAlignment $isAsyncLegacyAlignment - * @param SynchronousSetDataToLegacyCatalogInventory $synchronousSetDataToLegacyCatalogInventory - * @param AsynchronousSetDataToLegacyCatalogInventory $asynchronousSetDataToLegacyCatalogInventory - * @SuppressWarnings(PHPMD.LongVariable) - */ - public function __construct( - IsAsyncLegacyAlignment $isAsyncLegacyAlignment, - SynchronousSetDataToLegacyCatalogInventory $synchronousSetDataToLegacyCatalogInventory, - AsynchronousSetDataToLegacyCatalogInventory $asynchronousSetDataToLegacyCatalogInventory - ) { - $this->isAsyncLegacyAlignment = $isAsyncLegacyAlignment; - $this->synchronousSetDataToLegacyCatalogInventory = $synchronousSetDataToLegacyCatalogInventory; - $this->asynchronousSetDataToLegacyCatalogInventory = $asynchronousSetDataToLegacyCatalogInventory; - } - - /** - * @param array $skus - * @return void - * @throws \Magento\Framework\Exception\LocalizedException - */ - public function execute(array $skus): void - { - if ($this->isAsyncLegacyAlignment->execute()) { - $this->asynchronousSetDataToLegacyCatalogInventory->execute($skus); - } else { - $this->synchronousSetDataToLegacyCatalogInventory->execute($skus); - } - } -} diff --git a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AlignSourceItemsByProducts.php b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AlignSourceItemsByProducts.php deleted file mode 100644 index f20c017bc303..000000000000 --- a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AlignSourceItemsByProducts.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization; - -/** - * Set source item from legacy CatalogInventory Stock Item table - */ -class AlignSourceItemsByProducts -{ - -} diff --git a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AsynchronousSetDataToLegacyCatalogInventoryConsumer.php b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AsyncConsumer.php similarity index 61% rename from InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AsynchronousSetDataToLegacyCatalogInventoryConsumer.php rename to InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AsyncConsumer.php index 3c8675e47d89..44c5dc82ba8b 100644 --- a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AsynchronousSetDataToLegacyCatalogInventoryConsumer.php +++ b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AsyncConsumer.php @@ -9,8 +9,9 @@ use Magento\AsynchronousOperations\Api\Data\OperationInterface; use Magento\Framework\Serialize\SerializerInterface; +use Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\ToLegacyCatalogInventory\SetDataToLegacyInventory; -class AsynchronousSetDataToLegacyCatalogInventoryConsumer +class AsyncConsumer { /** * @var SerializerInterface @@ -18,22 +19,22 @@ class AsynchronousSetDataToLegacyCatalogInventoryConsumer private $serializer; /** - * @var SynchronousSetDataToLegacyCatalogInventory + * @var SetDataToLegacyInventory */ - private $synchronousSetDataToLegacyCatalogInventory; + private $setDataToLegacyInventory; /** * Consumer constructor. * @param SerializerInterface $serializer - * @param SynchronousSetDataToLegacyCatalogInventory $synchronousSetDataToLegacyCatalogInventory + * @param SetDataToLegacyInventory $setDataToLegacyInventory * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( SerializerInterface $serializer, - SynchronousSetDataToLegacyCatalogInventory $synchronousSetDataToLegacyCatalogInventory + SetDataToLegacyInventory $setDataToLegacyInventory ) { $this->serializer = $serializer; - $this->synchronousSetDataToLegacyCatalogInventory = $synchronousSetDataToLegacyCatalogInventory; + $this->setDataToLegacyInventory = $setDataToLegacyInventory; } /** @@ -45,7 +46,9 @@ public function __construct( */ public function processOperations(OperationInterface $operation): void { - $skus = $this->serializer->unserialize($operation->getSerializedData()); - $this->synchronousSetDataToLegacyCatalogInventory->execute($skus); + $data = $this->serializer->unserialize($operation->getSerializedData()); + if ($data['direction'] === Synchronize::DIRECTION_TO_LEGACY) { + $this->setDataToLegacyInventory->execute($data['skus']); + } } } diff --git a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/GetLegacyStockItemsByProductIds.php b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/GetLegacyStockItemsByProductIds.php index 4ea085464a96..003e1ae81317 100644 --- a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/GetLegacyStockItemsByProductIds.php +++ b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/GetLegacyStockItemsByProductIds.php @@ -50,14 +50,7 @@ public function __construct( public function execute(array $productIds): array { $searchCriteria = $this->stockItemCriteriaFactory->create(); - - $searchCriteria->addFilter( - StockItemInterface::PRODUCT_ID, - StockItemInterface::PRODUCT_ID, - [ - 'in' => $productIds - ] - ); + $searchCriteria->setProductsFilter($productIds); $searchCriteria->addFilter( StockItemInterface::STOCK_ID, StockItemInterface::STOCK_ID, diff --git a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/SetDataToDestination.php b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/SetDataToDestination.php new file mode 100644 index 000000000000..68c5ac3c2b59 --- /dev/null +++ b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/SetDataToDestination.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization; + +use Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\ToLegacyCatalogInventory\SetDataToLegacyInventory; + +/** + * Set Qty and status for legacy CatalogInventory Stock Item table + */ +class SetDataToDestination +{ + /** + * @var SetDataToLegacyInventory + */ + private $setDataToLegacyInventory; + + /** + * SetDataToDestination constructor. + * @param SetDataToLegacyInventory $setDataToLegacyInventory + * @SuppressWarnings(PHPMD.LongVariable) + */ + public function __construct( + SetDataToLegacyInventory $setDataToLegacyInventory + ) { + $this->setDataToLegacyInventory = $setDataToLegacyInventory; + } + + /** + * @param string $direction + * @param array $skus + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function execute(string $direction, array $skus): void + { + if ($direction === Synchronize::DIRECTION_TO_LEGACY) { + $this->setDataToLegacyInventory->execute($skus); + } + } +} diff --git a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AsynchronousSetDataToLegacyCatalogInventory.php b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/Synchronize.php similarity index 63% rename from InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AsynchronousSetDataToLegacyCatalogInventory.php rename to InventoryCatalog/Model/LegacyCatalogInventorySynchronization/Synchronize.php index c74810a1797d..ff77672bb16f 100644 --- a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AsynchronousSetDataToLegacyCatalogInventory.php +++ b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/Synchronize.php @@ -16,10 +16,13 @@ /** * Set Qty and status for legacy CatalogInventory Stock Item table */ -class AsynchronousSetDataToLegacyCatalogInventory +class Synchronize { private const TOPIC_NAME = 'inventory.catalog.product.legacy_inventory.set_data'; + public const DIRECTION_TO_LEGACY = 'to-legacy'; + public const DIRECTION_TO_INVENTORY = 'to-inventory'; + /** * @var BulkManagementInterface */ @@ -40,6 +43,16 @@ class AsynchronousSetDataToLegacyCatalogInventory */ private $operationInterfaceFactory; + /** + * @var IsAsyncLegacyAlignment + */ + private $isAsyncLegacyAlignment; + + /** + * @var SetDataToDestination + */ + private $setDataToDestination; + /** * @var int */ @@ -51,6 +64,8 @@ class AsynchronousSetDataToLegacyCatalogInventory * @param SerializerInterface $serializer * @param IdentityGeneratorInterface $identityService * @param OperationInterfaceFactory $operationInterfaceFactory + * @param IsAsyncLegacyAlignment $isAsyncLegacyAlignment + * @param SetDataToDestination $setDataToDestination * @param int $batchSize */ public function __construct( @@ -58,6 +73,8 @@ public function __construct( SerializerInterface $serializer, IdentityGeneratorInterface $identityService, OperationInterfaceFactory $operationInterfaceFactory, + IsAsyncLegacyAlignment $isAsyncLegacyAlignment, + SetDataToDestination $setDataToDestination, int $batchSize ) { $this->bulkManagement = $bulkManagement; @@ -65,13 +82,15 @@ public function __construct( $this->serializer = $serializer; $this->operationInterfaceFactory = $operationInterfaceFactory; $this->batchSize = $batchSize; + $this->isAsyncLegacyAlignment = $isAsyncLegacyAlignment; + $this->setDataToDestination = $setDataToDestination; } /** + * @param string $direction * @param array $skus - * @return void */ - public function execute(array $skus): void + private function executeAsync(string $direction, array $skus): void { $operations = []; @@ -83,7 +102,12 @@ public function execute(array $skus): void 'data' => [ 'bulk_uuid' => $bulkUuid, 'topic_name' => self::TOPIC_NAME, - 'serialized_data' => $this->serializer->serialize($chunk), + 'serialized_data' => $this->serializer->serialize( + [ + 'direction' => $direction, + 'skus' => $chunk + ] + ), 'status' => OperationInterface::STATUS_TYPE_OPEN, ] ]; @@ -95,4 +119,29 @@ public function execute(array $skus): void $this->bulkManagement->scheduleBulk($bulkUuid, $operations, __('Set legacy stock data')); } + + /** + * @param string $direction + * @param array $skus + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function executeSync(string $direction, array $skus): void + { + $this->setDataToDestination->execute($direction, $skus); + } + + /** + * @param string $direction + * @param array $skus + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function execute(string $direction, array $skus): void + { + if ($this->isAsyncLegacyAlignment->execute()) { + $this->executeAsync($direction, $skus); + } else { + $this->executeSync($direction, $skus); + } + } } diff --git a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/ToInventory/SetDataToSourceItem.php b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/ToInventory/SetDataToSourceItem.php new file mode 100644 index 000000000000..0171d28bd950 --- /dev/null +++ b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/ToInventory/SetDataToSourceItem.php @@ -0,0 +1,65 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\ToLegacyCatalogInventory; + +use Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\GetLegacyStockItemsByProductIds; +use Magento\InventoryCatalog\Model\UpdateSourceItemBasedOnLegacyStockItem; +use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface; + +class SetDataToSourceItem +{ + /** + * @var UpdateSourceItemBasedOnLegacyStockItem + */ + private $updateSourceItemBasedOnLegacyStockItem; + + /** + * @var GetLegacyStockItemsByProductIds + */ + private $getLegacyStockItemsByProductIds; + + /** + * @var GetProductIdsBySkusInterface + */ + private $getProductIdsBySkus; + + /** + * SetDataToSourceItem constructor. + * @param UpdateSourceItemBasedOnLegacyStockItem $updateSourceItemBasedOnLegacyStockItem + * @param GetLegacyStockItemsByProductIds $getLegacyStockItemsByProductIds + * @param GetProductIdsBySkusInterface $getProductIdsBySkus + * @SuppressWarnings(PHPMD.LongVariable) + */ + public function __construct( + UpdateSourceItemBasedOnLegacyStockItem $updateSourceItemBasedOnLegacyStockItem, + GetLegacyStockItemsByProductIds $getLegacyStockItemsByProductIds, + GetProductIdsBySkusInterface $getProductIdsBySkus + ) { + $this->updateSourceItemBasedOnLegacyStockItem = $updateSourceItemBasedOnLegacyStockItem; + $this->getLegacyStockItemsByProductIds = $getLegacyStockItemsByProductIds; + $this->getProductIdsBySkus = $getProductIdsBySkus; + } + + /** + * @param array $skus + * @throws \Magento\Framework\Exception\CouldNotSaveException + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws \Magento\Framework\Validation\ValidationException + */ + public function execute(array $skus): void + { + $productIds = $this->getProductIdsBySkus->execute($skus); + $stockItems = $this->getLegacyStockItemsByProductIds->execute($productIds); + + foreach ($stockItems as $stockItem) { + $this->updateSourceItemBasedOnLegacyStockItem->execute($stockItem); + } + } +} diff --git a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/SynchronousSetDataToLegacyCatalogInventory.php b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/ToLegacyCatalogInventory/SetDataToLegacyInventory.php similarity index 95% rename from InventoryCatalog/Model/LegacyCatalogInventorySynchronization/SynchronousSetDataToLegacyCatalogInventory.php rename to InventoryCatalog/Model/LegacyCatalogInventorySynchronization/ToLegacyCatalogInventory/SetDataToLegacyInventory.php index fb1beea7d6b3..4f532eb5ce78 100644 --- a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/SynchronousSetDataToLegacyCatalogInventory.php +++ b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/ToLegacyCatalogInventory/SetDataToLegacyInventory.php @@ -5,16 +5,17 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization; +namespace Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\ToLegacyCatalogInventory; use Magento\Catalog\Model\ResourceModel\Product; use Magento\CatalogInventory\Model\Indexer\Stock\Processor; use Magento\CatalogInventory\Model\Spi\StockStateProviderInterface; use Magento\Framework\Exception\LocalizedException; use Magento\InventoryCatalog\Model\GetDefaultSourceItemsBySkus; +use Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\GetLegacyStockItemsByProductIds; use Magento\InventoryCatalog\Model\ResourceModel\SetDataToLegacyStockItem; -class SynchronousSetDataToLegacyCatalogInventory +class SetDataToLegacyInventory { /** * @var SetDataToLegacyStockItem diff --git a/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php b/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php index 43e5156b1d77..eb5c966a436d 100644 --- a/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php +++ b/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php @@ -7,38 +7,35 @@ namespace Magento\InventoryCatalog\Model\SourceItemsSaveSynchronization; -use Magento\CatalogInventory\Api\Data\StockItemInterface; use Magento\CatalogInventory\Api\StockItemRepositoryInterface; use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory; use Magento\CatalogInventory\Model\Indexer\Stock\Processor; use Magento\CatalogInventory\Model\Spi\StockStateProviderInterface; -use Magento\CatalogInventory\Model\Stock; use Magento\Framework\App\ObjectManager; -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\AlignLegacyCatalogInventoryByProducts; +use Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\Synchronize; use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface; use Magento\InventoryCatalog\Model\ResourceModel\SetDataToLegacyStockItem; /** * Set Qty and status for legacy CatalogInventory Stock Item table * @deprecated - * @see \Magento\InventoryCatalog\Model\SourceItemsSaveSynchronization\SetDataToLegacyCatalogInventory + * @see \Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\Synchronize */ class SetDataToLegacyCatalogInventory { /** - * @var AlignLegacyCatalogInventoryByProducts + * @var Synchronize */ - private $alignLegacyCatalogInventoryByProducts; + private $synchronize; /** - * @param SetDataToLegacyStockItem $setDataToLegacyStockItem - * @param StockItemCriteriaInterfaceFactory $legacyStockItemCriteriaFactory - * @param StockItemRepositoryInterface $legacyStockItemRepository - * @param GetProductIdsBySkusInterface $getProductIdsBySkus - * @param StockStateProviderInterface $stockStateProvider - * @param Processor $indexerProcessor - * @param AlignLegacyCatalogInventoryByProducts $alignLegacyCatalogInventoryByProducts + * @param SetDataToLegacyStockItem $setDataToLegacyStockItem @deprecated + * @param StockItemCriteriaInterfaceFactory $legacyStockItemCriteriaFactory @deprecated + * @param StockItemRepositoryInterface $legacyStockItemRepository @deprecated + * @param GetProductIdsBySkusInterface $getProductIdsBySkus @deprecated + * @param StockStateProviderInterface $stockStateProvider @deprecated + * @param Processor $indexerProcessor @deprecated + * @param Synchronize $synchronize * @SupressWarnings(PHPMD.UnusedFormalParameter) * @SupressWarnings(PHPMD.LongVariable) */ @@ -49,15 +46,16 @@ public function __construct( GetProductIdsBySkusInterface $getProductIdsBySkus, StockStateProviderInterface $stockStateProvider, Processor $indexerProcessor, - AlignLegacyCatalogInventoryByProducts $alignLegacyCatalogInventoryByProducts = null + Synchronize $synchronize = null ) { - $this->alignLegacyCatalogInventoryByProducts = $alignLegacyCatalogInventoryByProducts ?: - ObjectManager::getInstance()->get(AlignLegacyCatalogInventoryByProducts::class); + $this->alignLegacyCatalogInventoryByProducts = $synchronize ?: + ObjectManager::getInstance()->get(Synchronize::class); } /** * @param array $sourceItems * @return void + * @throws \Magento\Framework\Exception\LocalizedException */ public function execute(array $sourceItems): void { @@ -66,6 +64,6 @@ public function execute(array $sourceItems): void $skus[] = $sourceItem->getSku(); } - $this->alignLegacyCatalogInventoryByProducts->execute($skus); + $this->synchronize->execute(Synchronize::DIRECTION_TO_LEGACY, $skus); } } diff --git a/InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyStockItemSavePlugin.php b/InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyStockItemSavePlugin.php index a9de68821e3e..e7ca16cae3bf 100755 --- a/InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyStockItemSavePlugin.php +++ b/InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyStockItemSavePlugin.php @@ -9,9 +9,11 @@ use Magento\CatalogInventory\Model\ResourceModel\Stock\Item as ItemResourceModel; use Magento\CatalogInventory\Model\Stock\Item; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\ResourceConnection; use Magento\Framework\Model\AbstractModel; use Magento\InventoryCatalog\Model\GetDefaultSourceItemBySku; +use Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\Synchronize; use Magento\InventoryCatalogApi\Model\GetProductTypesBySkusInterface; use Magento\InventoryCatalogApi\Model\GetSkusByProductIdsInterface; use Magento\InventoryCatalog\Model\UpdateSourceItemBasedOnLegacyStockItem; @@ -33,11 +35,6 @@ class UpdateSourceItemAtLegacyStockItemSavePlugin */ private $isSourceItemManagementAllowedForProductType; - /** - * @var UpdateSourceItemBasedOnLegacyStockItem - */ - private $updateSourceItemBasedOnLegacyStockItem; - /** * @var GetProductTypesBySkusInterface */ @@ -54,12 +51,18 @@ class UpdateSourceItemAtLegacyStockItemSavePlugin private $getDefaultSourceItemBySku; /** - * @param UpdateSourceItemBasedOnLegacyStockItem $updateSourceItemBasedOnLegacyStockItem + * @var Synchronize + */ + private $synchronize; + + /** + * @param UpdateSourceItemBasedOnLegacyStockItem $updateSourceItemBasedOnLegacyStockItem @deprecated * @param ResourceConnection $resourceConnection * @param IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemManagementAllowedForProductType * @param GetProductTypesBySkusInterface $getProductTypeBySku * @param GetSkusByProductIdsInterface $getSkusByProductIds * @param GetDefaultSourceItemBySku $getDefaultSourceItemBySku + * @param Synchronize|null $synchronize */ public function __construct( UpdateSourceItemBasedOnLegacyStockItem $updateSourceItemBasedOnLegacyStockItem, @@ -67,14 +70,16 @@ public function __construct( IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemManagementAllowedForProductType, GetProductTypesBySkusInterface $getProductTypeBySku, GetSkusByProductIdsInterface $getSkusByProductIds, - GetDefaultSourceItemBySku $getDefaultSourceItemBySku + GetDefaultSourceItemBySku $getDefaultSourceItemBySku, + Synchronize $synchronize = null ) { - $this->updateSourceItemBasedOnLegacyStockItem = $updateSourceItemBasedOnLegacyStockItem; $this->resourceConnection = $resourceConnection; $this->isSourceItemManagementAllowedForProductType = $isSourceItemManagementAllowedForProductType; $this->getProductTypeBySku = $getProductTypeBySku; $this->getSkusByProductIds = $getSkusByProductIds; $this->getDefaultSourceItemBySku = $getDefaultSourceItemBySku; + $this->synchronize = $synchronize ?: + ObjectManager::getInstance()->get(Synchronize::class); } /** @@ -97,7 +102,7 @@ public function aroundSave(ItemResourceModel $subject, callable $proceed, Abstra $typeId = $this->getTypeId($legacyStockItem); if ($this->isSourceItemManagementAllowedForProductType->execute($typeId)) { if ($this->shouldAlignDefaultSourceWithLegacy($legacyStockItem)) { - $this->updateSourceItemBasedOnLegacyStockItem->execute($legacyStockItem); + $this->synchronize->execute(Synchronize::DIRECTION_TO_INVENTORY, $legacyStockItem->getSku()); } } diff --git a/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php b/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php index b59f9a7274e2..23f605c6e14f 100644 --- a/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php +++ b/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php @@ -10,7 +10,7 @@ use Magento\Framework\App\ObjectManager; use Magento\InventoryApi\Api\Data\SourceItemInterface; use Magento\InventoryApi\Api\SourceItemsSaveInterface; -use Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\AlignLegacyCatalogInventoryByProducts; +use Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\Synchronize; use Magento\InventoryCatalog\Model\SourceItemsSaveSynchronization\SetDataToLegacyCatalogInventory; use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; use Magento\InventoryCatalogApi\Model\GetProductTypesBySkusInterface; @@ -37,16 +37,16 @@ class SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin private $getProductTypeBySku; /** - * @var AlignLegacyCatalogInventoryByProducts|null + * @var Synchronize|null */ - private $alignLegacyCatalogInventoryByProducts; + private $synchronize; /** - * @param DefaultSourceProviderInterface $defaultSourceProvider - * @param IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemsAllowedForProductType - * @param GetProductTypesBySkusInterface $getProductTypeBySku - * @param SetDataToLegacyCatalogInventory $setDataToLegacyCatalogInventory - * @param AlignLegacyCatalogInventoryByProducts|null $alignLegacyCatalogInventoryByProducts + * @param DefaultSourceProviderInterface $defaultSourceProvider @deprecated + * @param IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemsAllowedForProductType @deprecated + * @param GetProductTypesBySkusInterface $getProductTypeBySku @deprecated + * @param SetDataToLegacyCatalogInventory $setDataToLegacyCatalogInventory @deprecated + * @param Synchronize|null $synchronize * @SuppressWarnings(PHPMD.LongVariable) * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ @@ -55,13 +55,13 @@ public function __construct( IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemsAllowedForProductType, GetProductTypesBySkusInterface $getProductTypeBySku, SetDataToLegacyCatalogInventory $setDataToLegacyCatalogInventory, - AlignLegacyCatalogInventoryByProducts $alignLegacyCatalogInventoryByProducts = null + Synchronize $synchronize = null ) { $this->defaultSourceProvider = $defaultSourceProvider; $this->isSourceItemsAllowedForProductType = $isSourceItemsAllowedForProductType; $this->getProductTypeBySku = $getProductTypeBySku; - $this->alignLegacyCatalogInventoryByProducts = $alignLegacyCatalogInventoryByProducts ?: - ObjectManager::getInstance()->get(AlignLegacyCatalogInventoryByProducts::class); + $this->synchronize = $synchronize ?: + ObjectManager::getInstance()->get(Synchronize::class); } /** @@ -95,6 +95,6 @@ public function afterExecute(SourceItemsSaveInterface $subject, $result, array $ $skuToSynchronize[] = $sourceItem->getSku(); } - $this->alignLegacyCatalogInventoryByProducts->execute($skuToSynchronize); + $this->synchronize->execute(Synchronize::DIRECTION_TO_LEGACY, $skuToSynchronize); } } diff --git a/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php b/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php index 8cf028ca56cc..ecd82e124ba5 100644 --- a/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php +++ b/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php @@ -9,6 +9,7 @@ use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\MessageQueue\ConsumerInterface; use Magento\InventoryApi\Api\Data\SourceItemInterface; use Magento\InventoryApi\Api\SourceItemRepositoryInterface; use Magento\InventoryApi\Api\SourceItemsSaveInterface; @@ -56,6 +57,12 @@ class SetDataToLegacyStockItemAtSourceItemsSaveTest extends TestCase */ private $defaultSourceProvider; + /** + * @var ConsumerInterface + */ + private $consumer; + + protected function setUp() { $this->productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); @@ -70,12 +77,17 @@ protected function setUp() $this->sourceItemsSave = Bootstrap::getObjectManager()->get(SourceItemsSaveInterface::class); $this->defaultSourceProvider = Bootstrap::getObjectManager()->get(DefaultSourceProviderInterface::class); + + $this->consumer = Bootstrap::getObjectManager()->create( + \Magento\Framework\MessageQueue\ConsumerFactory::class + )->get('legacyCatalogInventorySynchronization', 100); } /** * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php + * @SuppressWarnings(PHPMD.LongVariable) */ public function testSetData() { @@ -103,7 +115,7 @@ public function testSetData() self::assertCount(1, $sourceItems); $sourceItem = reset($sourceItems); - $sourceItem->setQuantity(20); + $sourceItem->setQuantity(20.0); $sourceItem->setStatus(SourceItemInterface::STATUS_OUT_OF_STOCK); $this->sourceItemsSave->execute($sourceItems); @@ -114,4 +126,57 @@ public function testSetData() self::assertFalse($legacyStockItem->getIsInStock()); self::assertEquals(20, $legacyStockItem->getQty()); } + + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php + * @magentoAdminConfigFixture cataloginventory/legacy_stock/async 1 + * @SuppressWarnings(PHPMD.LongVariable) + */ + public function testSetDataAsynchronously() + { + $productSku = 'SKU-1'; + $product = $this->productRepository->get($productSku); + $productId = $product->getId(); + $websiteId = 0; + + /** @var StockItemCriteriaInterface $legacyStockItemCriteria */ + $legacyStockItemCriteria = $this->legacyStockItemCriteriaFactory->create(); + $legacyStockItemCriteria->setProductsFilter($productId); + $legacyStockItemCriteria->setScopeFilter($websiteId); + $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); + self::assertCount(1, $legacyStockItems); + + $legacyStockItem = reset($legacyStockItems); + self::assertTrue($legacyStockItem->getIsInStock()); + self::assertEquals(5.5, $legacyStockItem->getQty()); + + $searchCriteria = $this->searchCriteriaBuilder + ->addFilter(SourceItemInterface::SKU, $productSku) + ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) + ->create(); + $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); + self::assertCount(1, $sourceItems); + + $sourceItem = reset($sourceItems); + $sourceItem->setQuantity(20.0); + $sourceItem->setStatus(SourceItemInterface::STATUS_OUT_OF_STOCK); + $this->sourceItemsSave->execute($sourceItems); + + // Make sure we did not yet synchronized it + $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); + $legacyStockItem = current($legacyStockItems); + self::assertCount(1, $legacyStockItems); + self::assertTrue($legacyStockItem->getIsInStock()); + self::assertEquals(5.5, $legacyStockItem->getQty()); + + $this->consumer->process(1); + + // Check after asynchrnous consumer call + $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); + $legacyStockItem = current($legacyStockItems); + self::assertFalse($legacyStockItem->getIsInStock()); + self::assertEquals(20, $legacyStockItem->getQty()); + } } diff --git a/InventoryCatalog/etc/di.xml b/InventoryCatalog/etc/di.xml index 19c8b7e1618d..477abfac65e5 100644 --- a/InventoryCatalog/etc/di.xml +++ b/InventoryCatalog/etc/di.xml @@ -141,7 +141,7 @@ type="Magento\InventoryCatalog\Plugin\CatalogInventory\Model\Spi\StockStateProvider\AdaptVerifyStockToNegativeMinQtyPlugin"/> </type> - <type name="Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\AsynchronousSetDataToLegacyCatalogInventory"> + <type name="Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\Synchronize"> <arguments> <argument name="batchSize" xsi:type="number">100</argument> </arguments> diff --git a/InventoryCatalog/etc/queue_consumer.xml b/InventoryCatalog/etc/queue_consumer.xml index 00b5db2b71b1..904cbb52f2a9 100644 --- a/InventoryCatalog/etc/queue_consumer.xml +++ b/InventoryCatalog/etc/queue_consumer.xml @@ -9,8 +9,8 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework-message-queue:etc/queue_consumer.xsd"> <consumer - name="inventoryCatalogProductLegacyInventorySetData" + name="legacyCatalogInventorySynchronization" queue="inventory_catalog_product_legacy_inventory_set_data_queue" connection="amqp" - handler="Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\AsynchronousSetDataToLegacyCatalogInventoryConsumer::processOperations"/> + handler="Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\AsyncConsumer::processOperations"/> </config> \ No newline at end of file diff --git a/InventoryCatalog/etc/queue_topology.xml b/InventoryCatalog/etc/queue_topology.xml index 4cf88b59880b..d252b7a82b88 100644 --- a/InventoryCatalog/etc/queue_topology.xml +++ b/InventoryCatalog/etc/queue_topology.xml @@ -11,7 +11,7 @@ <exchange name="inventory.catalog.product.legacy_inventory.set_data.exchange" type="topic" connection="amqp"> <binding - id="inventoryCatalogProductLegacyInventorySetDataExchangeBinding" + id="legacyCatalogInventorySynchronizationExchangeBinding" topic="inventory.catalog.product.legacy_inventory.set_data" destinationType="queue" destination="inventory_catalog_product_legacy_inventory_set_data_queue"/> From 5034473f1b29ce4603eddd09f37e1e03ebd78ace Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Fri, 8 Mar 2019 14:47:33 +0100 Subject: [PATCH 026/231] Async legacy stock to source item alignment --- .../AsyncConsumer.php | 15 +++---- .../SetDataToDestination.php | 13 +++++- .../ToInventory/SetDataToSourceItem.php | 25 +++++------ ...eSourceItemAtLegacyStockItemSavePlugin.php | 21 ++++++++-- ...ToLegacyStockItemAtSourceItemsSaveTest.php | 6 +-- ...ultSourceItemAtLegacyStockItemSaveTest.php | 41 +++++++++++++++++++ 6 files changed, 93 insertions(+), 28 deletions(-) diff --git a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AsyncConsumer.php b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AsyncConsumer.php index 44c5dc82ba8b..cb4aa3b61eee 100644 --- a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AsyncConsumer.php +++ b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AsyncConsumer.php @@ -9,7 +9,6 @@ use Magento\AsynchronousOperations\Api\Data\OperationInterface; use Magento\Framework\Serialize\SerializerInterface; -use Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\ToLegacyCatalogInventory\SetDataToLegacyInventory; class AsyncConsumer { @@ -19,22 +18,22 @@ class AsyncConsumer private $serializer; /** - * @var SetDataToLegacyInventory + * @var SetDataToDestination */ - private $setDataToLegacyInventory; + private $setDataToDestination; /** * Consumer constructor. * @param SerializerInterface $serializer - * @param SetDataToLegacyInventory $setDataToLegacyInventory + * @param SetDataToDestination $setDataToDestination * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( SerializerInterface $serializer, - SetDataToLegacyInventory $setDataToLegacyInventory + SetDataToDestination $setDataToDestination ) { $this->serializer = $serializer; - $this->setDataToLegacyInventory = $setDataToLegacyInventory; + $this->setDataToDestination = $setDataToDestination; } /** @@ -47,8 +46,6 @@ public function __construct( public function processOperations(OperationInterface $operation): void { $data = $this->serializer->unserialize($operation->getSerializedData()); - if ($data['direction'] === Synchronize::DIRECTION_TO_LEGACY) { - $this->setDataToLegacyInventory->execute($data['skus']); - } + $this->setDataToDestination->execute($data['direction'], $data['skus']); } } diff --git a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/SetDataToDestination.php b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/SetDataToDestination.php index 68c5ac3c2b59..33b1867f4f0e 100644 --- a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/SetDataToDestination.php +++ b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/SetDataToDestination.php @@ -7,6 +7,7 @@ namespace Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization; +use Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\ToInventory\SetDataToSourceItem; use Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\ToLegacyCatalogInventory\SetDataToLegacyInventory; /** @@ -19,15 +20,23 @@ class SetDataToDestination */ private $setDataToLegacyInventory; + /** + * @var SetDataToSourceItem + */ + private $setDataToSourceItem; + /** * SetDataToDestination constructor. * @param SetDataToLegacyInventory $setDataToLegacyInventory + * @param SetDataToSourceItem $setDataToSourceItem * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( - SetDataToLegacyInventory $setDataToLegacyInventory + SetDataToLegacyInventory $setDataToLegacyInventory, + SetDataToSourceItem $setDataToSourceItem ) { $this->setDataToLegacyInventory = $setDataToLegacyInventory; + $this->setDataToSourceItem = $setDataToSourceItem; } /** @@ -39,6 +48,8 @@ public function execute(string $direction, array $skus): void { if ($direction === Synchronize::DIRECTION_TO_LEGACY) { $this->setDataToLegacyInventory->execute($skus); + } else { + $this->setDataToSourceItem->execute($skus); } } } diff --git a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/ToInventory/SetDataToSourceItem.php b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/ToInventory/SetDataToSourceItem.php index 0171d28bd950..72f8c1f37635 100644 --- a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/ToInventory/SetDataToSourceItem.php +++ b/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/ToInventory/SetDataToSourceItem.php @@ -5,11 +5,11 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\ToLegacyCatalogInventory; +namespace Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\ToInventory; +use Magento\Catalog\Model\ResourceModel\Product; use Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\GetLegacyStockItemsByProductIds; use Magento\InventoryCatalog\Model\UpdateSourceItemBasedOnLegacyStockItem; -use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface; class SetDataToSourceItem { @@ -24,25 +24,25 @@ class SetDataToSourceItem private $getLegacyStockItemsByProductIds; /** - * @var GetProductIdsBySkusInterface + * @var Product */ - private $getProductIdsBySkus; + private $productResource; /** * SetDataToSourceItem constructor. * @param UpdateSourceItemBasedOnLegacyStockItem $updateSourceItemBasedOnLegacyStockItem * @param GetLegacyStockItemsByProductIds $getLegacyStockItemsByProductIds - * @param GetProductIdsBySkusInterface $getProductIdsBySkus + * @param Product $productResource * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( UpdateSourceItemBasedOnLegacyStockItem $updateSourceItemBasedOnLegacyStockItem, GetLegacyStockItemsByProductIds $getLegacyStockItemsByProductIds, - GetProductIdsBySkusInterface $getProductIdsBySkus + Product $productResource ) { $this->updateSourceItemBasedOnLegacyStockItem = $updateSourceItemBasedOnLegacyStockItem; + $this->productResource = $productResource; $this->getLegacyStockItemsByProductIds = $getLegacyStockItemsByProductIds; - $this->getProductIdsBySkus = $getProductIdsBySkus; } /** @@ -50,16 +50,17 @@ public function __construct( * @throws \Magento\Framework\Exception\CouldNotSaveException * @throws \Magento\Framework\Exception\InputException * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Magento\Framework\Exception\NoSuchEntityException * @throws \Magento\Framework\Validation\ValidationException */ public function execute(array $skus): void { - $productIds = $this->getProductIdsBySkus->execute($skus); - $stockItems = $this->getLegacyStockItemsByProductIds->execute($productIds); + $productIds = $this->productResource->getProductsIdsBySkus($skus); + if (!empty($productIds)) { + $stockItems = $this->getLegacyStockItemsByProductIds->execute($productIds); - foreach ($stockItems as $stockItem) { - $this->updateSourceItemBasedOnLegacyStockItem->execute($stockItem); + foreach ($stockItems as $stockItem) { + $this->updateSourceItemBasedOnLegacyStockItem->execute($stockItem); + } } } } diff --git a/InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyStockItemSavePlugin.php b/InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyStockItemSavePlugin.php index e7ca16cae3bf..606d6e45dd61 100755 --- a/InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyStockItemSavePlugin.php +++ b/InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyStockItemSavePlugin.php @@ -102,7 +102,12 @@ public function aroundSave(ItemResourceModel $subject, callable $proceed, Abstra $typeId = $this->getTypeId($legacyStockItem); if ($this->isSourceItemManagementAllowedForProductType->execute($typeId)) { if ($this->shouldAlignDefaultSourceWithLegacy($legacyStockItem)) { - $this->synchronize->execute(Synchronize::DIRECTION_TO_INVENTORY, $legacyStockItem->getSku()); + $this->synchronize->execute( + Synchronize::DIRECTION_TO_INVENTORY, + [ + $this->getProductSkuById((int) $legacyStockItem->getProductId()) + ] + ); } } @@ -115,6 +120,17 @@ public function aroundSave(ItemResourceModel $subject, callable $proceed, Abstra } } + /** + * @param int $productId + * @return string + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function getProductSkuById(int $productId): string + { + return $this->getSkusByProductIds + ->execute([$productId])[$productId]; + } + /** * Return true if legacy stock item should update default source (if existing) * @param Item $legacyStockItem @@ -123,8 +139,7 @@ public function aroundSave(ItemResourceModel $subject, callable $proceed, Abstra */ private function shouldAlignDefaultSourceWithLegacy(Item $legacyStockItem): bool { - $productSku = $this->getSkusByProductIds - ->execute([$legacyStockItem->getProductId()])[$legacyStockItem->getProductId()]; + $productSku = $this->getProductSkuById((int) $legacyStockItem->getProductId()); $result = $legacyStockItem->getIsInStock() || ((float) $legacyStockItem->getQty() !== (float) 0) || diff --git a/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php b/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php index ecd82e124ba5..898499084330 100644 --- a/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php +++ b/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php @@ -9,6 +9,7 @@ use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\MessageQueue\ConsumerFactory; use Magento\Framework\MessageQueue\ConsumerInterface; use Magento\InventoryApi\Api\Data\SourceItemInterface; use Magento\InventoryApi\Api\SourceItemRepositoryInterface; @@ -78,9 +79,8 @@ protected function setUp() $this->sourceItemsSave = Bootstrap::getObjectManager()->get(SourceItemsSaveInterface::class); $this->defaultSourceProvider = Bootstrap::getObjectManager()->get(DefaultSourceProviderInterface::class); - $this->consumer = Bootstrap::getObjectManager()->create( - \Magento\Framework\MessageQueue\ConsumerFactory::class - )->get('legacyCatalogInventorySynchronization', 100); + $this->consumer = Bootstrap::getObjectManager()->create(ConsumerFactory::class) + ->get('legacyCatalogInventorySynchronization', 100); } /** diff --git a/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php b/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php index 4d8c4b21bd70..3de7f228dde6 100644 --- a/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php +++ b/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php @@ -8,6 +8,8 @@ namespace Magento\InventoryCatalog\Test\Integration; use Magento\CatalogInventory\Api\StockRegistryInterface; +use Magento\Framework\MessageQueue\ConsumerFactory; +use Magento\Framework\MessageQueue\ConsumerInterface; use Magento\InventoryCatalog\Model\GetDefaultSourceItemBySku; use PHPUnit\Framework\TestCase; use Magento\TestFramework\Helper\Bootstrap; @@ -24,12 +26,19 @@ class UpdateDefaultSourceItemAtLegacyStockItemSaveTest extends TestCase */ private $getDefaultSourceItemBySku; + /** + * @var ConsumerInterface + */ + private $consumer; + protected function setUp() { parent::setUp(); $this->stockRegistry = Bootstrap::getObjectManager()->create(StockRegistryInterface::class); $this->getDefaultSourceItemBySku = Bootstrap::getObjectManager()->get(GetDefaultSourceItemBySku::class); + $this->consumer = Bootstrap::getObjectManager()->create(ConsumerFactory::class) + ->get('legacyCatalogInventorySynchronization', 100); } /** @@ -55,6 +64,38 @@ public function testSaveLegacyStockItemAssignedToDefaultSource() ); } + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php + * @magentoAdminConfigFixture cataloginventory/legacy_stock/async 1 + * @magentoDbIsolation enabled + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function testSaveLegacyStockItemAssignedToDefaultSourceAsynchronously() + { + $stockItem = $this->stockRegistry->getStockItemBySku('SKU-1'); + $stockItem->setQty(10); + $this->stockRegistry->updateStockItemBySku('SKU-1', $stockItem); + + $defaultSourceItem = $this->getDefaultSourceItemBySku->execute('SKU-1'); + self::assertEquals( + 5.5, + $defaultSourceItem->getQuantity(), + 'Source item was update synchronously even if asynchronous operation was requested' + ); + + $this->consumer->process(1); + + $defaultSourceItem = $this->getDefaultSourceItemBySku->execute('SKU-1'); + self::assertEquals( + 10, + $defaultSourceItem->getQuantity(), + 'Asynchronous source item update failed' + ); + } + /** * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php From 86f3ec015a99bf0c27cb9a9f7fd26cfa5c44e139 Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Wed, 13 Mar 2019 11:14:58 +0100 Subject: [PATCH 027/231] FIX CS --- .../SetDataToLegacyCatalogInventory.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php b/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php index eb5c966a436d..1a5c7bc26e4f 100644 --- a/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php +++ b/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php @@ -36,8 +36,8 @@ class SetDataToLegacyCatalogInventory * @param StockStateProviderInterface $stockStateProvider @deprecated * @param Processor $indexerProcessor @deprecated * @param Synchronize $synchronize - * @SupressWarnings(PHPMD.UnusedFormalParameter) - * @SupressWarnings(PHPMD.LongVariable) + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( SetDataToLegacyStockItem $setDataToLegacyStockItem, From d2541f168986a0e29e560fb2ada51b44896a0974 Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Wed, 13 Mar 2019 11:54:33 +0100 Subject: [PATCH 028/231] FIX CS --- .../AsyncConsumer.php | 2 +- .../GetLegacyStockItemsByProductIds.php | 4 ++-- .../IsAsyncLegacyAlignment.php | 2 +- .../SetDataToDestination.php | 6 +++--- .../Synchronize.php | 2 +- .../ToInventory/SetDataToSourceItem.php | 4 ++-- .../ToLegacyCatalogInventory/SetDataToLegacyInventory.php | 4 ++-- .../SetDataToLegacyCatalogInventory.php | 4 ++-- .../UpdateSourceItemAtLegacyStockItemSavePlugin.php | 2 +- ...tDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php | 2 +- .../SetDataToLegacyStockItemAtSourceItemsSaveTest.php | 1 - InventoryCatalog/etc/di.xml | 2 +- InventoryCatalog/etc/queue_consumer.xml | 2 +- 13 files changed, 18 insertions(+), 19 deletions(-) rename InventoryCatalog/Model/{LegacyCatalogInventorySynchronization => LegacySynchronization}/AsyncConsumer.php (94%) rename InventoryCatalog/Model/{LegacyCatalogInventorySynchronization => LegacySynchronization}/GetLegacyStockItemsByProductIds.php (94%) rename InventoryCatalog/Model/{LegacyCatalogInventorySynchronization => LegacySynchronization}/IsAsyncLegacyAlignment.php (91%) rename InventoryCatalog/Model/{LegacyCatalogInventorySynchronization => LegacySynchronization}/SetDataToDestination.php (81%) rename InventoryCatalog/Model/{LegacyCatalogInventorySynchronization => LegacySynchronization}/Synchronize.php (98%) rename InventoryCatalog/Model/{LegacyCatalogInventorySynchronization => LegacySynchronization}/ToInventory/SetDataToSourceItem.php (91%) rename InventoryCatalog/Model/{LegacyCatalogInventorySynchronization => LegacySynchronization}/ToLegacyCatalogInventory/SetDataToLegacyInventory.php (94%) diff --git a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AsyncConsumer.php b/InventoryCatalog/Model/LegacySynchronization/AsyncConsumer.php similarity index 94% rename from InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AsyncConsumer.php rename to InventoryCatalog/Model/LegacySynchronization/AsyncConsumer.php index cb4aa3b61eee..029aef5c7d28 100644 --- a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/AsyncConsumer.php +++ b/InventoryCatalog/Model/LegacySynchronization/AsyncConsumer.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization; +namespace Magento\InventoryCatalog\Model\LegacySynchronization; use Magento\AsynchronousOperations\Api\Data\OperationInterface; use Magento\Framework\Serialize\SerializerInterface; diff --git a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/GetLegacyStockItemsByProductIds.php b/InventoryCatalog/Model/LegacySynchronization/GetLegacyStockItemsByProductIds.php similarity index 94% rename from InventoryCatalog/Model/LegacyCatalogInventorySynchronization/GetLegacyStockItemsByProductIds.php rename to InventoryCatalog/Model/LegacySynchronization/GetLegacyStockItemsByProductIds.php index 003e1ae81317..f9e8eeb6e725 100644 --- a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/GetLegacyStockItemsByProductIds.php +++ b/InventoryCatalog/Model/LegacySynchronization/GetLegacyStockItemsByProductIds.php @@ -5,12 +5,12 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization; +namespace Magento\InventoryCatalog\Model\LegacySynchronization; use Magento\CatalogInventory\Api\Data\StockItemInterface; use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory; use Magento\CatalogInventory\Api\StockItemRepositoryInterface; -use Magento\CatalogInventory\Model\Stock;; +use Magento\CatalogInventory\Model\Stock; use Magento\Framework\Exception\LocalizedException; /** diff --git a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/IsAsyncLegacyAlignment.php b/InventoryCatalog/Model/LegacySynchronization/IsAsyncLegacyAlignment.php similarity index 91% rename from InventoryCatalog/Model/LegacyCatalogInventorySynchronization/IsAsyncLegacyAlignment.php rename to InventoryCatalog/Model/LegacySynchronization/IsAsyncLegacyAlignment.php index aeef115367c9..3056f3feab0e 100644 --- a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/IsAsyncLegacyAlignment.php +++ b/InventoryCatalog/Model/LegacySynchronization/IsAsyncLegacyAlignment.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization; +namespace Magento\InventoryCatalog\Model\LegacySynchronization; use Magento\Framework\App\Config\ScopeConfigInterface; diff --git a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/SetDataToDestination.php b/InventoryCatalog/Model/LegacySynchronization/SetDataToDestination.php similarity index 81% rename from InventoryCatalog/Model/LegacyCatalogInventorySynchronization/SetDataToDestination.php rename to InventoryCatalog/Model/LegacySynchronization/SetDataToDestination.php index 33b1867f4f0e..5fb53921c04d 100644 --- a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/SetDataToDestination.php +++ b/InventoryCatalog/Model/LegacySynchronization/SetDataToDestination.php @@ -5,10 +5,10 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization; +namespace Magento\InventoryCatalog\Model\LegacySynchronization; -use Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\ToInventory\SetDataToSourceItem; -use Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\ToLegacyCatalogInventory\SetDataToLegacyInventory; +use Magento\InventoryCatalog\Model\LegacySynchronization\ToInventory\SetDataToSourceItem; +use Magento\InventoryCatalog\Model\LegacySynchronization\ToLegacyCatalogInventory\SetDataToLegacyInventory; /** * Set Qty and status for legacy CatalogInventory Stock Item table diff --git a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/Synchronize.php b/InventoryCatalog/Model/LegacySynchronization/Synchronize.php similarity index 98% rename from InventoryCatalog/Model/LegacyCatalogInventorySynchronization/Synchronize.php rename to InventoryCatalog/Model/LegacySynchronization/Synchronize.php index ff77672bb16f..04166ec25381 100644 --- a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/Synchronize.php +++ b/InventoryCatalog/Model/LegacySynchronization/Synchronize.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization; +namespace Magento\InventoryCatalog\Model\LegacySynchronization; use Magento\AsynchronousOperations\Api\Data\OperationInterface; use Magento\AsynchronousOperations\Api\Data\OperationInterfaceFactory; diff --git a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/ToInventory/SetDataToSourceItem.php b/InventoryCatalog/Model/LegacySynchronization/ToInventory/SetDataToSourceItem.php similarity index 91% rename from InventoryCatalog/Model/LegacyCatalogInventorySynchronization/ToInventory/SetDataToSourceItem.php rename to InventoryCatalog/Model/LegacySynchronization/ToInventory/SetDataToSourceItem.php index 72f8c1f37635..3bb02a797b1d 100644 --- a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/ToInventory/SetDataToSourceItem.php +++ b/InventoryCatalog/Model/LegacySynchronization/ToInventory/SetDataToSourceItem.php @@ -5,10 +5,10 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\ToInventory; +namespace Magento\InventoryCatalog\Model\LegacySynchronization\ToInventory; use Magento\Catalog\Model\ResourceModel\Product; -use Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\GetLegacyStockItemsByProductIds; +use Magento\InventoryCatalog\Model\LegacySynchronization\GetLegacyStockItemsByProductIds; use Magento\InventoryCatalog\Model\UpdateSourceItemBasedOnLegacyStockItem; class SetDataToSourceItem diff --git a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/ToLegacyCatalogInventory/SetDataToLegacyInventory.php b/InventoryCatalog/Model/LegacySynchronization/ToLegacyCatalogInventory/SetDataToLegacyInventory.php similarity index 94% rename from InventoryCatalog/Model/LegacyCatalogInventorySynchronization/ToLegacyCatalogInventory/SetDataToLegacyInventory.php rename to InventoryCatalog/Model/LegacySynchronization/ToLegacyCatalogInventory/SetDataToLegacyInventory.php index 4f532eb5ce78..003a47386b2f 100644 --- a/InventoryCatalog/Model/LegacyCatalogInventorySynchronization/ToLegacyCatalogInventory/SetDataToLegacyInventory.php +++ b/InventoryCatalog/Model/LegacySynchronization/ToLegacyCatalogInventory/SetDataToLegacyInventory.php @@ -5,14 +5,14 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\ToLegacyCatalogInventory; +namespace Magento\InventoryCatalog\Model\LegacySynchronization\ToLegacyCatalogInventory; use Magento\Catalog\Model\ResourceModel\Product; use Magento\CatalogInventory\Model\Indexer\Stock\Processor; use Magento\CatalogInventory\Model\Spi\StockStateProviderInterface; use Magento\Framework\Exception\LocalizedException; use Magento\InventoryCatalog\Model\GetDefaultSourceItemsBySkus; -use Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\GetLegacyStockItemsByProductIds; +use Magento\InventoryCatalog\Model\LegacySynchronization\GetLegacyStockItemsByProductIds; use Magento\InventoryCatalog\Model\ResourceModel\SetDataToLegacyStockItem; class SetDataToLegacyInventory diff --git a/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php b/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php index 1a5c7bc26e4f..16eb53e60b32 100644 --- a/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php +++ b/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php @@ -12,14 +12,14 @@ use Magento\CatalogInventory\Model\Indexer\Stock\Processor; use Magento\CatalogInventory\Model\Spi\StockStateProviderInterface; use Magento\Framework\App\ObjectManager; -use Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\Synchronize; +use Magento\InventoryCatalog\Model\LegacySynchronization\Synchronize; use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface; use Magento\InventoryCatalog\Model\ResourceModel\SetDataToLegacyStockItem; /** * Set Qty and status for legacy CatalogInventory Stock Item table * @deprecated - * @see \Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\Synchronize + * @see \Magento\InventoryCatalog\Model\LegacySynchronization\Synchronize */ class SetDataToLegacyCatalogInventory { diff --git a/InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyStockItemSavePlugin.php b/InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyStockItemSavePlugin.php index 606d6e45dd61..1184f25bb614 100755 --- a/InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyStockItemSavePlugin.php +++ b/InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyStockItemSavePlugin.php @@ -13,7 +13,7 @@ use Magento\Framework\App\ResourceConnection; use Magento\Framework\Model\AbstractModel; use Magento\InventoryCatalog\Model\GetDefaultSourceItemBySku; -use Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\Synchronize; +use Magento\InventoryCatalog\Model\LegacySynchronization\Synchronize; use Magento\InventoryCatalogApi\Model\GetProductTypesBySkusInterface; use Magento\InventoryCatalogApi\Model\GetSkusByProductIdsInterface; use Magento\InventoryCatalog\Model\UpdateSourceItemBasedOnLegacyStockItem; diff --git a/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php b/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php index 23f605c6e14f..7764248884ec 100644 --- a/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php +++ b/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php @@ -10,7 +10,7 @@ use Magento\Framework\App\ObjectManager; use Magento\InventoryApi\Api\Data\SourceItemInterface; use Magento\InventoryApi\Api\SourceItemsSaveInterface; -use Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\Synchronize; +use Magento\InventoryCatalog\Model\LegacySynchronization\Synchronize; use Magento\InventoryCatalog\Model\SourceItemsSaveSynchronization\SetDataToLegacyCatalogInventory; use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; use Magento\InventoryCatalogApi\Model\GetProductTypesBySkusInterface; diff --git a/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php b/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php index 898499084330..a5c3561c94ff 100644 --- a/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php +++ b/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php @@ -63,7 +63,6 @@ class SetDataToLegacyStockItemAtSourceItemsSaveTest extends TestCase */ private $consumer; - protected function setUp() { $this->productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); diff --git a/InventoryCatalog/etc/di.xml b/InventoryCatalog/etc/di.xml index 477abfac65e5..4895f47c5bd8 100644 --- a/InventoryCatalog/etc/di.xml +++ b/InventoryCatalog/etc/di.xml @@ -141,7 +141,7 @@ type="Magento\InventoryCatalog\Plugin\CatalogInventory\Model\Spi\StockStateProvider\AdaptVerifyStockToNegativeMinQtyPlugin"/> </type> - <type name="Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\Synchronize"> + <type name="Magento\InventoryCatalog\Model\LegacySynchronization\Synchronize"> <arguments> <argument name="batchSize" xsi:type="number">100</argument> </arguments> diff --git a/InventoryCatalog/etc/queue_consumer.xml b/InventoryCatalog/etc/queue_consumer.xml index 904cbb52f2a9..fb595941850c 100644 --- a/InventoryCatalog/etc/queue_consumer.xml +++ b/InventoryCatalog/etc/queue_consumer.xml @@ -12,5 +12,5 @@ name="legacyCatalogInventorySynchronization" queue="inventory_catalog_product_legacy_inventory_set_data_queue" connection="amqp" - handler="Magento\InventoryCatalog\Model\LegacyCatalogInventorySynchronization\AsyncConsumer::processOperations"/> + handler="Magento\InventoryCatalog\Model\LegacySynchronization\AsyncConsumer::processOperations"/> </config> \ No newline at end of file From e1bbd906006d93cbb1eef617840b5a51f481dca4 Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Wed, 13 Mar 2019 15:31:06 +0100 Subject: [PATCH 029/231] FIX r-w-w bug due to asynchronous race condition with DB transaction --- .../LegacySynchronization/AsyncConsumer.php | 2 +- .../SetDataToDestination.php | 8 +- .../LegacySynchronization/Synchronize.php | 24 +++--- .../ToInventory/SetDataToSourceItem.php | 35 +++------ .../SetDataToLegacyInventory.php | 73 ++++++++++--------- ...eSourceItemAtLegacyStockItemSavePlugin.php | 4 +- ...atalogInventoryAtSourceItemsSavePlugin.php | 6 +- 7 files changed, 73 insertions(+), 79 deletions(-) diff --git a/InventoryCatalog/Model/LegacySynchronization/AsyncConsumer.php b/InventoryCatalog/Model/LegacySynchronization/AsyncConsumer.php index 029aef5c7d28..93267067ce98 100644 --- a/InventoryCatalog/Model/LegacySynchronization/AsyncConsumer.php +++ b/InventoryCatalog/Model/LegacySynchronization/AsyncConsumer.php @@ -46,6 +46,6 @@ public function __construct( public function processOperations(OperationInterface $operation): void { $data = $this->serializer->unserialize($operation->getSerializedData()); - $this->setDataToDestination->execute($data['direction'], $data['skus']); + $this->setDataToDestination->execute($data['direction'], $data['items']); } } diff --git a/InventoryCatalog/Model/LegacySynchronization/SetDataToDestination.php b/InventoryCatalog/Model/LegacySynchronization/SetDataToDestination.php index 5fb53921c04d..e2fa0da28ba4 100644 --- a/InventoryCatalog/Model/LegacySynchronization/SetDataToDestination.php +++ b/InventoryCatalog/Model/LegacySynchronization/SetDataToDestination.php @@ -41,15 +41,15 @@ public function __construct( /** * @param string $direction - * @param array $skus + * @param array $items * @throws \Magento\Framework\Exception\LocalizedException */ - public function execute(string $direction, array $skus): void + public function execute(string $direction, array $items): void { if ($direction === Synchronize::DIRECTION_TO_LEGACY) { - $this->setDataToLegacyInventory->execute($skus); + $this->setDataToLegacyInventory->execute($items); } else { - $this->setDataToSourceItem->execute($skus); + $this->setDataToSourceItem->execute($items); } } } diff --git a/InventoryCatalog/Model/LegacySynchronization/Synchronize.php b/InventoryCatalog/Model/LegacySynchronization/Synchronize.php index 04166ec25381..f3b3fd9d5862 100644 --- a/InventoryCatalog/Model/LegacySynchronization/Synchronize.php +++ b/InventoryCatalog/Model/LegacySynchronization/Synchronize.php @@ -88,15 +88,15 @@ public function __construct( /** * @param string $direction - * @param array $skus + * @param array $items */ - private function executeAsync(string $direction, array $skus): void + private function executeAsync(string $direction, array $items): void { $operations = []; $bulkUuid = $this->identityService->generateId(); - $chunks = array_chunk($skus, $this->batchSize); + $chunks = array_chunk($items, $this->batchSize); foreach ($chunks as $chunk) { $data = [ 'data' => [ @@ -105,7 +105,7 @@ private function executeAsync(string $direction, array $skus): void 'serialized_data' => $this->serializer->serialize( [ 'direction' => $direction, - 'skus' => $chunk + 'items' => $chunk ] ), 'status' => OperationInterface::STATUS_TYPE_OPEN, @@ -117,31 +117,31 @@ private function executeAsync(string $direction, array $skus): void $operations[] = $operation; } - $this->bulkManagement->scheduleBulk($bulkUuid, $operations, __('Set legacy stock data')); + $this->bulkManagement->scheduleBulk($bulkUuid, $operations, __('Synchronize legacy stock')); } /** * @param string $direction - * @param array $skus + * @param array $items * @throws \Magento\Framework\Exception\LocalizedException */ - private function executeSync(string $direction, array $skus): void + private function executeSync(string $direction, array $items): void { - $this->setDataToDestination->execute($direction, $skus); + $this->setDataToDestination->execute($direction, $items); } /** * @param string $direction - * @param array $skus + * @param array $items * @return void * @throws \Magento\Framework\Exception\LocalizedException */ - public function execute(string $direction, array $skus): void + public function execute(string $direction, array $items): void { if ($this->isAsyncLegacyAlignment->execute()) { - $this->executeAsync($direction, $skus); + $this->executeAsync($direction, $items); } else { - $this->executeSync($direction, $skus); + $this->executeSync($direction, $items); } } } diff --git a/InventoryCatalog/Model/LegacySynchronization/ToInventory/SetDataToSourceItem.php b/InventoryCatalog/Model/LegacySynchronization/ToInventory/SetDataToSourceItem.php index 3bb02a797b1d..bc63ba4cf26a 100644 --- a/InventoryCatalog/Model/LegacySynchronization/ToInventory/SetDataToSourceItem.php +++ b/InventoryCatalog/Model/LegacySynchronization/ToInventory/SetDataToSourceItem.php @@ -7,8 +7,7 @@ namespace Magento\InventoryCatalog\Model\LegacySynchronization\ToInventory; -use Magento\Catalog\Model\ResourceModel\Product; -use Magento\InventoryCatalog\Model\LegacySynchronization\GetLegacyStockItemsByProductIds; +use Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory; use Magento\InventoryCatalog\Model\UpdateSourceItemBasedOnLegacyStockItem; class SetDataToSourceItem @@ -19,48 +18,36 @@ class SetDataToSourceItem private $updateSourceItemBasedOnLegacyStockItem; /** - * @var GetLegacyStockItemsByProductIds + * @var StockItemInterfaceFactory */ - private $getLegacyStockItemsByProductIds; - - /** - * @var Product - */ - private $productResource; + private $stockItemInterfaceFactory; /** * SetDataToSourceItem constructor. * @param UpdateSourceItemBasedOnLegacyStockItem $updateSourceItemBasedOnLegacyStockItem - * @param GetLegacyStockItemsByProductIds $getLegacyStockItemsByProductIds - * @param Product $productResource + * @param StockItemInterfaceFactory $stockItemInterfaceFactory * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( UpdateSourceItemBasedOnLegacyStockItem $updateSourceItemBasedOnLegacyStockItem, - GetLegacyStockItemsByProductIds $getLegacyStockItemsByProductIds, - Product $productResource + StockItemInterfaceFactory $stockItemInterfaceFactory ) { $this->updateSourceItemBasedOnLegacyStockItem = $updateSourceItemBasedOnLegacyStockItem; - $this->productResource = $productResource; - $this->getLegacyStockItemsByProductIds = $getLegacyStockItemsByProductIds; + $this->stockItemInterfaceFactory = $stockItemInterfaceFactory; } /** - * @param array $skus + * @param array $legacyItemsData * @throws \Magento\Framework\Exception\CouldNotSaveException * @throws \Magento\Framework\Exception\InputException * @throws \Magento\Framework\Exception\LocalizedException * @throws \Magento\Framework\Validation\ValidationException */ - public function execute(array $skus): void + public function execute(array $legacyItemsData): void { - $productIds = $this->productResource->getProductsIdsBySkus($skus); - if (!empty($productIds)) { - $stockItems = $this->getLegacyStockItemsByProductIds->execute($productIds); - - foreach ($stockItems as $stockItem) { - $this->updateSourceItemBasedOnLegacyStockItem->execute($stockItem); - } + foreach ($legacyItemsData as $legacyItemData) { + $stockItem = $this->stockItemInterfaceFactory->create(['data' => $legacyItemData]); + $this->updateSourceItemBasedOnLegacyStockItem->execute($stockItem); } } } diff --git a/InventoryCatalog/Model/LegacySynchronization/ToLegacyCatalogInventory/SetDataToLegacyInventory.php b/InventoryCatalog/Model/LegacySynchronization/ToLegacyCatalogInventory/SetDataToLegacyInventory.php index 003a47386b2f..c2b58f243029 100644 --- a/InventoryCatalog/Model/LegacySynchronization/ToLegacyCatalogInventory/SetDataToLegacyInventory.php +++ b/InventoryCatalog/Model/LegacySynchronization/ToLegacyCatalogInventory/SetDataToLegacyInventory.php @@ -7,13 +7,15 @@ namespace Magento\InventoryCatalog\Model\LegacySynchronization\ToLegacyCatalogInventory; -use Magento\Catalog\Model\ResourceModel\Product; use Magento\CatalogInventory\Model\Indexer\Stock\Processor; use Magento\CatalogInventory\Model\Spi\StockStateProviderInterface; use Magento\Framework\Exception\LocalizedException; -use Magento\InventoryCatalog\Model\GetDefaultSourceItemsBySkus; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryCatalog\Model\GetProductIdsBySkus; use Magento\InventoryCatalog\Model\LegacySynchronization\GetLegacyStockItemsByProductIds; use Magento\InventoryCatalog\Model\ResourceModel\SetDataToLegacyStockItem; +use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; class SetDataToLegacyInventory { @@ -22,16 +24,6 @@ class SetDataToLegacyInventory */ private $setDataToLegacyStockItem; - /** - * @var Product - */ - private $productResourceModel; - - /** - * @var GetDefaultSourceItemsBySkus - */ - private $getDefaultSourceItemsBySkus; - /** * @var GetLegacyStockItemsByProductIds */ @@ -47,63 +39,76 @@ class SetDataToLegacyInventory */ private $indexerProcessor; + /** + * @var GetProductIdsBySkus + */ + private $getProductIdsBySkus; + + /** + * @var DefaultSourceProviderInterface + */ + private $defaultSourceProvider; + /** * SetDataToLegacyCatalogInventory constructor. - * @param GetDefaultSourceItemsBySkus $getDefaultSourceItemsBySkus + * @param DefaultSourceProviderInterface $defaultSourceProvider * @param SetDataToLegacyStockItem $setDataToLegacyStockItem * @param GetLegacyStockItemsByProductIds $getLegacyStockItemsByProductIds * @param StockStateProviderInterface $stockStateProvider * @param Processor $indexerProcessor - * @param Product $productResourceModel + * @param GetProductIdsBySkus $getProductIdsBySkus * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( - GetDefaultSourceItemsBySkus $getDefaultSourceItemsBySkus, + DefaultSourceProviderInterface $defaultSourceProvider, SetDataToLegacyStockItem $setDataToLegacyStockItem, GetLegacyStockItemsByProductIds $getLegacyStockItemsByProductIds, StockStateProviderInterface $stockStateProvider, Processor $indexerProcessor, - Product $productResourceModel + GetProductIdsBySkus $getProductIdsBySkus ) { $this->setDataToLegacyStockItem = $setDataToLegacyStockItem; - $this->productResourceModel = $productResourceModel; - $this->getDefaultSourceItemsBySkus = $getDefaultSourceItemsBySkus; $this->getLegacyStockItemsByProductIds = $getLegacyStockItemsByProductIds; $this->stockStateProvider = $stockStateProvider; $this->indexerProcessor = $indexerProcessor; + $this->getProductIdsBySkus = $getProductIdsBySkus; + $this->defaultSourceProvider = $defaultSourceProvider; } /** * Synchronously execute legacy alignment * - * @param array $skus + * @param array $sourceItemsData * @throws LocalizedException */ - public function execute(array $skus): void + public function execute(array $sourceItemsData): void { - $sourceItems = $this->getDefaultSourceItemsBySkus->execute($skus); - $productIds = $this->productResourceModel->getProductsIdsBySkus($skus); - $legacyStockItems = $this->getLegacyStockItemsByProductIds->execute($productIds); + $productIds = []; + foreach ($sourceItemsData as $sourceItemData) { + $sku = (string) $sourceItemData[SourceItemInterface::SKU]; - foreach ($sourceItems as $sourceItem) { - $sku = $sourceItem->getSku(); - - if (!isset($productIds[$sku])) { - continue; // This product does not exist anymore + if ($sourceItemData[SourceItemInterface::SOURCE_CODE] !== $this->defaultSourceProvider->getCode()) { + throw new LocalizedException(__('Only default source can synchronize legacy stock')); } - $productId = (int) $productIds[$sku]; + try { + $productId = (int) current($this->getProductIdsBySkus->execute([$sku])); + } catch (NoSuchEntityException $e) { + continue; + } + $legacyStockItems = $this->getLegacyStockItemsByProductIds->execute([$productId]); if (!isset($legacyStockItems[$productId])) { continue; } $legacyStockItem = $legacyStockItems[$productId]; - $isInStock = (int) $sourceItem->getStatus(); + $isInStock = (int) $sourceItemData[SourceItemInterface::STATUS]; + $quantity = (float) $sourceItemData[SourceItemInterface::QUANTITY]; if ($legacyStockItem->getManageStock()) { $legacyStockItem->setIsInStock($isInStock); - $legacyStockItem->setQty((float)$sourceItem->getQuantity()); + $legacyStockItem->setQty($quantity); if (false === $this->stockStateProvider->verifyStock($legacyStockItem)) { $isInStock = 0; @@ -111,10 +116,12 @@ public function execute(array $skus): void } $this->setDataToLegacyStockItem->execute( - (string) $sourceItem->getSku(), - (float) $sourceItem->getQuantity(), + $sku, + $quantity, $isInStock ); + + $productIds[] = $productId; } if (!empty($productIds)) { diff --git a/InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyStockItemSavePlugin.php b/InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyStockItemSavePlugin.php index 1184f25bb614..b0e9456c9bef 100755 --- a/InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyStockItemSavePlugin.php +++ b/InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyStockItemSavePlugin.php @@ -62,7 +62,7 @@ class UpdateSourceItemAtLegacyStockItemSavePlugin * @param GetProductTypesBySkusInterface $getProductTypeBySku * @param GetSkusByProductIdsInterface $getSkusByProductIds * @param GetDefaultSourceItemBySku $getDefaultSourceItemBySku - * @param Synchronize|null $synchronize + * @param Synchronize $synchronize */ public function __construct( UpdateSourceItemBasedOnLegacyStockItem $updateSourceItemBasedOnLegacyStockItem, @@ -105,7 +105,7 @@ public function aroundSave(ItemResourceModel $subject, callable $proceed, Abstra $this->synchronize->execute( Synchronize::DIRECTION_TO_INVENTORY, [ - $this->getProductSkuById((int) $legacyStockItem->getProductId()) + $legacyStockItem->getData() ] ); } diff --git a/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php b/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php index 7764248884ec..2a5126d67fa3 100644 --- a/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php +++ b/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php @@ -73,7 +73,7 @@ public function __construct( */ public function afterExecute(SourceItemsSaveInterface $subject, $result, array $sourceItems): void { - $skuToSynchronize = []; + $sourceItemsData = []; foreach ($sourceItems as $sourceItem) { if ($sourceItem->getSourceCode() !== $this->defaultSourceProvider->getCode()) { continue; @@ -92,9 +92,9 @@ public function afterExecute(SourceItemsSaveInterface $subject, $result, array $ continue; } - $skuToSynchronize[] = $sourceItem->getSku(); + $sourceItemsData[] = $sourceItem->getData(); } - $this->synchronize->execute(Synchronize::DIRECTION_TO_LEGACY, $skuToSynchronize); + $this->synchronize->execute(Synchronize::DIRECTION_TO_LEGACY, $sourceItemsData); } } From e530a40dd388c3aeccb5b81e2aad9f01f5fc744b Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Thu, 14 Mar 2019 14:28:40 +0100 Subject: [PATCH 030/231] Missing dependency --- InventoryCatalog/composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/InventoryCatalog/composer.json b/InventoryCatalog/composer.json index fa94a574e4d4..2d79e1a915f1 100644 --- a/InventoryCatalog/composer.json +++ b/InventoryCatalog/composer.json @@ -13,7 +13,8 @@ "magento/module-inventory-configuration-api": "*", "magento/module-inventory-indexer": "*", "magento/module-inventory-sales-api": "*", - "magento/module-inventory-configuration": "*" + "magento/module-inventory-configuration": "*", + "magento/module-asynchronous-operations": "*" }, "suggest": { "magento/module-inventory-reservations-api": "*" From 10b8cd650250ae612872705b15d766f0f6848225 Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Thu, 14 Mar 2019 14:32:52 +0100 Subject: [PATCH 031/231] Wrong URN for queue definition files --- InventoryCatalog/etc/queue_consumer.xml | 2 +- InventoryCatalog/etc/queue_publisher.xml | 2 +- InventoryCatalog/etc/queue_topology.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/InventoryCatalog/etc/queue_consumer.xml b/InventoryCatalog/etc/queue_consumer.xml index fb595941850c..9bb7fe0ece0c 100644 --- a/InventoryCatalog/etc/queue_consumer.xml +++ b/InventoryCatalog/etc/queue_consumer.xml @@ -7,7 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:framework-message-queue:etc/queue_consumer.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:framework-message-queue:etc/consumer.xsd"> <consumer name="legacyCatalogInventorySynchronization" queue="inventory_catalog_product_legacy_inventory_set_data_queue" diff --git a/InventoryCatalog/etc/queue_publisher.xml b/InventoryCatalog/etc/queue_publisher.xml index 61e96de6dbfe..e6160b54ee5e 100644 --- a/InventoryCatalog/etc/queue_publisher.xml +++ b/InventoryCatalog/etc/queue_publisher.xml @@ -7,7 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:framework-message-queue:etc/queue_publisher.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:framework-message-queue:etc/publisher.xsd"> <publisher topic="inventory.catalog.product.legacy_inventory.set_data"> <connection name="amqp" exchange="inventory.catalog.product.legacy_inventory.set_data.exchange" /> </publisher> diff --git a/InventoryCatalog/etc/queue_topology.xml b/InventoryCatalog/etc/queue_topology.xml index d252b7a82b88..6d77d6950d06 100644 --- a/InventoryCatalog/etc/queue_topology.xml +++ b/InventoryCatalog/etc/queue_topology.xml @@ -7,7 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:framework-message-queue:etc/queue_topology.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:framework-message-queue:etc/topology.xsd"> <exchange name="inventory.catalog.product.legacy_inventory.set_data.exchange" type="topic" connection="amqp"> <binding From 484eb82d1515cf6a1b59f55d8a81c5460966a85f Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Sat, 16 Mar 2019 15:14:17 +0200 Subject: [PATCH 032/231] MSI-1912: added ability to ship items without stock managing --- InventoryShippingAdminUi/Model/ShipmentProvider.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/InventoryShippingAdminUi/Model/ShipmentProvider.php b/InventoryShippingAdminUi/Model/ShipmentProvider.php index ff4140923ccc..9416dcc17f91 100644 --- a/InventoryShippingAdminUi/Model/ShipmentProvider.php +++ b/InventoryShippingAdminUi/Model/ShipmentProvider.php @@ -36,7 +36,9 @@ public function getShipmentData(): array $shipmentItems = []; foreach ($items as $item) { + $orderItemId = $item['orderItemId']; if (empty($item['sources'])) { + $shipmentItems['items'][$orderItemId] = $item['qtyToShip']; continue; } $orderItemId = $item['orderItemId']; From c5c208996d8c72315624db859013641e8486f4a2 Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Sat, 16 Mar 2019 16:11:25 +0100 Subject: [PATCH 033/231] Performance rework for MSI#1990 - Improve performance of Sources sorting in SSA Source items are extracted and computed in a single query and cycle instead of nested cycles and multiple queries --- ...ilableSourceItemsBySkusAndSortedSource.php | 57 ++++++ ...leSourceItemsDataBySkusAndSortedSource.php | 53 ++++++ ...etSourceItemsBySkusAndSortedSourceTest.php | 76 ++++++++ Inventory/etc/di.xml | 1 + ...rceItemsBySkusAndSortedSourceInterface.php | 27 +++ .../Result/GetDefaultSortedSourcesResult.php | 79 ++++---- .../GetDefaultSortedSourcesResultTest.php | 171 ++++++++++++++++++ 7 files changed, 428 insertions(+), 36 deletions(-) create mode 100644 Inventory/Model/GetAvailableSourceItemsBySkusAndSortedSource.php create mode 100644 Inventory/Model/ResourceModel/GetAvailableSourceItemsDataBySkusAndSortedSource.php create mode 100644 Inventory/Test/Integration/GetSourceItemsBySkusAndSortedSourceTest.php create mode 100644 InventoryApi/Api/GetAvailableSourceItemsBySkusAndSortedSourceInterface.php create mode 100644 InventorySourceSelectionApi/Test/Integration/GetDefaultSortedSourcesResultTest.php diff --git a/Inventory/Model/GetAvailableSourceItemsBySkusAndSortedSource.php b/Inventory/Model/GetAvailableSourceItemsBySkusAndSortedSource.php new file mode 100644 index 000000000000..c8b698669bb8 --- /dev/null +++ b/Inventory/Model/GetAvailableSourceItemsBySkusAndSortedSource.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Inventory\Model; + +use Magento\Inventory\Model\ResourceModel\GetAvailableSourceItemsDataBySkusAndSortedSource; +use Magento\InventoryApi\Api\Data\SourceItemInterfaceFactory; +use Magento\InventoryApi\Api\GetAvailableSourceItemsBySkusAndSortedSourceInterface; + +/** + * @inheritdoc + */ +class GetAvailableSourceItemsBySkusAndSortedSource implements GetAvailableSourceItemsBySkusAndSortedSourceInterface +{ + /** + * @var GetAvailableSourceItemsDataBySkusAndSortedSource + */ + private $getSourceItemsDataBySkusAndSortedSource; + + /** + * @var SourceItemInterfaceFactory + */ + private $sourceItemInterfaceFactory; + + /** + * GetSourceItemsBySkusAndSortedSource constructor. + * @param GetAvailableSourceItemsDataBySkusAndSortedSource $getSourceItemsDataBySkusAndSortedSource + * @param SourceItemInterfaceFactory $sourceItemInterfaceFactory + * @SuppressWarnings(PHPMD.LongVariable) + */ + public function __construct( + GetAvailableSourceItemsDataBySkusAndSortedSource $getSourceItemsDataBySkusAndSortedSource, + SourceItemInterfaceFactory $sourceItemInterfaceFactory + ) { + $this->getSourceItemsDataBySkusAndSortedSource = $getSourceItemsDataBySkusAndSortedSource; + $this->sourceItemInterfaceFactory = $sourceItemInterfaceFactory; + } + + /** + * @inheritdoc + */ + public function execute(array $skus, array $sortedSourceCodes): array + { + $res = []; + $sourceItemsData = $this->getSourceItemsDataBySkusAndSortedSource->execute($skus, $sortedSourceCodes); + + foreach ($sourceItemsData as $sourceItemData) { + $res[] = $this->sourceItemInterfaceFactory->create(['data' => $sourceItemData]); + } + + return $res; + } +} diff --git a/Inventory/Model/ResourceModel/GetAvailableSourceItemsDataBySkusAndSortedSource.php b/Inventory/Model/ResourceModel/GetAvailableSourceItemsDataBySkusAndSortedSource.php new file mode 100644 index 000000000000..dbf0fe2b1eb6 --- /dev/null +++ b/Inventory/Model/ResourceModel/GetAvailableSourceItemsDataBySkusAndSortedSource.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Inventory\Model\ResourceModel; + +use Magento\Framework\App\ResourceConnection; +use Magento\InventoryApi\Api\Data\SourceItemInterface; + +/** + * Retrieve source items for a defined set of skus and sorted source codes + */ +class GetAvailableSourceItemsDataBySkusAndSortedSource +{ + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @param ResourceConnection $resourceConnection + */ + public function __construct( + ResourceConnection $resourceConnection + ) { + $this->resourceConnection = $resourceConnection; + } + + /** + * @param array $skus + * @param array $sourceCodes + * @return array[] + */ + public function execute(array $skus, array $sourceCodes): array + { + $tableName = $this->resourceConnection->getTableName(SourceItem::TABLE_NAME_SOURCE_ITEM); + $connection = $this->resourceConnection->getConnection(); + + $qry = $connection + ->select() + ->from($tableName) + ->where(SourceItemInterface::SKU . ' IN (?)', $skus) + ->where(SourceItemInterface::SOURCE_CODE . ' IN (?)', $sourceCodes) + ->where(SourceItemInterface::QUANTITY . ' > 0') + ->where(SourceItemInterface::STATUS . ' = ?', SourceItemInterface::STATUS_IN_STOCK) + ->order($connection->quoteInto('FIELD(' . SourceItemInterface::SOURCE_CODE . ', ?)', $sourceCodes)); + + return $connection->fetchAll($qry) ?? []; + } +} diff --git a/Inventory/Test/Integration/GetSourceItemsBySkusAndSortedSourceTest.php b/Inventory/Test/Integration/GetSourceItemsBySkusAndSortedSourceTest.php new file mode 100644 index 000000000000..af6cb1b403df --- /dev/null +++ b/Inventory/Test/Integration/GetSourceItemsBySkusAndSortedSourceTest.php @@ -0,0 +1,76 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Inventory\Test\Integration; + +use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryApi\Api\GetAvailableSourceItemsBySkusAndSortedSourceInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +class GetSourceItemsBySkusAndSortedSourceTest extends TestCase +{ + /** + * @var GetAvailableSourceItemsBySkusAndSortedSourceInterface + */ + private $subject; + + protected function setUp() + { + parent::setUp(); + + $this->subject = Bootstrap::getObjectManager()->get(GetAvailableSourceItemsBySkusAndSortedSourceInterface::class); + } + + /** + * @return array + */ + public function shouldReturnSortedSourceItemsDataProvider(): array + { + return [ + [ + ['SKU-1', 'SKU-2', 'SKU-3'], + ['eu-1', 'eu-2', 'eu-3'], + [ + 'eu-1/SKU-1' => 5.5, + 'eu-2/SKU-1' => 3.0, + ] + ], + [ + ['SKU-1', 'SKU-2', 'SKU-3'], + ['eu-3', 'eu-2', 'eu-1'], + [ + 'eu-2/SKU-1' => 3.0, + 'eu-1/SKU-1' => 5.5, + ] + ] + ]; + } + + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stocks.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stock_source_links.php + * @dataProvider shouldReturnSortedSourceItemsDataProvider + * @param array $skus + * @param array $sortedSourceCodes + * @param array $expected + */ + public function testShouldReturnSortedSourceItems(array $skus, array $sortedSourceCodes, array $expected): void + { + $sourceItems = $this->subject->execute($skus, $sortedSourceCodes); + + self::assertCount(count($expected), $sourceItems); + + foreach ($sourceItems as $sourceItem) { + $key = $sourceItem->getSourceCode() . '/' . $sourceItem->getSku(); + self::assertSame($expected[$key], $sourceItem->getQuantity()); + self::assertSame(SourceItemInterface::STATUS_IN_STOCK, $sourceItem->getStatus()); + } + } +} diff --git a/Inventory/etc/di.xml b/Inventory/etc/di.xml index 889a57da39d2..71d238e5e5d5 100644 --- a/Inventory/etc/di.xml +++ b/Inventory/etc/di.xml @@ -90,4 +90,5 @@ </arguments> </type> <preference for="Magento\InventoryApi\Model\IsProductAssignedToStockInterface" type="Magento\Inventory\Model\ResourceModel\IsProductAssignedToStock"/> + <preference for="Magento\InventoryApi\Api\GetAvailableSourceItemsBySkusAndSortedSourceInterface" type="Magento\Inventory\Model\GetAvailableSourceItemsBySkusAndSortedSource" /> </config> diff --git a/InventoryApi/Api/GetAvailableSourceItemsBySkusAndSortedSourceInterface.php b/InventoryApi/Api/GetAvailableSourceItemsBySkusAndSortedSourceInterface.php new file mode 100644 index 000000000000..f48356d448c8 --- /dev/null +++ b/InventoryApi/Api/GetAvailableSourceItemsBySkusAndSortedSourceInterface.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryApi\Api; + +/** + * Retrieve source items for a defined set of skus and sorted source codes + * + * Useful for determining availability and source selection + * + * @api + */ +interface GetAvailableSourceItemsBySkusAndSortedSourceInterface +{ + /** + * Get Source items assigned toa set of sorted source codes and return respecting the source codes order + * + * @param array $skus + * @param array $sortedSourceCodes + * @return \Magento\InventoryApi\Api\Data\SourceItemInterface[] + */ + public function execute(array $skus, array $sortedSourceCodes): array; +} diff --git a/InventorySourceSelectionApi/Model/Algorithms/Result/GetDefaultSortedSourcesResult.php b/InventorySourceSelectionApi/Model/Algorithms/Result/GetDefaultSortedSourcesResult.php index 8d1d80a51587..527d5e659a10 100644 --- a/InventorySourceSelectionApi/Model/Algorithms/Result/GetDefaultSortedSourcesResult.php +++ b/InventorySourceSelectionApi/Model/Algorithms/Result/GetDefaultSortedSourcesResult.php @@ -7,8 +7,10 @@ namespace Magento\InventorySourceSelectionApi\Model\Algorithms\Result; +use Magento\Framework\App\ObjectManager; use Magento\InventoryApi\Api\Data\SourceInterface; use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryApi\Api\GetAvailableSourceItemsBySkusAndSortedSourceInterface; use Magento\InventoryApi\Api\SourceItemRepositoryInterface; use Magento\InventorySourceSelectionApi\Api\Data\InventoryRequestInterface; use Magento\InventorySourceSelectionApi\Api\Data\SourceSelectionResultInterface; @@ -41,6 +43,11 @@ class GetDefaultSortedSourcesResult */ private $sourceItemRepository; + /** + * @var GetAvailableSourceItemsBySkusAndSortedSourceInterface + */ + private $getAvailableSourceItemsBySkusAndSortedSource; + /** * GetDefaultSortedSourcesResult constructor. * @@ -48,17 +55,21 @@ class GetDefaultSortedSourcesResult * @param SourceSelectionResultInterfaceFactory $sourceSelectionResultFactory * @param SearchCriteriaBuilder $searchCriteriaBuilder * @param SourceItemRepositoryInterface $sourceItemRepository + * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( SourceSelectionItemInterfaceFactory $sourceSelectionItemFactory, SourceSelectionResultInterfaceFactory $sourceSelectionResultFactory, SearchCriteriaBuilder $searchCriteriaBuilder, - SourceItemRepositoryInterface $sourceItemRepository + SourceItemRepositoryInterface $sourceItemRepository, + GetAvailableSourceItemsBySkusAndSortedSourceInterface $getAvailableSourceItemsBySkusAndSortedSource = null ) { $this->sourceSelectionItemFactory = $sourceSelectionItemFactory; $this->sourceSelectionResultFactory = $sourceSelectionResultFactory; $this->searchCriteriaBuilder = $searchCriteriaBuilder; $this->sourceItemRepository = $sourceItemRepository; + $this->getAvailableSourceItemsBySkusAndSortedSource = $getAvailableSourceItemsBySkusAndSortedSource ?: + ObjectManager::getInstance()->get(GetAvailableSourceItemsBySkusAndSortedSourceInterface::class); } /** @@ -105,44 +116,40 @@ public function execute( $isShippable = true; $sourceItemSelections = []; - //TODO from performance perspective it's better to switch these foreaches and make the inner one - //TODO which loops over sources to be outermost and iterate over inventory request items inside + $itemsTdDeliver = []; foreach ($inventoryRequest->getItems() as $item) { - $itemSku = $item->getSku(); - $qtyToDeliver = $item->getQty(); - - foreach ($sortedSources as $source) { - $sourceItem = $this->getSourceItemBySourceCodeAndSku($source->getSourceCode(), $itemSku); - if (null === $sourceItem) { - continue; - } - - if ($sourceItem->getStatus() !== SourceItemInterface::STATUS_IN_STOCK) { - continue; - } - - $sourceItemQty = $sourceItem->getQuantity(); - $qtyToDeduct = min($sourceItemQty, $qtyToDeliver); - - // check if source has some qty of SKU, so it's possible to take them into account - if ($this->isZero((float)$sourceItemQty)) { - continue; - } - - $sourceItemSelections[] = $this->sourceSelectionItemFactory->create([ - 'sourceCode' => $sourceItem->getSourceCode(), - 'sku' => $itemSku, - 'qtyToDeduct' => $qtyToDeduct, - 'qtyAvailable' => $sourceItemQty - ]); - - $qtyToDeliver -= $qtyToDeduct; - } + $itemsTdDeliver[$item->getSku()] = (float) $item->getQty(); + } + + $sortedSourceCodes = []; + foreach ($sortedSources as $sortedSource) { + $sortedSourceCodes[] = $sortedSource->getSourceCode(); + } - // if we go through all sources from the stock and there is still some qty to delivery, - // then it doesn't have enough items to delivery - if (!$this->isZero($qtyToDeliver)) { + $sourceItems = + $this->getAvailableSourceItemsBySkusAndSortedSource->execute( + array_keys($itemsTdDeliver), + $sortedSourceCodes + ); + + foreach ($sourceItems as $sourceItem) { + $qtyToDeduct = min($sourceItem->getQuantity(), $itemsTdDeliver[$sourceItem->getSku()] ?? 0.0); + + $sourceItemSelections[] = $this->sourceSelectionItemFactory->create([ + 'sourceCode' => $sourceItem->getSourceCode(), + 'sku' => $sourceItem->getSku(), + 'qtyToDeduct' => $qtyToDeduct, + 'qtyAvailable' => $sourceItem->getQuantity() + ]); + + $itemsTdDeliver[$sourceItem->getSku()] -= $qtyToDeduct; + } + + $isShippable = true; + foreach ($itemsTdDeliver as $itemToDeliver) { + if (!$this->isZero($itemToDeliver)) { $isShippable = false; + break; } } diff --git a/InventorySourceSelectionApi/Test/Integration/GetDefaultSortedSourcesResultTest.php b/InventorySourceSelectionApi/Test/Integration/GetDefaultSortedSourcesResultTest.php new file mode 100644 index 000000000000..5b4469a07f9e --- /dev/null +++ b/InventorySourceSelectionApi/Test/Integration/GetDefaultSortedSourcesResultTest.php @@ -0,0 +1,171 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventorySourceSelectionApi\Test\Integration; + +use Magento\InventoryApi\Api\SourceRepositoryInterface; +use Magento\InventorySourceSelectionApi\Api\Data\InventoryRequestInterfaceFactory; +use Magento\InventorySourceSelectionApi\Api\Data\ItemRequestInterfaceFactory; +use Magento\InventorySourceSelectionApi\Model\Algorithms\Result\GetDefaultSortedSourcesResult; +use PHPUnit\Framework\TestCase; +use Magento\TestFramework\Helper\Bootstrap; + +class GetDefaultSortedSourcesResultTest extends TestCase +{ + /** + * @var GetDefaultSortedSourcesResult + */ + private $subject; + + /** + * @var InventoryRequestInterfaceFactory + */ + private $inventoryRequestFactory; + + /** + * @var ItemRequestInterfaceFactory + */ + private $itemRequestFactory; + + /** + * @var SourceRepositoryInterface + */ + private $sourceRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->subject = Bootstrap::getObjectManager()->get(GetDefaultSortedSourcesResult::class); + $this->inventoryRequestFactory = Bootstrap::getObjectManager()->get(InventoryRequestInterfaceFactory::class); + $this->itemRequestFactory = Bootstrap::getObjectManager()->get(ItemRequestInterfaceFactory::class); + $this->sourceRepository = Bootstrap::getObjectManager()->get(SourceRepositoryInterface::class); + } + + /** + * @return array + */ + public function shouldReturnDefaultResultsDataProvider(): array + { + return [ + [ + 10, + [ + ['sku' => 'SKU-1', 'qty' => 7], + ], + [ + 'eu-1', + 'eu-2', + 'eu-3', + ], + [ + 'eu-1/SKU-1' => ['deduct' => 5.5, 'avail' => 5.5], + 'eu-2/SKU-1' => ['deduct' => 1.5, 'avail' => 3], + ], + true + ], + [ + 10, + [ + ['sku' => 'SKU-1', 'qty' => 15], + ], + [ + 'eu-1', + 'eu-2', + 'eu-3', + ], + [ + 'eu-1/SKU-1' => ['deduct' => 5.5, 'avail' => 5.5], + 'eu-2/SKU-1' => ['deduct' => 3, 'avail' => 3], + ], + false + ], + [ + 10, + [ + ['sku' => 'SKU-1', 'qty' => 5], + ['sku' => 'SKU-2', 'qty' => 3], + ], + [ + 'eu-1', + 'eu-2', + 'eu-3', + ], + [ + 'eu-1/SKU-1' => ['deduct' => 5, 'avail' => 5.5], + 'eu-2/SKU-1' => ['deduct' => 0, 'avail' => 3], + ], + false + ], + [ + 20, + [ + ['sku' => 'SKU-2', 'qty' => 3], + ], + [ + 'us-1', + ], + [ + 'us-1/SKU-2' => ['deduct' => 3, 'avail' => 5], + ], + true + ], + ]; + } + + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stocks.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stock_source_links.php + * @dataProvider shouldReturnDefaultResultsDataProvider + * @param int $stockId + * @param array $requestItemsData + * @param array $sortedSourcesCodes + * @param array $expected + * @param bool $expectIsShippable + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function testShouldReturnDefaultResults( + int $stockId, + array $requestItemsData, + array $sortedSourcesCodes, + array $expected, + bool $expectIsShippable + ): void { + $requestItems = []; + foreach ($requestItemsData as $requestItemData) { + $requestItems[] = $this->itemRequestFactory->create($requestItemData); + } + + $inventoryRequest = $this->inventoryRequestFactory->create([ + 'stockId' => $stockId, + 'items' => $requestItems + ]); + + $sortedSources = []; + foreach ($sortedSourcesCodes as $sortedSourceCode) { + $sortedSources[] = $this->sourceRepository->get($sortedSourceCode); + } + + $res = $this->subject->execute( + $inventoryRequest, + $sortedSources + ); + + $sourceSelectionItems = $res->getSourceSelectionItems(); + self::assertCount(count($expected), $sourceSelectionItems); + self::assertSame($expectIsShippable, $res->isShippable()); + + foreach ($sourceSelectionItems as $selectionItem) { + $key = $selectionItem->getSourceCode() . '/' . $selectionItem->getSku(); + self::assertSame((float) $expected[$key]['deduct'], $selectionItem->getQtyToDeduct()); + self::assertSame((float) $expected[$key]['avail'], $selectionItem->getQtyAvailable()); + } + } +} From 620c09ed9edc7f32e3ca1361c56cdf9c908004fd Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Sat, 16 Mar 2019 16:30:46 +0100 Subject: [PATCH 034/231] Deprecation annotation on injected params --- .../Result/GetDefaultSortedSourcesResult.php | 35 ++----------------- 1 file changed, 3 insertions(+), 32 deletions(-) diff --git a/InventorySourceSelectionApi/Model/Algorithms/Result/GetDefaultSortedSourcesResult.php b/InventorySourceSelectionApi/Model/Algorithms/Result/GetDefaultSortedSourcesResult.php index 527d5e659a10..35e53b92f108 100644 --- a/InventorySourceSelectionApi/Model/Algorithms/Result/GetDefaultSortedSourcesResult.php +++ b/InventorySourceSelectionApi/Model/Algorithms/Result/GetDefaultSortedSourcesResult.php @@ -33,16 +33,6 @@ class GetDefaultSortedSourcesResult */ private $sourceSelectionResultFactory; - /** - * @var SearchCriteriaBuilder - */ - private $searchCriteriaBuilder; - - /** - * @var SourceItemRepositoryInterface - */ - private $sourceItemRepository; - /** * @var GetAvailableSourceItemsBySkusAndSortedSourceInterface */ @@ -53,8 +43,9 @@ class GetDefaultSortedSourcesResult * * @param SourceSelectionItemInterfaceFactory $sourceSelectionItemFactory * @param SourceSelectionResultInterfaceFactory $sourceSelectionResultFactory - * @param SearchCriteriaBuilder $searchCriteriaBuilder - * @param SourceItemRepositoryInterface $sourceItemRepository + * @param SearchCriteriaBuilder $searchCriteriaBuilder @deprecated + * @param SourceItemRepositoryInterface $sourceItemRepository @deprecated + * @param GetAvailableSourceItemsBySkusAndSortedSourceInterface $getAvailableSourceItemsBySkusAndSortedSource = null * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( @@ -66,8 +57,6 @@ public function __construct( ) { $this->sourceSelectionItemFactory = $sourceSelectionItemFactory; $this->sourceSelectionResultFactory = $sourceSelectionResultFactory; - $this->searchCriteriaBuilder = $searchCriteriaBuilder; - $this->sourceItemRepository = $sourceItemRepository; $this->getAvailableSourceItemsBySkusAndSortedSource = $getAvailableSourceItemsBySkusAndSortedSource ?: ObjectManager::getInstance()->get(GetAvailableSourceItemsBySkusAndSortedSourceInterface::class); } @@ -84,24 +73,6 @@ private function isZero(float $floatNumber): bool return $floatNumber < 0.0000001; } - /** - * Returns source item from specific source by given SKU. Return null if source item is not found - * - * @param string $sourceCode - * @param string $sku - * @return SourceItemInterface|null - */ - private function getSourceItemBySourceCodeAndSku(string $sourceCode, string $sku): ?SourceItemInterface - { - $searchCriteria = $this->searchCriteriaBuilder - ->addFilter(SourceItemInterface::SOURCE_CODE, $sourceCode) - ->addFilter(SourceItemInterface::SKU, $sku) - ->create(); - $sourceItemsResult = $this->sourceItemRepository->getList($searchCriteria); - - return $sourceItemsResult->getTotalCount() > 0 ? current($sourceItemsResult->getItems()) : null; - } - /** * Generate default result for priority based algorithms * From 97065f23a8e9d5a935cc335b3a75fff4eb836553 Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Sat, 16 Mar 2019 16:38:46 +0100 Subject: [PATCH 035/231] Added assertion to make sure sources sorting order is preserved --- .../Integration/GetSourceItemsBySkusAndSortedSourceTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Inventory/Test/Integration/GetSourceItemsBySkusAndSortedSourceTest.php b/Inventory/Test/Integration/GetSourceItemsBySkusAndSortedSourceTest.php index af6cb1b403df..5aac2ef011dd 100644 --- a/Inventory/Test/Integration/GetSourceItemsBySkusAndSortedSourceTest.php +++ b/Inventory/Test/Integration/GetSourceItemsBySkusAndSortedSourceTest.php @@ -67,10 +67,14 @@ public function testShouldReturnSortedSourceItems(array $skus, array $sortedSour self::assertCount(count($expected), $sourceItems); + $keys = []; foreach ($sourceItems as $sourceItem) { $key = $sourceItem->getSourceCode() . '/' . $sourceItem->getSku(); + $keys[] = $key; self::assertSame($expected[$key], $sourceItem->getQuantity()); self::assertSame(SourceItemInterface::STATUS_IN_STOCK, $sourceItem->getStatus()); } + + self::assertSame(array_keys($expected), $keys, 'Sources sorting is not preserved'); } } From a05fd8e3144bf1881870f2f76ec04beac322ab4d Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Sat, 16 Mar 2019 17:43:47 +0100 Subject: [PATCH 036/231] Rework of bulk inventory transfer to improve performance --- .../ResourceModel/BulkInventoryTransfer.php | 153 ++++++++++-------- 1 file changed, 85 insertions(+), 68 deletions(-) diff --git a/InventoryCatalog/Model/ResourceModel/BulkInventoryTransfer.php b/InventoryCatalog/Model/ResourceModel/BulkInventoryTransfer.php index 219d5802b541..e283d8458a03 100644 --- a/InventoryCatalog/Model/ResourceModel/BulkInventoryTransfer.php +++ b/InventoryCatalog/Model/ResourceModel/BulkInventoryTransfer.php @@ -7,10 +7,13 @@ namespace Magento\InventoryCatalog\Model\ResourceModel; +use Magento\CatalogInventory\Api\Data\StockItemInterface; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\ResourceConnection; use Magento\Inventory\Model\ResourceModel\SourceItem; use Magento\InventoryApi\Api\Data\SourceItemInterface; use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; +use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface; use Magento\InventoryCatalogApi\Model\GetProductTypesBySkusInterface; use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForProductTypeInterface; @@ -43,21 +46,21 @@ class BulkInventoryTransfer private $defaultSourceProvider; /** - * @var SetDataToLegacyStockItem + * @var BulkZeroLegacyStockItem */ - private $setDataToLegacyStockItem; + private $bulkZeroLegacyStockItem; /** - * @var BulkZeroLegacyStockItem + * @var GetProductIdsBySkusInterface */ - private $bulkZeroLegacyStockItem; + private $getProductIdsBySkus; /** * @param ResourceConnection $resourceConnection * @param GetProductTypesBySkusInterface $getProductTypesBySkus * @param IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemManagementAllowedForProductType * @param DefaultSourceProviderInterface $defaultSourceProvider - * @param SetDataToLegacyStockItem $setDataToLegacyStockItem + * @param SetDataToLegacyStockItem $setDataToLegacyStockItem @deprecated * @param BulkZeroLegacyStockItem $bulkZeroLegacyStockItem * @SuppressWarnings(PHPMD.LongVariable) */ @@ -67,88 +70,98 @@ public function __construct( IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemManagementAllowedForProductType, DefaultSourceProviderInterface $defaultSourceProvider, SetDataToLegacyStockItem $setDataToLegacyStockItem, - BulkZeroLegacyStockItem $bulkZeroLegacyStockItem + BulkZeroLegacyStockItem $bulkZeroLegacyStockItem, + GetProductIdsBySkusInterface $getProductIdsBySkus = null ) { $this->resourceConnection = $resourceConnection; $this->getProductTypesBySkus = $getProductTypesBySkus; $this->isSourceItemManagementAllowedForProductType = $isSourceItemManagementAllowedForProductType; $this->defaultSourceProvider = $defaultSourceProvider; - $this->setDataToLegacyStockItem = $setDataToLegacyStockItem; $this->bulkZeroLegacyStockItem = $bulkZeroLegacyStockItem; + $this->getProductIdsBySkus = + $getProductIdsBySkus ?: ObjectManager::getInstance()->get(GetProductIdsBySkusInterface::class); } /** - * @param string $sku - * @param string $source - * @return array|null - */ - private function getSourceItemData(string $sku, string $source): ?array - { - $connection = $this->resourceConnection->getConnection(); - $tableName = $this->resourceConnection->getTableName(SourceItem::TABLE_NAME_SOURCE_ITEM); - - $query = $connection->select()->from($tableName) - ->where(SourceItemInterface::SOURCE_CODE . ' = ?', $source) - ->where(SourceItemInterface::SKU . ' = ?', $sku); - - $res = $connection->fetchRow($query); - if ($res === false) { - return null; - } - - return $res; - } - - /** - * @param string $sku + * @param array $skus * @param string $originSource * @param string $destinationSource * @return void */ private function transferInventory( - string $sku, + array $skus, string $originSource, string $destinationSource ): void { $connection = $this->resourceConnection->getConnection(); $tableName = $this->resourceConnection->getTableName(SourceItem::TABLE_NAME_SOURCE_ITEM); - $orgSourceItem = $this->getSourceItemData($sku, $originSource); - $dstSourceItem = $this->getSourceItemData($sku, $destinationSource); - - $orgSourceItemQty = $orgSourceItem === null ? 0.0 : (float) $orgSourceItem[SourceItemInterface::QUANTITY]; - $dstSourceItemQty = $dstSourceItem === null ? 0.0 : (float) $dstSourceItem[SourceItemInterface::QUANTITY]; - - $finalQuantity = $orgSourceItemQty + $dstSourceItemQty; - - if ($orgSourceItem !== null) { - $status = (int) $orgSourceItem[SourceItemInterface::STATUS]; - } elseif ($dstSourceItemQty !== null) { - $status = (int) $dstSourceItem[SourceItemInterface::STATUS]; - } else { - $status = (int) SourceItemInterface::STATUS_OUT_OF_STOCK; + $defaultSourceDestination = ($destinationSource === $this->defaultSourceProvider->getCode()); + + $orgSourceItemsData = $connection->fetchAssoc( + $connection->select() + ->from($tableName, [SourceItemInterface::SKU, '*']) + ->where(SourceItemInterface::SOURCE_CODE . ' = ?', $originSource) + ->where(SourceItemInterface::SKU . ' IN (?)', $skus) + ); + $dstSourceItemsData = $connection->fetchAssoc( + $connection->select() + ->from($tableName, [SourceItemInterface::SKU, '*']) + ->where(SourceItemInterface::SOURCE_CODE . ' = ?', $destinationSource) + ->where(SourceItemInterface::SKU . ' IN (?)', $skus) + ); + + $productIds = []; + if ($defaultSourceDestination) { + $productIds = $this->getProductIdsBySkus->execute($skus); } - $updateOperation = [ - SourceItemInterface::QUANTITY => $finalQuantity, - SourceItemInterface::STATUS => $status, - ]; - - if ($dstSourceItem === null) { - $updateOperation[SourceItemInterface::SOURCE_CODE] = $destinationSource; - $updateOperation[SourceItemInterface::SKU] = $sku; - - $connection->insert($tableName, $updateOperation); - } elseif ($orgSourceItem !== null) { - $connection->update($tableName, $updateOperation, [ - SourceItemInterface::SOURCE_CODE . '=?' => $destinationSource, - SourceItemInterface::SKU . '=?' => $sku, - ]); + $finalSourceItemsData = []; + $legacySyncData = []; + foreach ($skus as $sku) { + $finalQuantity = (float) + ($orgSourceItemsData[$sku][SourceItemInterface::QUANTITY] ?? 0.0) + + ($dstSourceItemsData[$sku][SourceItemInterface::QUANTITY] ?? 0.0); + + $finalStatus = $orgSourceItemsData[$sku][SourceItemInterface::STATUS] ?? + $dstSourceItemsData[$sku][SourceItemInterface::STATUS] ?? + SourceItemInterface::STATUS_OUT_OF_STOCK; + + $finalSourceItemsData[] = [ + SourceItemInterface::SOURCE_CODE => $destinationSource, + SourceItemInterface::SKU => $sku, + SourceItemInterface::QUANTITY => $finalQuantity, + SourceItemInterface::STATUS => $finalStatus, + ]; + + if (isset($productIds[$sku]) && $defaultSourceDestination) { + $legacySyncData[] = [ + StockItemInterface::QTY => $finalQuantity, + StockItemInterface::IS_IN_STOCK => $finalStatus, + StockItemInterface::PRODUCT_ID => $productIds[$sku], + 'website_id' => 0 + ]; + } } - // Align legacy stock - if ($destinationSource === $this->defaultSourceProvider->getCode()) { - $this->setDataToLegacyStockItem->execute($sku, $finalQuantity, $status); + $connection->insertOnDuplicate( + $tableName, + $finalSourceItemsData, + [ + SourceItemInterface::QUANTITY, + SourceItemInterface::STATUS + ] + ); + + if ($defaultSourceDestination) { + $connection->insertOnDuplicate( // Mass update via insert on duplicate + $this->resourceConnection->getTableName('cataloginventory_stock_item'), + $legacySyncData, + [ + StockItemInterface::QTY, + StockItemInterface::IS_IN_STOCK, + ] + ); } } @@ -203,18 +216,22 @@ public function execute( $types = $this->getProductTypesBySkus->execute($skus); $processedSkus = []; - $connection->beginTransaction(); + $filteredSkus = []; foreach ($types as $sku => $type) { if ($this->isSourceItemManagementAllowedForProductType->execute($type)) { - $this->transferInventory((string)$sku, $originSource, $destinationSource); - $processedSkus[] = $sku; + $filteredSkus[] = $sku; } } - if (!empty($processedSkus)) { - $this->clearSource($processedSkus, $originSource, $unassignFromOrigin); + if (empty($filteredSkus)) { + return; } + $connection->beginTransaction(); + + $this->transferInventory($filteredSkus, $originSource, $destinationSource); + $this->clearSource($filteredSkus, $originSource, $unassignFromOrigin); + $connection->commit(); } } From f36c85c4b12d1b6f240beaa629b13838beec60df Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Mon, 18 Mar 2019 14:39:47 +0100 Subject: [PATCH 037/231] Test integration failure --- ...ToLegacyStockItemAtSourceItemsSaveTest.php | 53 ------------------- ...ultSourceItemAtLegacyStockItemSaveTest.php | 32 ----------- 2 files changed, 85 deletions(-) diff --git a/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php b/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php index a5c3561c94ff..4344e39e938d 100644 --- a/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php +++ b/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php @@ -125,57 +125,4 @@ public function testSetData() self::assertFalse($legacyStockItem->getIsInStock()); self::assertEquals(20, $legacyStockItem->getQty()); } - - /** - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php - * @magentoAdminConfigFixture cataloginventory/legacy_stock/async 1 - * @SuppressWarnings(PHPMD.LongVariable) - */ - public function testSetDataAsynchronously() - { - $productSku = 'SKU-1'; - $product = $this->productRepository->get($productSku); - $productId = $product->getId(); - $websiteId = 0; - - /** @var StockItemCriteriaInterface $legacyStockItemCriteria */ - $legacyStockItemCriteria = $this->legacyStockItemCriteriaFactory->create(); - $legacyStockItemCriteria->setProductsFilter($productId); - $legacyStockItemCriteria->setScopeFilter($websiteId); - $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); - self::assertCount(1, $legacyStockItems); - - $legacyStockItem = reset($legacyStockItems); - self::assertTrue($legacyStockItem->getIsInStock()); - self::assertEquals(5.5, $legacyStockItem->getQty()); - - $searchCriteria = $this->searchCriteriaBuilder - ->addFilter(SourceItemInterface::SKU, $productSku) - ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) - ->create(); - $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); - self::assertCount(1, $sourceItems); - - $sourceItem = reset($sourceItems); - $sourceItem->setQuantity(20.0); - $sourceItem->setStatus(SourceItemInterface::STATUS_OUT_OF_STOCK); - $this->sourceItemsSave->execute($sourceItems); - - // Make sure we did not yet synchronized it - $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); - $legacyStockItem = current($legacyStockItems); - self::assertCount(1, $legacyStockItems); - self::assertTrue($legacyStockItem->getIsInStock()); - self::assertEquals(5.5, $legacyStockItem->getQty()); - - $this->consumer->process(1); - - // Check after asynchrnous consumer call - $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); - $legacyStockItem = current($legacyStockItems); - self::assertFalse($legacyStockItem->getIsInStock()); - self::assertEquals(20, $legacyStockItem->getQty()); - } } diff --git a/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php b/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php index 3de7f228dde6..809b5d9c3cb5 100644 --- a/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php +++ b/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php @@ -64,38 +64,6 @@ public function testSaveLegacyStockItemAssignedToDefaultSource() ); } - /** - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php - * @magentoAdminConfigFixture cataloginventory/legacy_stock/async 1 - * @magentoDbIsolation enabled - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ - public function testSaveLegacyStockItemAssignedToDefaultSourceAsynchronously() - { - $stockItem = $this->stockRegistry->getStockItemBySku('SKU-1'); - $stockItem->setQty(10); - $this->stockRegistry->updateStockItemBySku('SKU-1', $stockItem); - - $defaultSourceItem = $this->getDefaultSourceItemBySku->execute('SKU-1'); - self::assertEquals( - 5.5, - $defaultSourceItem->getQuantity(), - 'Source item was update synchronously even if asynchronous operation was requested' - ); - - $this->consumer->process(1); - - $defaultSourceItem = $this->getDefaultSourceItemBySku->execute('SKU-1'); - self::assertEquals( - 10, - $defaultSourceItem->getQuantity(), - 'Asynchronous source item update failed' - ); - } - /** * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php From c9f704e3eeb932df5d6c9e38321757fd36b345be Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Thu, 21 Mar 2019 16:05:05 +0100 Subject: [PATCH 038/231] Revert "Test integration failure" This reverts test commit 3de33db5a5c16a6b41d6c9ecb210fc94de124097. --- ...ToLegacyStockItemAtSourceItemsSaveTest.php | 53 +++++++++++++++++++ ...ultSourceItemAtLegacyStockItemSaveTest.php | 32 +++++++++++ 2 files changed, 85 insertions(+) diff --git a/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php b/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php index 4344e39e938d..a5c3561c94ff 100644 --- a/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php +++ b/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php @@ -125,4 +125,57 @@ public function testSetData() self::assertFalse($legacyStockItem->getIsInStock()); self::assertEquals(20, $legacyStockItem->getQty()); } + + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php + * @magentoAdminConfigFixture cataloginventory/legacy_stock/async 1 + * @SuppressWarnings(PHPMD.LongVariable) + */ + public function testSetDataAsynchronously() + { + $productSku = 'SKU-1'; + $product = $this->productRepository->get($productSku); + $productId = $product->getId(); + $websiteId = 0; + + /** @var StockItemCriteriaInterface $legacyStockItemCriteria */ + $legacyStockItemCriteria = $this->legacyStockItemCriteriaFactory->create(); + $legacyStockItemCriteria->setProductsFilter($productId); + $legacyStockItemCriteria->setScopeFilter($websiteId); + $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); + self::assertCount(1, $legacyStockItems); + + $legacyStockItem = reset($legacyStockItems); + self::assertTrue($legacyStockItem->getIsInStock()); + self::assertEquals(5.5, $legacyStockItem->getQty()); + + $searchCriteria = $this->searchCriteriaBuilder + ->addFilter(SourceItemInterface::SKU, $productSku) + ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) + ->create(); + $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); + self::assertCount(1, $sourceItems); + + $sourceItem = reset($sourceItems); + $sourceItem->setQuantity(20.0); + $sourceItem->setStatus(SourceItemInterface::STATUS_OUT_OF_STOCK); + $this->sourceItemsSave->execute($sourceItems); + + // Make sure we did not yet synchronized it + $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); + $legacyStockItem = current($legacyStockItems); + self::assertCount(1, $legacyStockItems); + self::assertTrue($legacyStockItem->getIsInStock()); + self::assertEquals(5.5, $legacyStockItem->getQty()); + + $this->consumer->process(1); + + // Check after asynchrnous consumer call + $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); + $legacyStockItem = current($legacyStockItems); + self::assertFalse($legacyStockItem->getIsInStock()); + self::assertEquals(20, $legacyStockItem->getQty()); + } } diff --git a/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php b/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php index 809b5d9c3cb5..3de7f228dde6 100644 --- a/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php +++ b/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php @@ -64,6 +64,38 @@ public function testSaveLegacyStockItemAssignedToDefaultSource() ); } + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php + * @magentoAdminConfigFixture cataloginventory/legacy_stock/async 1 + * @magentoDbIsolation enabled + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function testSaveLegacyStockItemAssignedToDefaultSourceAsynchronously() + { + $stockItem = $this->stockRegistry->getStockItemBySku('SKU-1'); + $stockItem->setQty(10); + $this->stockRegistry->updateStockItemBySku('SKU-1', $stockItem); + + $defaultSourceItem = $this->getDefaultSourceItemBySku->execute('SKU-1'); + self::assertEquals( + 5.5, + $defaultSourceItem->getQuantity(), + 'Source item was update synchronously even if asynchronous operation was requested' + ); + + $this->consumer->process(1); + + $defaultSourceItem = $this->getDefaultSourceItemBySku->execute('SKU-1'); + self::assertEquals( + 10, + $defaultSourceItem->getQuantity(), + 'Asynchronous source item update failed' + ); + } + /** * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php From 6a8089797de90ca3bf0218d5402990a225c14013 Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Thu, 21 Mar 2019 17:02:40 +0100 Subject: [PATCH 039/231] WIP refactor with isolated module --- InventoryCatalog/composer.json | 3 +- InventoryCatalog/etc/di.xml | 22 ------------ .../Model}/AsyncConsumer.php | 19 ++++++----- .../Model/GetDefaultSourceItemsBySkus.php | 3 +- .../GetLegacyStockItemsByProductIds.php | 2 +- .../Model}/IsAsyncLegacyAlignment.php | 5 ++- .../Model}/Synchronize.php | 31 +++++++++++------ .../Model/SynchronizeInventoryData.php | 14 ++++---- .../ToLegacy}/SetDataToLegacyInventory.php | 4 +-- .../Model/ToMsi}/SetDataToSourceItem.php | 2 +- ...atalogInventoryAtSourceItemsSavePlugin.php | 4 +-- ...alogInventoryAtSourceItemsDeletePlugin.php | 3 +- ...dateSourceItemAtLegacyQtyCounterPlugin.php | 10 +++++- ...eSourceItemAtLegacyStockItemSavePlugin.php | 4 +-- InventoryLegacySynchronization/composer.json | 23 +++++++++++++ .../etc/communication.xml | 12 +++++++ InventoryLegacySynchronization/etc/di.xml | 34 +++++++++++++++++++ InventoryLegacySynchronization/etc/module.xml | 12 +++++++ .../etc/queue_consumer.xml | 16 +++++++++ .../etc/queue_publisher.xml | 14 ++++++++ .../etc/queue_topology.xml | 19 +++++++++++ .../registration.php | 13 +++++++ 22 files changed, 206 insertions(+), 63 deletions(-) rename {InventoryCatalog/Model/LegacySynchronization => InventoryLegacySynchronization/Model}/AsyncConsumer.php (65%) rename {InventoryCatalog => InventoryLegacySynchronization}/Model/GetDefaultSourceItemsBySkus.php (95%) rename {InventoryCatalog/Model/LegacySynchronization => InventoryLegacySynchronization/Model}/GetLegacyStockItemsByProductIds.php (96%) rename {InventoryCatalog/Model/LegacySynchronization => InventoryLegacySynchronization/Model}/IsAsyncLegacyAlignment.php (84%) rename {InventoryCatalog/Model/LegacySynchronization => InventoryLegacySynchronization/Model}/Synchronize.php (82%) rename InventoryCatalog/Model/LegacySynchronization/SetDataToDestination.php => InventoryLegacySynchronization/Model/SynchronizeInventoryData.php (71%) rename {InventoryCatalog/Model/LegacySynchronization/ToLegacyCatalogInventory => InventoryLegacySynchronization/Model/ToLegacy}/SetDataToLegacyInventory.php (95%) rename {InventoryCatalog/Model/LegacySynchronization/ToInventory => InventoryLegacySynchronization/Model/ToMsi}/SetDataToSourceItem.php (95%) rename {InventoryCatalog/Plugin/InventoryApi => InventoryLegacySynchronization/Plugin}/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php (95%) rename {InventoryCatalog/Plugin/InventoryApi => InventoryLegacySynchronization/Plugin}/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php (97%) rename {InventoryCatalog/Plugin/CatalogInventory => InventoryLegacySynchronization/Plugin}/UpdateSourceItemAtLegacyQtyCounterPlugin.php (90%) rename {InventoryCatalog/Plugin/CatalogInventory => InventoryLegacySynchronization/Plugin}/UpdateSourceItemAtLegacyStockItemSavePlugin.php (97%) create mode 100644 InventoryLegacySynchronization/composer.json create mode 100644 InventoryLegacySynchronization/etc/communication.xml create mode 100644 InventoryLegacySynchronization/etc/di.xml create mode 100644 InventoryLegacySynchronization/etc/module.xml create mode 100644 InventoryLegacySynchronization/etc/queue_consumer.xml create mode 100644 InventoryLegacySynchronization/etc/queue_publisher.xml create mode 100644 InventoryLegacySynchronization/etc/queue_topology.xml create mode 100644 InventoryLegacySynchronization/registration.php diff --git a/InventoryCatalog/composer.json b/InventoryCatalog/composer.json index 2d79e1a915f1..fa94a574e4d4 100644 --- a/InventoryCatalog/composer.json +++ b/InventoryCatalog/composer.json @@ -13,8 +13,7 @@ "magento/module-inventory-configuration-api": "*", "magento/module-inventory-indexer": "*", "magento/module-inventory-sales-api": "*", - "magento/module-inventory-configuration": "*", - "magento/module-asynchronous-operations": "*" + "magento/module-inventory-configuration": "*" }, "suggest": { "magento/module-inventory-reservations-api": "*" diff --git a/InventoryCatalog/etc/di.xml b/InventoryCatalog/etc/di.xml index 4895f47c5bd8..1912e95fa519 100644 --- a/InventoryCatalog/etc/di.xml +++ b/InventoryCatalog/etc/di.xml @@ -16,10 +16,6 @@ <plugin name="prevent_default_stock_deleting" type="Magento\InventoryCatalog\Plugin\InventoryApi\StockRepository\PreventDeleting\DefaultStockPlugin"/> </type> - <type name="Magento\InventoryApi\Api\SourceItemsSaveInterface"> - <plugin name="set_data_to_legacy_catalog_inventory_at_source_items_save" - type="Magento\InventoryCatalog\Plugin\InventoryApi\SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin"/> - </type> <type name="Magento\InventoryIndexer\Indexer\SourceItem\SourceItemIndexer"> <plugin name="priceIndexUpdater" type="Magento\InventoryCatalog\Plugin\InventoryIndexer\Indexer\SourceItem\PriceIndexUpdater" sortOrder="20"/> </type> @@ -29,18 +25,6 @@ <type name="Magento\CatalogInventory\Model\Indexer\ProductPriceIndexFilter"> <plugin name="change_select_for_price_modifier" type="Magento\InventoryCatalog\Plugin\CatalogInventory\Model\Indexer\ModifySelectInProductPriceIndexFilter"/> </type> - <type name="Magento\InventoryApi\Api\SourceItemsDeleteInterface"> - <plugin name="set_to_zero_legacy_catalog_inventory_at_source_items_delete" - type="Magento\InventoryCatalog\Plugin\InventoryApi\SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin"/> - </type> - <type name="Magento\CatalogInventory\Model\ResourceModel\QtyCounterInterface"> - <plugin name="update_source_item_at_legacy_qty_counter" type="Magento\InventoryCatalog\Plugin\CatalogInventory\UpdateSourceItemAtLegacyQtyCounterPlugin"/> - </type> - <type name="Magento\CatalogInventory\Model\ResourceModel\Stock\Item"> - <plugin name="update_source_item_at_legacy_stock_item_save" - type="Magento\InventoryCatalog\Plugin\CatalogInventory\UpdateSourceItemAtLegacyStockItemSavePlugin"/> - <plugin name="priceIndexUpdater" disabled="true"/> - </type> <type name="Magento\CatalogInventory\Model\ResourceModel\Stock\Status"> <plugin name="adapt_add_stock_data_to_collection" type="Magento\InventoryCatalog\Plugin\CatalogInventory\Model\ResourceModel\Stock\Status\AdaptAddStockDataToCollectionPlugin"/> @@ -140,10 +124,4 @@ <plugin name="adapt_verify_stock_to_negative_min_qty" type="Magento\InventoryCatalog\Plugin\CatalogInventory\Model\Spi\StockStateProvider\AdaptVerifyStockToNegativeMinQtyPlugin"/> </type> - - <type name="Magento\InventoryCatalog\Model\LegacySynchronization\Synchronize"> - <arguments> - <argument name="batchSize" xsi:type="number">100</argument> - </arguments> - </type> </config> diff --git a/InventoryCatalog/Model/LegacySynchronization/AsyncConsumer.php b/InventoryLegacySynchronization/Model/AsyncConsumer.php similarity index 65% rename from InventoryCatalog/Model/LegacySynchronization/AsyncConsumer.php rename to InventoryLegacySynchronization/Model/AsyncConsumer.php index 93267067ce98..833829324625 100644 --- a/InventoryCatalog/Model/LegacySynchronization/AsyncConsumer.php +++ b/InventoryLegacySynchronization/Model/AsyncConsumer.php @@ -5,11 +5,15 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Model\LegacySynchronization; +namespace Magento\InventoryLegacySynchronization\Model; use Magento\AsynchronousOperations\Api\Data\OperationInterface; use Magento\Framework\Serialize\SerializerInterface; +/** + * Consumer class for asynchronous legacy synchronization. + * Works bot from MSI to legacy and vice versa. + */ class AsyncConsumer { /** @@ -18,22 +22,21 @@ class AsyncConsumer private $serializer; /** - * @var SetDataToDestination + * @var SynchronizeInventoryData */ - private $setDataToDestination; + private $synchronizeInventoryData; /** - * Consumer constructor. * @param SerializerInterface $serializer - * @param SetDataToDestination $setDataToDestination + * @param SynchronizeInventoryData $synchronizeInventoryData * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( SerializerInterface $serializer, - SetDataToDestination $setDataToDestination + SynchronizeInventoryData $synchronizeInventoryData ) { $this->serializer = $serializer; - $this->setDataToDestination = $setDataToDestination; + $this->synchronizeInventoryData = $synchronizeInventoryData; } /** @@ -46,6 +49,6 @@ public function __construct( public function processOperations(OperationInterface $operation): void { $data = $this->serializer->unserialize($operation->getSerializedData()); - $this->setDataToDestination->execute($data['direction'], $data['items']); + $this->synchronizeInventoryData->execute($data['direction'], $data['items']); } } diff --git a/InventoryCatalog/Model/GetDefaultSourceItemsBySkus.php b/InventoryLegacySynchronization/Model/GetDefaultSourceItemsBySkus.php similarity index 95% rename from InventoryCatalog/Model/GetDefaultSourceItemsBySkus.php rename to InventoryLegacySynchronization/Model/GetDefaultSourceItemsBySkus.php index cc9d87d261bf..e831f87a2940 100644 --- a/InventoryCatalog/Model/GetDefaultSourceItemsBySkus.php +++ b/InventoryLegacySynchronization/Model/GetDefaultSourceItemsBySkus.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Model; +namespace Magento\InventoryLegacySynchronization\Model; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\InventoryApi\Api\Data\SourceItemInterface; @@ -33,7 +33,6 @@ class GetDefaultSourceItemsBySkus private $sourceItemRepository; /** - * GetDefaultSourceItemsBySkus constructor. * @param SearchCriteriaBuilder $searchCriteriaBuilder * @param DefaultSourceProviderInterface $defaultSourceProvider * @param SourceItemRepositoryInterface $sourceItemRepository diff --git a/InventoryCatalog/Model/LegacySynchronization/GetLegacyStockItemsByProductIds.php b/InventoryLegacySynchronization/Model/GetLegacyStockItemsByProductIds.php similarity index 96% rename from InventoryCatalog/Model/LegacySynchronization/GetLegacyStockItemsByProductIds.php rename to InventoryLegacySynchronization/Model/GetLegacyStockItemsByProductIds.php index f9e8eeb6e725..df7cdd01b37e 100644 --- a/InventoryCatalog/Model/LegacySynchronization/GetLegacyStockItemsByProductIds.php +++ b/InventoryLegacySynchronization/Model/GetLegacyStockItemsByProductIds.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Model\LegacySynchronization; +namespace Magento\InventoryLegacySynchronization\Model; use Magento\CatalogInventory\Api\Data\StockItemInterface; use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory; diff --git a/InventoryCatalog/Model/LegacySynchronization/IsAsyncLegacyAlignment.php b/InventoryLegacySynchronization/Model/IsAsyncLegacyAlignment.php similarity index 84% rename from InventoryCatalog/Model/LegacySynchronization/IsAsyncLegacyAlignment.php rename to InventoryLegacySynchronization/Model/IsAsyncLegacyAlignment.php index 3056f3feab0e..13705c557082 100644 --- a/InventoryCatalog/Model/LegacySynchronization/IsAsyncLegacyAlignment.php +++ b/InventoryLegacySynchronization/Model/IsAsyncLegacyAlignment.php @@ -5,12 +5,15 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Model\LegacySynchronization; +namespace Magento\InventoryLegacySynchronization\Model; use Magento\Framework\App\Config\ScopeConfigInterface; class IsAsyncLegacyAlignment { + /** + * Configuration path for legacy stock asynchronous operation enabling status + */ private const XML_PATH = 'cataloginventory/legacy_stock/async'; /** diff --git a/InventoryCatalog/Model/LegacySynchronization/Synchronize.php b/InventoryLegacySynchronization/Model/Synchronize.php similarity index 82% rename from InventoryCatalog/Model/LegacySynchronization/Synchronize.php rename to InventoryLegacySynchronization/Model/Synchronize.php index f3b3fd9d5862..786249293a15 100644 --- a/InventoryCatalog/Model/LegacySynchronization/Synchronize.php +++ b/InventoryLegacySynchronization/Model/Synchronize.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Model\LegacySynchronization; +namespace Magento\InventoryLegacySynchronization\Model; use Magento\AsynchronousOperations\Api\Data\OperationInterface; use Magento\AsynchronousOperations\Api\Data\OperationInterfaceFactory; @@ -18,10 +18,20 @@ */ class Synchronize { - private const TOPIC_NAME = 'inventory.catalog.product.legacy_inventory.set_data'; + /** + * Asynchronous operation topic name + */ + private const TOPIC_NAME = 'inventory.legacy_synchronization.set_data'; - public const DIRECTION_TO_LEGACY = 'to-legacy'; - public const DIRECTION_TO_INVENTORY = 'to-inventory'; + /** + * Define synchronization from MSI source items to legacy catalog inventory + */ + public const MSI_TO_LEGACY = 'msi-to-legacy'; + + /** + * Define synchronization from legacy catalog inventory to MSI source items + */ + public const LEGACY_TO_MSI = 'legacy-to-msi'; /** * @var BulkManagementInterface @@ -49,9 +59,9 @@ class Synchronize private $isAsyncLegacyAlignment; /** - * @var SetDataToDestination + * @var SynchronizeInventoryData */ - private $setDataToDestination; + private $synchronizeInventoryData; /** * @var int @@ -59,13 +69,12 @@ class Synchronize private $batchSize; /** - * AsyncSetDataToLegacyCatalogInventory constructor. * @param BulkManagementInterface $bulkManagement * @param SerializerInterface $serializer * @param IdentityGeneratorInterface $identityService * @param OperationInterfaceFactory $operationInterfaceFactory * @param IsAsyncLegacyAlignment $isAsyncLegacyAlignment - * @param SetDataToDestination $setDataToDestination + * @param SynchronizeInventoryData $synchronizeInventoryData * @param int $batchSize */ public function __construct( @@ -74,7 +83,7 @@ public function __construct( IdentityGeneratorInterface $identityService, OperationInterfaceFactory $operationInterfaceFactory, IsAsyncLegacyAlignment $isAsyncLegacyAlignment, - SetDataToDestination $setDataToDestination, + SynchronizeInventoryData $synchronizeInventoryData, int $batchSize ) { $this->bulkManagement = $bulkManagement; @@ -83,7 +92,7 @@ public function __construct( $this->operationInterfaceFactory = $operationInterfaceFactory; $this->batchSize = $batchSize; $this->isAsyncLegacyAlignment = $isAsyncLegacyAlignment; - $this->setDataToDestination = $setDataToDestination; + $this->synchronizeInventoryData = $synchronizeInventoryData; } /** @@ -127,7 +136,7 @@ private function executeAsync(string $direction, array $items): void */ private function executeSync(string $direction, array $items): void { - $this->setDataToDestination->execute($direction, $items); + $this->synchronizeInventoryData->execute($direction, $items); } /** diff --git a/InventoryCatalog/Model/LegacySynchronization/SetDataToDestination.php b/InventoryLegacySynchronization/Model/SynchronizeInventoryData.php similarity index 71% rename from InventoryCatalog/Model/LegacySynchronization/SetDataToDestination.php rename to InventoryLegacySynchronization/Model/SynchronizeInventoryData.php index e2fa0da28ba4..58a306a5ffeb 100644 --- a/InventoryCatalog/Model/LegacySynchronization/SetDataToDestination.php +++ b/InventoryLegacySynchronization/Model/SynchronizeInventoryData.php @@ -5,15 +5,15 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Model\LegacySynchronization; +namespace Magento\InventoryLegacySynchronization\Model; -use Magento\InventoryCatalog\Model\LegacySynchronization\ToInventory\SetDataToSourceItem; -use Magento\InventoryCatalog\Model\LegacySynchronization\ToLegacyCatalogInventory\SetDataToLegacyInventory; +use Magento\InventoryLegacySynchronization\Model\ToMsi\SetDataToSourceItem; +use Magento\InventoryLegacySynchronization\Model\ToLegacy\SetDataToLegacyInventory; /** * Set Qty and status for legacy CatalogInventory Stock Item table */ -class SetDataToDestination +class SynchronizeInventoryData { /** * @var SetDataToLegacyInventory @@ -40,13 +40,13 @@ public function __construct( } /** - * @param string $direction + * @param string $destination * @param array $items * @throws \Magento\Framework\Exception\LocalizedException */ - public function execute(string $direction, array $items): void + public function execute(string $destination, array $items): void { - if ($direction === Synchronize::DIRECTION_TO_LEGACY) { + if ($destination === Synchronize::MSI_TO_LEGACY) { $this->setDataToLegacyInventory->execute($items); } else { $this->setDataToSourceItem->execute($items); diff --git a/InventoryCatalog/Model/LegacySynchronization/ToLegacyCatalogInventory/SetDataToLegacyInventory.php b/InventoryLegacySynchronization/Model/ToLegacy/SetDataToLegacyInventory.php similarity index 95% rename from InventoryCatalog/Model/LegacySynchronization/ToLegacyCatalogInventory/SetDataToLegacyInventory.php rename to InventoryLegacySynchronization/Model/ToLegacy/SetDataToLegacyInventory.php index c2b58f243029..62f132817a68 100644 --- a/InventoryCatalog/Model/LegacySynchronization/ToLegacyCatalogInventory/SetDataToLegacyInventory.php +++ b/InventoryLegacySynchronization/Model/ToLegacy/SetDataToLegacyInventory.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Model\LegacySynchronization\ToLegacyCatalogInventory; +namespace Magento\InventoryLegacySynchronization\Model\ToLegacy; use Magento\CatalogInventory\Model\Indexer\Stock\Processor; use Magento\CatalogInventory\Model\Spi\StockStateProviderInterface; @@ -13,7 +13,7 @@ use Magento\Framework\Exception\NoSuchEntityException; use Magento\InventoryApi\Api\Data\SourceItemInterface; use Magento\InventoryCatalog\Model\GetProductIdsBySkus; -use Magento\InventoryCatalog\Model\LegacySynchronization\GetLegacyStockItemsByProductIds; +use Magento\InventoryLegacySynchronization\Model\GetLegacyStockItemsByProductIds; use Magento\InventoryCatalog\Model\ResourceModel\SetDataToLegacyStockItem; use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; diff --git a/InventoryCatalog/Model/LegacySynchronization/ToInventory/SetDataToSourceItem.php b/InventoryLegacySynchronization/Model/ToMsi/SetDataToSourceItem.php similarity index 95% rename from InventoryCatalog/Model/LegacySynchronization/ToInventory/SetDataToSourceItem.php rename to InventoryLegacySynchronization/Model/ToMsi/SetDataToSourceItem.php index bc63ba4cf26a..35e694a1f4cf 100644 --- a/InventoryCatalog/Model/LegacySynchronization/ToInventory/SetDataToSourceItem.php +++ b/InventoryLegacySynchronization/Model/ToMsi/SetDataToSourceItem.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Model\LegacySynchronization\ToInventory; +namespace Magento\InventoryLegacySynchronization\Model\ToMsi; use Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory; use Magento\InventoryCatalog\Model\UpdateSourceItemBasedOnLegacyStockItem; diff --git a/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php b/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php similarity index 95% rename from InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php rename to InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php index 2a5126d67fa3..b7d79049726c 100644 --- a/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php +++ b/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Plugin\InventoryApi; +namespace Magento\InventoryLegacySynchronization\Plugin; use Magento\Framework\App\ObjectManager; use Magento\InventoryApi\Api\Data\SourceItemInterface; @@ -95,6 +95,6 @@ public function afterExecute(SourceItemsSaveInterface $subject, $result, array $ $sourceItemsData[] = $sourceItem->getData(); } - $this->synchronize->execute(Synchronize::DIRECTION_TO_LEGACY, $sourceItemsData); + $this->synchronize->execute(Synchronize::MSI_TO_LEGACY, $sourceItemsData); } } diff --git a/InventoryCatalog/Plugin/InventoryApi/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php b/InventoryLegacySynchronization/Plugin/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php similarity index 97% rename from InventoryCatalog/Plugin/InventoryApi/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php rename to InventoryLegacySynchronization/Plugin/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php index cda7741d1c62..c29964abaaa7 100644 --- a/InventoryCatalog/Plugin/InventoryApi/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php +++ b/InventoryLegacySynchronization/Plugin/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Plugin\InventoryApi; +namespace Magento\InventoryLegacySynchronization\Plugin; use Magento\CatalogInventory\Model\Indexer\Stock\Processor; use Magento\Framework\Exception\NoSuchEntityException; @@ -60,6 +60,7 @@ class SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin * @param Processor $indexerProcessor * @param IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemsAllowedForProductType * @param GetProductTypesBySkusInterface $getProductTypeBySku + * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( DefaultSourceProviderInterface $defaultSourceProvider, diff --git a/InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyQtyCounterPlugin.php b/InventoryLegacySynchronization/Plugin/UpdateSourceItemAtLegacyQtyCounterPlugin.php similarity index 90% rename from InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyQtyCounterPlugin.php rename to InventoryLegacySynchronization/Plugin/UpdateSourceItemAtLegacyQtyCounterPlugin.php index 9cd4062709e3..4149ae51fa9f 100644 --- a/InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyQtyCounterPlugin.php +++ b/InventoryLegacySynchronization/Plugin/UpdateSourceItemAtLegacyQtyCounterPlugin.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Plugin\CatalogInventory; +namespace Magento\InventoryLegacySynchronization\Plugin; use Magento\CatalogInventory\Model\ResourceModel\QtyCounterInterface; use Magento\Framework\App\ResourceConnection; @@ -59,6 +59,7 @@ class UpdateSourceItemAtLegacyQtyCounterPlugin * @param SearchCriteriaBuilder $searchCriteriaBuilder * @param ResourceConnection $resourceConnection * @param GetSkusByProductIdsInterface $getSkusByProductIds + * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( SourceItemRepositoryInterface $sourceItemRepository, @@ -115,6 +116,11 @@ public function aroundCorrectItemsQty( * @param int[] $productQuantitiesByProductId * @param string $operator * @return void + * @throws \Magento\Framework\Exception\CouldNotSaveException + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Validation\ValidationException + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @SuppressWarnings(PHPMD.LongVariable) */ private function updateSourceItemAtLegacyCatalogInventoryQtyCounter( array $productQuantitiesByProductId, @@ -143,6 +149,8 @@ private function updateSourceItemAtLegacyCatalogInventoryQtyCounter( /** * @param int[] $productQuantitiesByProductId * @return array + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @SuppressWarnings(PHPMD.LongVariable) */ private function getProductQuantitiesBySku(array $productQuantitiesByProductId): array { diff --git a/InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyStockItemSavePlugin.php b/InventoryLegacySynchronization/Plugin/UpdateSourceItemAtLegacyStockItemSavePlugin.php similarity index 97% rename from InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyStockItemSavePlugin.php rename to InventoryLegacySynchronization/Plugin/UpdateSourceItemAtLegacyStockItemSavePlugin.php index b0e9456c9bef..6d3af3ae6245 100755 --- a/InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyStockItemSavePlugin.php +++ b/InventoryLegacySynchronization/Plugin/UpdateSourceItemAtLegacyStockItemSavePlugin.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Plugin\CatalogInventory; +namespace Magento\InventoryLegacySynchronization\Plugin; use Magento\CatalogInventory\Model\ResourceModel\Stock\Item as ItemResourceModel; use Magento\CatalogInventory\Model\Stock\Item; @@ -103,7 +103,7 @@ public function aroundSave(ItemResourceModel $subject, callable $proceed, Abstra if ($this->isSourceItemManagementAllowedForProductType->execute($typeId)) { if ($this->shouldAlignDefaultSourceWithLegacy($legacyStockItem)) { $this->synchronize->execute( - Synchronize::DIRECTION_TO_INVENTORY, + Synchronize::LEGACY_TO_MSI, [ $legacyStockItem->getData() ] diff --git a/InventoryLegacySynchronization/composer.json b/InventoryLegacySynchronization/composer.json new file mode 100644 index 000000000000..1ec4e9aba225 --- /dev/null +++ b/InventoryLegacySynchronization/composer.json @@ -0,0 +1,23 @@ +{ + "name": "magento/module-inventory-legacy-synchronization", + "description": "N/A", + "require": { + "php": "~7.1.3||~7.2.0", + "magento/framework": "*", + "magento/module-catalog-inventory": "*", + "magento/module-asynchronous-operations": "*" + }, + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\InventoryLegacySynchronization\\": "" + } + } +} diff --git a/InventoryLegacySynchronization/etc/communication.xml b/InventoryLegacySynchronization/etc/communication.xml new file mode 100644 index 000000000000..74fee2a4edb9 --- /dev/null +++ b/InventoryLegacySynchronization/etc/communication.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:Communication/etc/communication.xsd"> + <topic name="inventory.legacy_synchronization.set_data" + request="Magento\AsynchronousOperations\Api\Data\OperationInterface" /> +</config> diff --git a/InventoryLegacySynchronization/etc/di.xml b/InventoryLegacySynchronization/etc/di.xml new file mode 100644 index 000000000000..8e87c8d0a240 --- /dev/null +++ b/InventoryLegacySynchronization/etc/di.xml @@ -0,0 +1,34 @@ +<?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\CatalogInventory\Model\ResourceModel\QtyCounterInterface"> + <plugin name="update_source_item_at_legacy_qty_counter" + type="Magento\InventoryLegacySynchronization\Plugin\UpdateSourceItemAtLegacyQtyCounterPlugin"/> + </type> + <type name="Magento\CatalogInventory\Model\ResourceModel\Stock\Item"> + <plugin name="update_source_item_at_legacy_stock_item_save" + type="Magento\InventoryLegacySynchronization\Plugin\UpdateSourceItemAtLegacyStockItemSavePlugin"/> + <plugin name="priceIndexUpdater" disabled="true"/> + </type> + <type name="Magento\InventoryApi\Api\SourceItemsSaveInterface"> + <plugin name="set_data_to_legacy_catalog_inventory_at_source_items_save" + type="Magento\InventoryLegacySynchronization\Plugin\SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin"/> + </type> + <type name="Magento\InventoryApi\Api\SourceItemsDeleteInterface"> + <plugin name="set_to_zero_legacy_catalog_inventory_at_source_items_delete" + type="Magento\InventoryLegacySynchronization\Plugin\SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin"/> + </type> + + <type name="Magento\InventoryLegacySynchronization\Model\Synchronize"> + <arguments> + <argument name="batchSize" xsi:type="number">100</argument> + </arguments> + </type> +</config> diff --git a/InventoryLegacySynchronization/etc/module.xml b/InventoryLegacySynchronization/etc/module.xml new file mode 100644 index 000000000000..f72b1d021bd1 --- /dev/null +++ b/InventoryLegacySynchronization/etc/module.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:Module/etc/module.xsd"> + <module name="Magento_InventoryLegacySynchronization" setup_version="0.1.0"> + </module> +</config> diff --git a/InventoryLegacySynchronization/etc/queue_consumer.xml b/InventoryLegacySynchronization/etc/queue_consumer.xml new file mode 100644 index 000000000000..0030f14e704f --- /dev/null +++ b/InventoryLegacySynchronization/etc/queue_consumer.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:framework-message-queue:etc/consumer.xsd"> + <consumer + name="legacyInventorySynchronization" + queue="inventory_legacy_synchronization_set_data_queue" + connection="amqp" + handler="Magento\InventoryLegacySynchronization\Model\AsyncConsumer::processOperations"/> +</config> \ No newline at end of file diff --git a/InventoryLegacySynchronization/etc/queue_publisher.xml b/InventoryLegacySynchronization/etc/queue_publisher.xml new file mode 100644 index 000000000000..9ab3c5385262 --- /dev/null +++ b/InventoryLegacySynchronization/etc/queue_publisher.xml @@ -0,0 +1,14 @@ +<?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-message-queue:etc/publisher.xsd"> + <publisher topic="inventory.legacy_synchronization.set_data"> + <connection name="amqp" exchange="inventory.legacy_synchronization.set_data.exchange" /> + </publisher> +</config> \ No newline at end of file diff --git a/InventoryLegacySynchronization/etc/queue_topology.xml b/InventoryLegacySynchronization/etc/queue_topology.xml new file mode 100644 index 000000000000..91c9b72a71f0 --- /dev/null +++ b/InventoryLegacySynchronization/etc/queue_topology.xml @@ -0,0 +1,19 @@ +<?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-message-queue:etc/topology.xsd"> + + <exchange name="inventory.legacy_synchronization.set_data.exchange" type="topic" connection="amqp"> + <binding + id="legacyInventorySynchronizationExchangeBinding" + topic="inventory.legacy_synchronization.set_data" + destinationType="queue" + destination="inventory_legacy_synchronization_set_data_queue"/> + </exchange> +</config> \ No newline at end of file diff --git a/InventoryLegacySynchronization/registration.php b/InventoryLegacySynchronization/registration.php new file mode 100644 index 000000000000..06eb9f828e1e --- /dev/null +++ b/InventoryLegacySynchronization/registration.php @@ -0,0 +1,13 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +\Magento\Framework\Component\ComponentRegistrar::register( + \Magento\Framework\Component\ComponentRegistrar::MODULE, + 'Magento_InventoryLegacySynchronization', + __DIR__ +); From c7c0116a50604ae32d067010ca5251fd782c87a8 Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Thu, 21 Mar 2019 17:33:25 +0100 Subject: [PATCH 040/231] Reverted SetDataToLegacyCatalogInventory and deprecated --- .../SetDataToLegacyCatalogInventory.php | 121 ++++++++++++++---- 1 file changed, 99 insertions(+), 22 deletions(-) diff --git a/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php b/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php index 16eb53e60b32..2e645248fd34 100644 --- a/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php +++ b/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php @@ -7,37 +7,59 @@ namespace Magento\InventoryCatalog\Model\SourceItemsSaveSynchronization; +use Magento\CatalogInventory\Api\Data\StockItemInterface; use Magento\CatalogInventory\Api\StockItemRepositoryInterface; use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory; use Magento\CatalogInventory\Model\Indexer\Stock\Processor; use Magento\CatalogInventory\Model\Spi\StockStateProviderInterface; -use Magento\Framework\App\ObjectManager; -use Magento\InventoryCatalog\Model\LegacySynchronization\Synchronize; +use Magento\CatalogInventory\Model\Stock; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface; use Magento\InventoryCatalog\Model\ResourceModel\SetDataToLegacyStockItem; /** * Set Qty and status for legacy CatalogInventory Stock Item table * @deprecated - * @see \Magento\InventoryCatalog\Model\LegacySynchronization\Synchronize */ class SetDataToLegacyCatalogInventory { /** - * @var Synchronize + * @var SetDataToLegacyStockItem */ - private $synchronize; + private $setDataToLegacyStockItem; /** - * @param SetDataToLegacyStockItem $setDataToLegacyStockItem @deprecated - * @param StockItemCriteriaInterfaceFactory $legacyStockItemCriteriaFactory @deprecated - * @param StockItemRepositoryInterface $legacyStockItemRepository @deprecated - * @param GetProductIdsBySkusInterface $getProductIdsBySkus @deprecated - * @param StockStateProviderInterface $stockStateProvider @deprecated - * @param Processor $indexerProcessor @deprecated - * @param Synchronize $synchronize - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * @SuppressWarnings(PHPMD.LongVariable) + * @var StockItemCriteriaInterfaceFactory + */ + private $legacyStockItemCriteriaFactory; + + /** + * @var StockItemRepositoryInterface + */ + private $legacyStockItemRepository; + + /** + * @var GetProductIdsBySkusInterface + */ + private $getProductIdsBySkus; + + /** + * @var StockStateProviderInterface + */ + private $stockStateProvider; + + /** + * @var Processor + */ + private $indexerProcessor; + + /** + * @param SetDataToLegacyStockItem $setDataToLegacyStockItem + * @param StockItemCriteriaInterfaceFactory $legacyStockItemCriteriaFactory + * @param StockItemRepositoryInterface $legacyStockItemRepository + * @param GetProductIdsBySkusInterface $getProductIdsBySkus + * @param StockStateProviderInterface $stockStateProvider + * @param Processor $indexerProcessor */ public function __construct( SetDataToLegacyStockItem $setDataToLegacyStockItem, @@ -45,25 +67,80 @@ public function __construct( StockItemRepositoryInterface $legacyStockItemRepository, GetProductIdsBySkusInterface $getProductIdsBySkus, StockStateProviderInterface $stockStateProvider, - Processor $indexerProcessor, - Synchronize $synchronize = null + Processor $indexerProcessor ) { - $this->alignLegacyCatalogInventoryByProducts = $synchronize ?: - ObjectManager::getInstance()->get(Synchronize::class); + $this->setDataToLegacyStockItem = $setDataToLegacyStockItem; + $this->legacyStockItemCriteriaFactory = $legacyStockItemCriteriaFactory; + $this->legacyStockItemRepository = $legacyStockItemRepository; + $this->getProductIdsBySkus = $getProductIdsBySkus; + $this->stockStateProvider = $stockStateProvider; + $this->indexerProcessor = $indexerProcessor; } /** * @param array $sourceItems * @return void - * @throws \Magento\Framework\Exception\LocalizedException */ public function execute(array $sourceItems): void { - $skus = []; + $productIds = []; foreach ($sourceItems as $sourceItem) { - $skus[] = $sourceItem->getSku(); + $sku = $sourceItem->getSku(); + + try { + $productId = (int)$this->getProductIdsBySkus->execute([$sku])[$sku]; + } catch (NoSuchEntityException $e) { + // Skip synchronization of for not existed product + continue; + } + + $legacyStockItem = $this->getLegacyStockItem($productId); + if (null === $legacyStockItem) { + continue; + } + + $isInStock = (int)$sourceItem->getStatus(); + + if ($legacyStockItem->getManageStock()) { + $legacyStockItem->setIsInStock($isInStock); + $legacyStockItem->setQty((float)$sourceItem->getQuantity()); + + if (false === $this->stockStateProvider->verifyStock($legacyStockItem)) { + $isInStock = 0; + } + } + + $this->setDataToLegacyStockItem->execute( + (string)$sourceItem->getSku(), + (float)$sourceItem->getQuantity(), + $isInStock + ); + $productIds[] = $productId; + } + + if ($productIds) { + $this->indexerProcessor->reindexList($productIds); + } + } + + /** + * @param int $productId + * @return null|StockItemInterface + */ + private function getLegacyStockItem(int $productId): ?StockItemInterface + { + $searchCriteria = $this->legacyStockItemCriteriaFactory->create(); + + $searchCriteria->addFilter(StockItemInterface::PRODUCT_ID, StockItemInterface::PRODUCT_ID, $productId); + $searchCriteria->addFilter(StockItemInterface::STOCK_ID, StockItemInterface::STOCK_ID, Stock::DEFAULT_STOCK_ID); + + $stockItemCollection = $this->legacyStockItemRepository->getList($searchCriteria); + if ($stockItemCollection->getTotalCount() === 0) { + return null; } - $this->synchronize->execute(Synchronize::DIRECTION_TO_LEGACY, $skus); + $stockItems = $stockItemCollection->getItems(); + $stockItem = reset($stockItems); + return $stockItem; } } From 52eb5df6891bcc127ad0ef7ca4605c3f126c25eb Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Fri, 22 Mar 2019 11:26:23 +0100 Subject: [PATCH 041/231] WIP refactor moving legacy sync to new module --- .../SetDataToLegacyCatalogInventory.php | 146 ------------------ ...UpdateSourceItemBasedOnLegacyStockItem.php | 91 ----------- InventoryCatalog/etc/config.xml | 3 - .../UpdateLegacyStockItemsData.php | 50 ++++++ .../ResourceModel/UpdateSourceItemsData.php | 50 ++++++ .../ToLegacy/SetDataToLegacyInventory.php | 69 +++++---- .../Model/ToMsi/SetDataToSourceItem.php | 107 ++++++++++--- ...atalogInventoryAtSourceItemsSavePlugin.php | 15 +- ...alogInventoryAtSourceItemsDeletePlugin.php | 60 +++---- ...eSourceItemAtLegacyStockItemSavePlugin.php | 13 +- ...ToLegacyStockItemAtSourceItemsSaveTest.php | 8 +- ...LegacyStockStatusAtSourceItemsSaveTest.php | 6 +- ...gacyStockStatusAtSourceItemsDeleteTest.php | 74 ++++++++- ...LegacyStockItemAtSourceItemsDeleteTest.php | 4 +- ...ultSourceItemAtLegacyStockItemSaveTest.php | 12 +- ...dateDefaultSourceItemAtProductSaveTest.php | 4 +- InventoryLegacySynchronization/composer.json | 2 + InventoryLegacySynchronization/etc/config.xml | 17 ++ InventoryLegacySynchronization/etc/module.xml | 2 +- .../composer.json | 22 +++ .../etc/adminhtml/system.xml | 25 +++ .../etc/module.xml | 12 ++ .../registration.php | 12 ++ 23 files changed, 435 insertions(+), 369 deletions(-) delete mode 100644 InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php delete mode 100644 InventoryCatalog/Model/UpdateSourceItemBasedOnLegacyStockItem.php create mode 100644 InventoryLegacySynchronization/Model/ResourceModel/UpdateLegacyStockItemsData.php create mode 100644 InventoryLegacySynchronization/Model/ResourceModel/UpdateSourceItemsData.php rename {InventoryCatalog => InventoryLegacySynchronization}/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php (96%) rename {InventoryCatalog => InventoryLegacySynchronization}/Test/Integration/SetDataToLegacyStockStatusAtSourceItemsSaveTest.php (96%) rename {InventoryCatalog => InventoryLegacySynchronization}/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php (54%) rename {InventoryCatalog => InventoryLegacySynchronization}/Test/Integration/SetToZeroLegacyStockItemAtSourceItemsDeleteTest.php (96%) rename {InventoryCatalog => InventoryLegacySynchronization}/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php (97%) rename {InventoryCatalog => InventoryLegacySynchronization}/Test/Integration/UpdateDefaultSourceItemAtProductSaveTest.php (95%) create mode 100644 InventoryLegacySynchronization/etc/config.xml create mode 100644 InventoryLegacySynchronizationAdminUi/composer.json create mode 100755 InventoryLegacySynchronizationAdminUi/etc/adminhtml/system.xml create mode 100644 InventoryLegacySynchronizationAdminUi/etc/module.xml create mode 100644 InventoryLegacySynchronizationAdminUi/registration.php diff --git a/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php b/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php deleted file mode 100644 index 2e645248fd34..000000000000 --- a/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php +++ /dev/null @@ -1,146 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryCatalog\Model\SourceItemsSaveSynchronization; - -use Magento\CatalogInventory\Api\Data\StockItemInterface; -use Magento\CatalogInventory\Api\StockItemRepositoryInterface; -use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory; -use Magento\CatalogInventory\Model\Indexer\Stock\Processor; -use Magento\CatalogInventory\Model\Spi\StockStateProviderInterface; -use Magento\CatalogInventory\Model\Stock; -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface; -use Magento\InventoryCatalog\Model\ResourceModel\SetDataToLegacyStockItem; - -/** - * Set Qty and status for legacy CatalogInventory Stock Item table - * @deprecated - */ -class SetDataToLegacyCatalogInventory -{ - /** - * @var SetDataToLegacyStockItem - */ - private $setDataToLegacyStockItem; - - /** - * @var StockItemCriteriaInterfaceFactory - */ - private $legacyStockItemCriteriaFactory; - - /** - * @var StockItemRepositoryInterface - */ - private $legacyStockItemRepository; - - /** - * @var GetProductIdsBySkusInterface - */ - private $getProductIdsBySkus; - - /** - * @var StockStateProviderInterface - */ - private $stockStateProvider; - - /** - * @var Processor - */ - private $indexerProcessor; - - /** - * @param SetDataToLegacyStockItem $setDataToLegacyStockItem - * @param StockItemCriteriaInterfaceFactory $legacyStockItemCriteriaFactory - * @param StockItemRepositoryInterface $legacyStockItemRepository - * @param GetProductIdsBySkusInterface $getProductIdsBySkus - * @param StockStateProviderInterface $stockStateProvider - * @param Processor $indexerProcessor - */ - public function __construct( - SetDataToLegacyStockItem $setDataToLegacyStockItem, - StockItemCriteriaInterfaceFactory $legacyStockItemCriteriaFactory, - StockItemRepositoryInterface $legacyStockItemRepository, - GetProductIdsBySkusInterface $getProductIdsBySkus, - StockStateProviderInterface $stockStateProvider, - Processor $indexerProcessor - ) { - $this->setDataToLegacyStockItem = $setDataToLegacyStockItem; - $this->legacyStockItemCriteriaFactory = $legacyStockItemCriteriaFactory; - $this->legacyStockItemRepository = $legacyStockItemRepository; - $this->getProductIdsBySkus = $getProductIdsBySkus; - $this->stockStateProvider = $stockStateProvider; - $this->indexerProcessor = $indexerProcessor; - } - - /** - * @param array $sourceItems - * @return void - */ - public function execute(array $sourceItems): void - { - $productIds = []; - foreach ($sourceItems as $sourceItem) { - $sku = $sourceItem->getSku(); - - try { - $productId = (int)$this->getProductIdsBySkus->execute([$sku])[$sku]; - } catch (NoSuchEntityException $e) { - // Skip synchronization of for not existed product - continue; - } - - $legacyStockItem = $this->getLegacyStockItem($productId); - if (null === $legacyStockItem) { - continue; - } - - $isInStock = (int)$sourceItem->getStatus(); - - if ($legacyStockItem->getManageStock()) { - $legacyStockItem->setIsInStock($isInStock); - $legacyStockItem->setQty((float)$sourceItem->getQuantity()); - - if (false === $this->stockStateProvider->verifyStock($legacyStockItem)) { - $isInStock = 0; - } - } - - $this->setDataToLegacyStockItem->execute( - (string)$sourceItem->getSku(), - (float)$sourceItem->getQuantity(), - $isInStock - ); - $productIds[] = $productId; - } - - if ($productIds) { - $this->indexerProcessor->reindexList($productIds); - } - } - - /** - * @param int $productId - * @return null|StockItemInterface - */ - private function getLegacyStockItem(int $productId): ?StockItemInterface - { - $searchCriteria = $this->legacyStockItemCriteriaFactory->create(); - - $searchCriteria->addFilter(StockItemInterface::PRODUCT_ID, StockItemInterface::PRODUCT_ID, $productId); - $searchCriteria->addFilter(StockItemInterface::STOCK_ID, StockItemInterface::STOCK_ID, Stock::DEFAULT_STOCK_ID); - - $stockItemCollection = $this->legacyStockItemRepository->getList($searchCriteria); - if ($stockItemCollection->getTotalCount() === 0) { - return null; - } - - $stockItems = $stockItemCollection->getItems(); - $stockItem = reset($stockItems); - return $stockItem; - } -} diff --git a/InventoryCatalog/Model/UpdateSourceItemBasedOnLegacyStockItem.php b/InventoryCatalog/Model/UpdateSourceItemBasedOnLegacyStockItem.php deleted file mode 100644 index a90c60298eac..000000000000 --- a/InventoryCatalog/Model/UpdateSourceItemBasedOnLegacyStockItem.php +++ /dev/null @@ -1,91 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryCatalog\Model; - -use Magento\CatalogInventory\Model\Stock\Item; -use Magento\InventoryApi\Api\Data\SourceItemInterface; -use Magento\InventoryApi\Api\Data\SourceItemInterfaceFactory; -use Magento\InventoryApi\Api\SourceItemsSaveInterface; -use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; -use Magento\InventoryCatalogApi\Model\GetSkusByProductIdsInterface; - -class UpdateSourceItemBasedOnLegacyStockItem -{ - /** - * @var SourceItemInterfaceFactory - */ - private $sourceItemFactory; - - /** - * @var SourceItemsSaveInterface - */ - private $sourceItemsSave; - - /** - * @var DefaultSourceProviderInterface - */ - private $defaultSourceProvider; - - /** - * @var GetSkusByProductIdsInterface - */ - private $getSkusByProductIds; - - /** - * @var GetDefaultSourceItemBySku - */ - private $getDefaultSourceItemBySku; - - /** - * @param SourceItemInterfaceFactory $sourceItemFactory - * @param SourceItemsSaveInterface $sourceItemsSave - * @param DefaultSourceProviderInterface $defaultSourceProvider - * @param GetSkusByProductIdsInterface $getSkusByProductIds - * @param GetDefaultSourceItemBySku $getDefaultSourceItemBySku - * @SuppressWarnings(PHPMD.LongVariable) - */ - public function __construct( - SourceItemInterfaceFactory $sourceItemFactory, - SourceItemsSaveInterface $sourceItemsSave, - DefaultSourceProviderInterface $defaultSourceProvider, - GetSkusByProductIdsInterface $getSkusByProductIds, - GetDefaultSourceItemBySku $getDefaultSourceItemBySku - ) { - $this->sourceItemFactory = $sourceItemFactory; - $this->sourceItemsSave = $sourceItemsSave; - $this->getSkusByProductIds = $getSkusByProductIds; - $this->getDefaultSourceItemBySku = $getDefaultSourceItemBySku; - $this->defaultSourceProvider = $defaultSourceProvider; - } - - /** - * @param Item $legacyStockItem - * @return void - * @throws \Magento\Framework\Exception\CouldNotSaveException - * @throws \Magento\Framework\Exception\InputException - * @throws \Magento\Framework\Validation\ValidationException - */ - public function execute(Item $legacyStockItem) - { - $productSku = $this->getSkusByProductIds - ->execute([$legacyStockItem->getProductId()])[$legacyStockItem->getProductId()]; - - $sourceItem = $this->getDefaultSourceItemBySku->execute($productSku); - if ($sourceItem === null) { - /** @var SourceItemInterface $sourceItem */ - $sourceItem = $this->sourceItemFactory->create(); - $sourceItem->setSourceCode($this->defaultSourceProvider->getCode()); - $sourceItem->setSku($productSku); - } - - $sourceItem->setQuantity((float)$legacyStockItem->getQty()); - $sourceItem->setStatus((int)$legacyStockItem->getIsInStock()); - - $this->sourceItemsSave->execute([$sourceItem]); - } -} diff --git a/InventoryCatalog/etc/config.xml b/InventoryCatalog/etc/config.xml index 3c1403a83eff..1e763271d00e 100644 --- a/InventoryCatalog/etc/config.xml +++ b/InventoryCatalog/etc/config.xml @@ -9,9 +9,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> <default> <cataloginventory> - <legacy_stock> - <async>0</async> - </legacy_stock> <bulk_operations> <async>0</async> <batch_size>100</batch_size> diff --git a/InventoryLegacySynchronization/Model/ResourceModel/UpdateLegacyStockItemsData.php b/InventoryLegacySynchronization/Model/ResourceModel/UpdateLegacyStockItemsData.php new file mode 100644 index 000000000000..67cd14ff5b1a --- /dev/null +++ b/InventoryLegacySynchronization/Model/ResourceModel/UpdateLegacyStockItemsData.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\InventoryLegacySynchronization\Model\ResourceModel; + +use Magento\CatalogInventory\Api\Data\StockItemInterface; +use Magento\Framework\App\ResourceConnection; + +/** + * Update legacy stock items data in one single database operation + */ +class UpdateLegacyStockItemsData +{ + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @param ResourceConnection $resourceConnection + */ + public function __construct( + ResourceConnection $resourceConnection + ) { + $this->resourceConnection = $resourceConnection; + } + + /** + * @param array $legacyItemsData + */ + public function execute(array $legacyItemsData): void + { + $tableName = $this->resourceConnection->getTableName('cataloginventory_stock_item'); + + $connection = $this->resourceConnection->getConnection(); + $connection->insertOnDuplicate( + $tableName, + $legacyItemsData, + [ + StockItemInterface::IS_IN_STOCK, + StockItemInterface::QTY, + ] + ); + } +} diff --git a/InventoryLegacySynchronization/Model/ResourceModel/UpdateSourceItemsData.php b/InventoryLegacySynchronization/Model/ResourceModel/UpdateSourceItemsData.php new file mode 100644 index 000000000000..1dd068dc94c0 --- /dev/null +++ b/InventoryLegacySynchronization/Model/ResourceModel/UpdateSourceItemsData.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\InventoryLegacySynchronization\Model\ResourceModel; + +use Magento\Framework\App\ResourceConnection; +use Magento\InventoryApi\Api\Data\SourceItemInterface; + +/** + * Update source items data in one single database operation + */ +class UpdateSourceItemsData +{ + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @param ResourceConnection $resourceConnection + */ + public function __construct( + ResourceConnection $resourceConnection + ) { + $this->resourceConnection = $resourceConnection; + } + + /** + * @param array $sourceItemsData + */ + public function execute(array $sourceItemsData): void + { + $tableName = $this->resourceConnection->getTableName('inventory_source_item'); + + $connection = $this->resourceConnection->getConnection(); + $connection->insertOnDuplicate( + $tableName, + $sourceItemsData, + [ + SourceItemInterface::QUANTITY, + SourceItemInterface::STATUS, + ] + ); + } +} diff --git a/InventoryLegacySynchronization/Model/ToLegacy/SetDataToLegacyInventory.php b/InventoryLegacySynchronization/Model/ToLegacy/SetDataToLegacyInventory.php index 62f132817a68..7531b00322b0 100644 --- a/InventoryLegacySynchronization/Model/ToLegacy/SetDataToLegacyInventory.php +++ b/InventoryLegacySynchronization/Model/ToLegacy/SetDataToLegacyInventory.php @@ -7,23 +7,22 @@ namespace Magento\InventoryLegacySynchronization\Model\ToLegacy; +use Magento\Catalog\Model\ResourceModel\Product as ProductResourceModel; +use Magento\CatalogInventory\Api\Data\StockItemInterface; use Magento\CatalogInventory\Model\Indexer\Stock\Processor; use Magento\CatalogInventory\Model\Spi\StockStateProviderInterface; use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Exception\NoSuchEntityException; use Magento\InventoryApi\Api\Data\SourceItemInterface; use Magento\InventoryCatalog\Model\GetProductIdsBySkus; use Magento\InventoryLegacySynchronization\Model\GetLegacyStockItemsByProductIds; -use Magento\InventoryCatalog\Model\ResourceModel\SetDataToLegacyStockItem; use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; +use Magento\InventoryLegacySynchronization\Model\ResourceModel\UpdateLegacyStockItemsData; +/** + * Copy source item information to legacy stock + */ class SetDataToLegacyInventory { - /** - * @var SetDataToLegacyStockItem - */ - private $setDataToLegacyStockItem; - /** * @var GetLegacyStockItemsByProductIds */ @@ -42,7 +41,7 @@ class SetDataToLegacyInventory /** * @var GetProductIdsBySkus */ - private $getProductIdsBySkus; + private $productResourceModel; /** * @var DefaultSourceProviderInterface @@ -50,29 +49,33 @@ class SetDataToLegacyInventory private $defaultSourceProvider; /** - * SetDataToLegacyCatalogInventory constructor. + * @var UpdateLegacyStockItemsData + */ + private $updateLegacyStockItemsData; + + /** + * @param UpdateLegacyStockItemsData $updateLegacyStockItemsData * @param DefaultSourceProviderInterface $defaultSourceProvider - * @param SetDataToLegacyStockItem $setDataToLegacyStockItem * @param GetLegacyStockItemsByProductIds $getLegacyStockItemsByProductIds * @param StockStateProviderInterface $stockStateProvider * @param Processor $indexerProcessor - * @param GetProductIdsBySkus $getProductIdsBySkus + * @param ProductResourceModel $productResourceModel * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( + UpdateLegacyStockItemsData $updateLegacyStockItemsData, DefaultSourceProviderInterface $defaultSourceProvider, - SetDataToLegacyStockItem $setDataToLegacyStockItem, GetLegacyStockItemsByProductIds $getLegacyStockItemsByProductIds, StockStateProviderInterface $stockStateProvider, Processor $indexerProcessor, - GetProductIdsBySkus $getProductIdsBySkus + ProductResourceModel $productResourceModel ) { - $this->setDataToLegacyStockItem = $setDataToLegacyStockItem; $this->getLegacyStockItemsByProductIds = $getLegacyStockItemsByProductIds; $this->stockStateProvider = $stockStateProvider; $this->indexerProcessor = $indexerProcessor; - $this->getProductIdsBySkus = $getProductIdsBySkus; + $this->productResourceModel = $productResourceModel; $this->defaultSourceProvider = $defaultSourceProvider; + $this->updateLegacyStockItemsData = $updateLegacyStockItemsData; } /** @@ -80,10 +83,17 @@ public function __construct( * * @param array $sourceItemsData * @throws LocalizedException + * @SuppressWarnings(PHPMD.LongVariable) */ public function execute(array $sourceItemsData): void { - $productIds = []; + $productSkus = array_column($sourceItemsData, SourceItemInterface::SKU); + $productIdsBySku = $this->productResourceModel->getProductsIdsBySkus($productSkus); + + $legacyStockItemsByProductsIds = + $this->getLegacyStockItemsByProductIds->execute(array_values($productIdsBySku)); + + $legacyItemsToUpdateData = []; foreach ($sourceItemsData as $sourceItemData) { $sku = (string) $sourceItemData[SourceItemInterface::SKU]; @@ -91,18 +101,17 @@ public function execute(array $sourceItemsData): void throw new LocalizedException(__('Only default source can synchronize legacy stock')); } - try { - $productId = (int) current($this->getProductIdsBySkus->execute([$sku])); - } catch (NoSuchEntityException $e) { + if (!isset($productIdsBySku[$sku])) { continue; } - $legacyStockItems = $this->getLegacyStockItemsByProductIds->execute([$productId]); - if (!isset($legacyStockItems[$productId])) { + $productId = $productIdsBySku[$sku]; + + if (!isset($legacyStockItemsByProductsIds[$productId])) { continue; } - $legacyStockItem = $legacyStockItems[$productId]; + $legacyStockItem = $legacyStockItemsByProductsIds[$productId]; $isInStock = (int) $sourceItemData[SourceItemInterface::STATUS]; $quantity = (float) $sourceItemData[SourceItemInterface::QUANTITY]; @@ -115,15 +124,21 @@ public function execute(array $sourceItemsData): void } } - $this->setDataToLegacyStockItem->execute( - $sku, - $quantity, - $isInStock - ); + $legacyItemsToUpdateData[] = [ + StockItemInterface::QTY => $quantity, + StockItemInterface::IS_IN_STOCK => $isInStock, + StockItemInterface::PRODUCT_ID => $productId, + 'stock_id' => 1, + 'website_id' => 0, + ]; $productIds[] = $productId; } + if (!empty($legacyItemsToUpdateData)) { + $this->updateLegacyStockItemsData->execute($legacyItemsToUpdateData); + } + if (!empty($productIds)) { $this->indexerProcessor->reindexList($productIds); } diff --git a/InventoryLegacySynchronization/Model/ToMsi/SetDataToSourceItem.php b/InventoryLegacySynchronization/Model/ToMsi/SetDataToSourceItem.php index 35e694a1f4cf..3b112ae3be4a 100644 --- a/InventoryLegacySynchronization/Model/ToMsi/SetDataToSourceItem.php +++ b/InventoryLegacySynchronization/Model/ToMsi/SetDataToSourceItem.php @@ -7,47 +7,118 @@ namespace Magento\InventoryLegacySynchronization\Model\ToMsi; -use Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory; -use Magento\InventoryCatalog\Model\UpdateSourceItemBasedOnLegacyStockItem; +use Magento\Catalog\Model\ResourceModel\Product as ProductResourceModel; +use Magento\CatalogInventory\Api\Data\StockItemInterface; +use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; +use Magento\InventoryIndexer\Indexer\SourceItem\SourceItemIndexer; +use Magento\InventoryLegacySynchronization\Model\GetDefaultSourceItemsBySkus; +use Magento\InventoryLegacySynchronization\Model\GetLegacyStockItemsByProductIds; +use Magento\InventoryLegacySynchronization\Model\ResourceModel\UpdateSourceItemsData; +/** + * Copy legacy stock item information to MSI source items + */ class SetDataToSourceItem { /** - * @var UpdateSourceItemBasedOnLegacyStockItem + * @var UpdateSourceItemsData + */ + private $updateSourceItemsData; + + /** + * @var DefaultSourceProviderInterface + */ + private $defaultSourceProvider; + + /** + * @var GetLegacyStockItemsByProductIds + */ + private $getLegacyStockItemsByProductIds; + + /** + * @var SourceItemIndexer + */ + private $sourceItemIndexer; + + /** + * @var GetDefaultSourceItemsBySkus */ - private $updateSourceItemBasedOnLegacyStockItem; + private $getDefaultSourceItemsBySkus; /** - * @var StockItemInterfaceFactory + * @var ProductResourceModel */ - private $stockItemInterfaceFactory; + private $productResourceModel; /** - * SetDataToSourceItem constructor. - * @param UpdateSourceItemBasedOnLegacyStockItem $updateSourceItemBasedOnLegacyStockItem - * @param StockItemInterfaceFactory $stockItemInterfaceFactory + * @param UpdateSourceItemsData $updateSourceItemsData + * @param GetDefaultSourceItemsBySkus $getDefaultSourceItemsBySkus + * @param GetLegacyStockItemsByProductIds $getLegacyStockItemsByProductIds + * @param DefaultSourceProviderInterface $defaultSourceProvider + * @param SourceItemIndexer $sourceItemIndexer + * @param ProductResourceModel $productResourceModel * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( - UpdateSourceItemBasedOnLegacyStockItem $updateSourceItemBasedOnLegacyStockItem, - StockItemInterfaceFactory $stockItemInterfaceFactory + UpdateSourceItemsData $updateSourceItemsData, + GetDefaultSourceItemsBySkus $getDefaultSourceItemsBySkus, + GetLegacyStockItemsByProductIds $getLegacyStockItemsByProductIds, + DefaultSourceProviderInterface $defaultSourceProvider, + SourceItemIndexer $sourceItemIndexer, + ProductResourceModel $productResourceModel ) { - $this->updateSourceItemBasedOnLegacyStockItem = $updateSourceItemBasedOnLegacyStockItem; - $this->stockItemInterfaceFactory = $stockItemInterfaceFactory; + $this->updateSourceItemsData = $updateSourceItemsData; + $this->defaultSourceProvider = $defaultSourceProvider; + $this->getLegacyStockItemsByProductIds = $getLegacyStockItemsByProductIds; + $this->sourceItemIndexer = $sourceItemIndexer; + $this->getDefaultSourceItemsBySkus = $getDefaultSourceItemsBySkus; + $this->productResourceModel = $productResourceModel; } /** * @param array $legacyItemsData - * @throws \Magento\Framework\Exception\CouldNotSaveException - * @throws \Magento\Framework\Exception\InputException * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Magento\Framework\Validation\ValidationException + * @SuppressWarnings(PHPMD.LongVariable) */ public function execute(array $legacyItemsData): void { + $sourceItemsData = []; + $productIds = array_column($legacyItemsData, StockItemInterface::PRODUCT_ID); + $defaultSourceCode = $this->defaultSourceProvider->getCode(); + + $legacyStockItemsByProductsIds = + $this->getLegacyStockItemsByProductIds->execute(array_values($productIds)); + + $productSkus = $this->productResourceModel->getProductsSku($productIds); + $productSkusById = array_combine( + array_column($productSkus, 'entity_id'), + array_column($productSkus, 'sku') + ); + foreach ($legacyItemsData as $legacyItemData) { - $stockItem = $this->stockItemInterfaceFactory->create(['data' => $legacyItemData]); - $this->updateSourceItemBasedOnLegacyStockItem->execute($stockItem); + $productId = (int) $legacyItemData[StockItemInterface::PRODUCT_ID]; + $productSku = $productSkusById[$productId]; + + /** @var StockItemInterface $legacyStockItem */ + $legacyStockItem = $legacyStockItemsByProductsIds[$productId]; + + $sourceItemsData[] = [ + SourceItemInterface::SOURCE_CODE => $defaultSourceCode, + SourceItemInterface::SKU => $productSku, + SourceItemInterface::QUANTITY => (float) $legacyStockItem->getQty(), + SourceItemInterface::STATUS => (int) $legacyStockItem->getIsInStock(), + ]; + } + + $this->updateSourceItemsData->execute($sourceItemsData); + + $sourceItemsToReindex = $this->getDefaultSourceItemsBySkus->execute(array_values($productSkusById)); + $sourceItemsIdsToReindex = []; + foreach ($sourceItemsToReindex as $sourceItemToReindex) { + $sourceItemsIdsToReindex[] = (int) $sourceItemToReindex->getId(); } + + $this->sourceItemIndexer->executeList($sourceItemsIdsToReindex); } } diff --git a/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php b/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php index 37bcc4727102..6d004b8d1818 100644 --- a/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php +++ b/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php @@ -7,11 +7,9 @@ namespace Magento\InventoryLegacySynchronization\Plugin; -use Magento\Framework\App\ObjectManager; use Magento\InventoryApi\Api\Data\SourceItemInterface; use Magento\InventoryApi\Api\SourceItemsSaveInterface; use Magento\InventoryLegacySynchronization\Model\Synchronize; -use Magento\InventoryCatalog\Model\SourceItemsSaveSynchronization\SetDataToLegacyCatalogInventory; use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; use Magento\InventoryCatalogApi\Model\GetProductTypesBySkusInterface; use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForProductTypeInterface; @@ -42,10 +40,9 @@ class SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin private $synchronize; /** - * @param DefaultSourceProviderInterface $defaultSourceProvider @deprecated - * @param IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemsAllowedForProductType @deprecated - * @param GetProductTypesBySkusInterface $getProductTypeBySku @deprecated - * @param SetDataToLegacyCatalogInventory $setDataToLegacyCatalogInventory @deprecated + * @param DefaultSourceProviderInterface $defaultSourceProvider + * @param IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemsAllowedForProductType + * @param GetProductTypesBySkusInterface $getProductTypeBySku * @param Synchronize|null $synchronize * @SuppressWarnings(PHPMD.LongVariable) * @SuppressWarnings(PHPMD.UnusedFormalParameter) @@ -54,14 +51,12 @@ public function __construct( DefaultSourceProviderInterface $defaultSourceProvider, IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemsAllowedForProductType, GetProductTypesBySkusInterface $getProductTypeBySku, - SetDataToLegacyCatalogInventory $setDataToLegacyCatalogInventory, - Synchronize $synchronize = null + Synchronize $synchronize ) { $this->defaultSourceProvider = $defaultSourceProvider; $this->isSourceItemsAllowedForProductType = $isSourceItemsAllowedForProductType; $this->getProductTypeBySku = $getProductTypeBySku; - $this->synchronize = $synchronize ?: - ObjectManager::getInstance()->get(Synchronize::class); + $this->synchronize = $synchronize; } /** diff --git a/InventoryLegacySynchronization/Plugin/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php b/InventoryLegacySynchronization/Plugin/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php index c29964abaaa7..caedbf36ccdf 100644 --- a/InventoryLegacySynchronization/Plugin/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php +++ b/InventoryLegacySynchronization/Plugin/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php @@ -7,15 +7,12 @@ namespace Magento\InventoryLegacySynchronization\Plugin; -use Magento\CatalogInventory\Model\Indexer\Stock\Processor; -use Magento\Framework\Exception\NoSuchEntityException; use Magento\InventoryApi\Api\Data\SourceItemInterface; use Magento\InventoryApi\Api\SourceItemsDeleteInterface; use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; -use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface; use Magento\InventoryCatalogApi\Model\GetProductTypesBySkusInterface; -use Magento\InventoryCatalog\Model\ResourceModel\SetDataToLegacyStockItem; use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForProductTypeInterface; +use Magento\InventoryLegacySynchronization\Model\Synchronize; /** * Set to zero Qty and status to ‘Out of Stock’ for legacy CatalogInventory Stock Status and Stock Item DB tables, @@ -28,21 +25,6 @@ class SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin */ private $defaultSourceProvider; - /** - * @var SetDataToLegacyStockItem - */ - private $setDataToLegacyStockItem; - - /** - * @var GetProductIdsBySkusInterface - */ - private $getProductIdsBySkus; - - /** - * @var Processor - */ - private $indexerProcessor; - /** * @var IsSourceItemManagementAllowedForProductTypeInterface */ @@ -53,29 +35,28 @@ class SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin */ private $getProductTypeBySku; + /** + * @var Synchronize + */ + private $synchronize; + /** * @param DefaultSourceProviderInterface $defaultSourceProvider - * @param SetDataToLegacyStockItem $setDataToLegacyStockItem - * @param GetProductIdsBySkusInterface $getProductIdsBySkus - * @param Processor $indexerProcessor * @param IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemsAllowedForProductType * @param GetProductTypesBySkusInterface $getProductTypeBySku + * @param Synchronize $synchronize * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( DefaultSourceProviderInterface $defaultSourceProvider, - SetDataToLegacyStockItem $setDataToLegacyStockItem, - GetProductIdsBySkusInterface $getProductIdsBySkus, - Processor $indexerProcessor, IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemsAllowedForProductType, - GetProductTypesBySkusInterface $getProductTypeBySku + GetProductTypesBySkusInterface $getProductTypeBySku, + Synchronize $synchronize ) { $this->defaultSourceProvider = $defaultSourceProvider; - $this->setDataToLegacyStockItem = $setDataToLegacyStockItem; - $this->getProductIdsBySkus = $getProductIdsBySkus; - $this->indexerProcessor = $indexerProcessor; $this->isSourceItemsAllowedForProductType = $isSourceItemsAllowedForProductType; $this->getProductTypeBySku = $getProductTypeBySku; + $this->synchronize = $synchronize; } /** @@ -83,11 +64,12 @@ public function __construct( * @param void $result * @param SourceItemInterface[] $sourceItems * @return void + * @throws \Magento\Framework\Exception\LocalizedException * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function afterExecute(SourceItemsDeleteInterface $subject, $result, array $sourceItems) { - $productIds = []; + $sourceItemsData = []; foreach ($sourceItems as $sourceItem) { if ($sourceItem->getSourceCode() !== $this->defaultSourceProvider->getCode()) { continue; @@ -95,24 +77,20 @@ public function afterExecute(SourceItemsDeleteInterface $subject, $result, array $sku = $sourceItem->getSku(); - try { - $productId = (int)$this->getProductIdsBySkus->execute([$sku])[$sku]; - } catch (NoSuchEntityException $e) { - // Delete source item data for not existed product - continue; - } - $typeId = $this->getProductTypeBySku->execute([$sku])[$sku]; if (false === $this->isSourceItemsAllowedForProductType->execute($typeId)) { continue; } - $this->setDataToLegacyStockItem->execute($sourceItem->getSku(), 0, 0); - $productIds[] = $productId; + $sourceItemData = $sourceItem->getData(); + $sourceItemData[SourceItemInterface::STATUS] = SourceItemInterface::STATUS_OUT_OF_STOCK; + $sourceItemData[SourceItemInterface::QUANTITY] = 0; + + $sourceItemsData[] = $sourceItemData; } - if ($productIds) { - $this->indexerProcessor->reindexList($productIds); + if (!empty($sourceItemsData)) { + $this->synchronize->execute(Synchronize::MSI_TO_LEGACY, $sourceItemsData); } } } diff --git a/InventoryLegacySynchronization/Plugin/UpdateSourceItemAtLegacyStockItemSavePlugin.php b/InventoryLegacySynchronization/Plugin/UpdateSourceItemAtLegacyStockItemSavePlugin.php index 78965c765470..1bc170a3d0ef 100755 --- a/InventoryLegacySynchronization/Plugin/UpdateSourceItemAtLegacyStockItemSavePlugin.php +++ b/InventoryLegacySynchronization/Plugin/UpdateSourceItemAtLegacyStockItemSavePlugin.php @@ -9,14 +9,12 @@ use Magento\CatalogInventory\Model\ResourceModel\Stock\Item as ItemResourceModel; use Magento\CatalogInventory\Model\Stock\Item; -use Magento\Framework\App\ObjectManager; use Magento\Framework\App\ResourceConnection; use Magento\Framework\Model\AbstractModel; use Magento\InventoryCatalog\Model\GetDefaultSourceItemBySku; use Magento\InventoryLegacySynchronization\Model\Synchronize; use Magento\InventoryCatalogApi\Model\GetProductTypesBySkusInterface; use Magento\InventoryCatalogApi\Model\GetSkusByProductIdsInterface; -use Magento\InventoryCatalog\Model\UpdateSourceItemBasedOnLegacyStockItem; use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForProductTypeInterface; /** @@ -56,7 +54,6 @@ class UpdateSourceItemAtLegacyStockItemSavePlugin private $synchronize; /** - * @param UpdateSourceItemBasedOnLegacyStockItem $updateSourceItemBasedOnLegacyStockItem @deprecated * @param ResourceConnection $resourceConnection * @param IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemManagementAllowedForProductType * @param GetProductTypesBySkusInterface $getProductTypeBySku @@ -65,21 +62,19 @@ class UpdateSourceItemAtLegacyStockItemSavePlugin * @param Synchronize $synchronize */ public function __construct( - UpdateSourceItemBasedOnLegacyStockItem $updateSourceItemBasedOnLegacyStockItem, ResourceConnection $resourceConnection, IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemManagementAllowedForProductType, GetProductTypesBySkusInterface $getProductTypeBySku, GetSkusByProductIdsInterface $getSkusByProductIds, GetDefaultSourceItemBySku $getDefaultSourceItemBySku, - Synchronize $synchronize = null + Synchronize $synchronize ) { $this->resourceConnection = $resourceConnection; $this->isSourceItemManagementAllowedForProductType = $isSourceItemManagementAllowedForProductType; $this->getProductTypeBySku = $getProductTypeBySku; $this->getSkusByProductIds = $getSkusByProductIds; $this->getDefaultSourceItemBySku = $getDefaultSourceItemBySku; - $this->synchronize = $synchronize ?: - ObjectManager::getInstance()->get(Synchronize::class); + $this->synchronize = $synchronize; } /** @@ -135,7 +130,7 @@ private function getProductSkuById(int $productId): string * Return true if legacy stock item should update default source (if existing) * @param Item $legacyStockItem * @return bool - * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\NoSuchEntityException */ private function shouldAlignDefaultSourceWithLegacy(Item $legacyStockItem): bool { @@ -151,7 +146,7 @@ private function shouldAlignDefaultSourceWithLegacy(Item $legacyStockItem): bool /** * @param Item $legacyStockItem * @return string - * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\NoSuchEntityException */ private function getTypeId(Item $legacyStockItem): string { diff --git a/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php b/InventoryLegacySynchronization/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php similarity index 96% rename from InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php rename to InventoryLegacySynchronization/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php index a5c3561c94ff..80306d1d2701 100644 --- a/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php +++ b/InventoryLegacySynchronization/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Test\Integration; +namespace Magento\InventoryLegacySynchronization\Test\Integration; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Framework\Api\SearchCriteriaBuilder; @@ -79,7 +79,7 @@ protected function setUp() $this->defaultSourceProvider = Bootstrap::getObjectManager()->get(DefaultSourceProviderInterface::class); $this->consumer = Bootstrap::getObjectManager()->create(ConsumerFactory::class) - ->get('legacyCatalogInventorySynchronization', 100); + ->get('legacyInventorySynchronization', 100); } /** @@ -88,7 +88,7 @@ protected function setUp() * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php * @SuppressWarnings(PHPMD.LongVariable) */ - public function testSetData() + public function testShouldSynchronizeLegacyStock(): void { $productSku = 'SKU-1'; $product = $this->productRepository->get($productSku); @@ -133,7 +133,7 @@ public function testSetData() * @magentoAdminConfigFixture cataloginventory/legacy_stock/async 1 * @SuppressWarnings(PHPMD.LongVariable) */ - public function testSetDataAsynchronously() + public function testShouldSynchronizeLegacyStockAsynchronously(): void { $productSku = 'SKU-1'; $product = $this->productRepository->get($productSku); diff --git a/InventoryCatalog/Test/Integration/SetDataToLegacyStockStatusAtSourceItemsSaveTest.php b/InventoryLegacySynchronization/Test/Integration/SetDataToLegacyStockStatusAtSourceItemsSaveTest.php similarity index 96% rename from InventoryCatalog/Test/Integration/SetDataToLegacyStockStatusAtSourceItemsSaveTest.php rename to InventoryLegacySynchronization/Test/Integration/SetDataToLegacyStockStatusAtSourceItemsSaveTest.php index b3af298aec6d..44eb375469fa 100644 --- a/InventoryCatalog/Test/Integration/SetDataToLegacyStockStatusAtSourceItemsSaveTest.php +++ b/InventoryLegacySynchronization/Test/Integration/SetDataToLegacyStockStatusAtSourceItemsSaveTest.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Test\Integration; +namespace Magento\InventoryLegacySynchronization\Test\Integration; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\CatalogInventory\Api\StockStatusCriteriaInterface; @@ -78,7 +78,7 @@ protected function setUp() * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php */ - public function testSetData() + public function testShouldSetDataToLegacyOnSourceItemSave(): void { $productSku = 'SKU-1'; $product = $this->productRepository->get($productSku); @@ -104,7 +104,7 @@ public function testSetData() self::assertCount(1, $sourceItems); $sourceItem = reset($sourceItems); - $sourceItem->setQuantity(20); + $sourceItem->setQuantity(20.0); $sourceItem->setStatus(SourceItemInterface::STATUS_OUT_OF_STOCK); $this->sourceItemsSave->execute($sourceItems); diff --git a/InventoryCatalog/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php b/InventoryLegacySynchronization/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php similarity index 54% rename from InventoryCatalog/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php rename to InventoryLegacySynchronization/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php index 443f503c471d..55fd8392d5fb 100644 --- a/InventoryCatalog/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php +++ b/InventoryLegacySynchronization/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Test\Integration; +namespace Magento\InventoryLegacySynchronization\Test\Integration; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\CatalogInventory\Api\StockStatusCriteriaInterface; @@ -13,6 +13,8 @@ use Magento\CatalogInventory\Api\StockStatusRepositoryInterface; use Magento\CatalogInventory\Model\Stock\Status; use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\MessageQueue\Consumer; +use Magento\Framework\MessageQueue\ConsumerFactory; use Magento\InventoryApi\Api\Data\SourceItemInterface; use Magento\InventoryApi\Api\SourceItemRepositoryInterface; use Magento\InventoryApi\Api\SourceItemsDeleteInterface; @@ -57,6 +59,11 @@ class SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest extends TestCase */ private $defaultSourceProvider; + /** + * @var Consumer + */ + private $consumer; + protected function setUp() { $this->productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); @@ -71,6 +78,9 @@ protected function setUp() $this->sourceItemsDelete = Bootstrap::getObjectManager()->get(SourceItemsDeleteInterface::class); $this->defaultSourceProvider = Bootstrap::getObjectManager()->get(DefaultSourceProviderInterface::class); + + $this->consumer = Bootstrap::getObjectManager()->create(ConsumerFactory::class) + ->get('legacyInventorySynchronization', 100); } /** @@ -78,7 +88,7 @@ protected function setUp() * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php */ - public function testSetOutOfStock() + public function testShouldSetOutOfStockOnDelete(): void { $productSku = 'SKU-1'; $product = $this->productRepository->get($productSku); @@ -93,8 +103,8 @@ public function testSetOutOfStock() self::assertCount(1, $legacyStockStatuses); $legacyStockStatus = reset($legacyStockStatuses); - self::assertEquals(Status::STATUS_IN_STOCK, $legacyStockStatus->getStockStatus()); - self::assertEquals(5.5, $legacyStockStatus->getQty()); + self::assertSame(Status::STATUS_IN_STOCK, $legacyStockStatus->getStockStatus()); + self::assertSame(5.5, (float) $legacyStockStatus->getQty()); $searchCriteria = $this->searchCriteriaBuilder ->addFilter(SourceItemInterface::SKU, $productSku) @@ -109,7 +119,59 @@ public function testSetOutOfStock() self::assertCount(1, $legacyStockStatuses); $legacyStockStatus = reset($legacyStockStatuses); - self::assertEquals(Status::STATUS_OUT_OF_STOCK, $legacyStockStatus->getStockStatus()); - self::assertEquals(0, $legacyStockStatus->getQty()); + self::assertSame(Status::STATUS_OUT_OF_STOCK, $legacyStockStatus->getStockStatus()); + self::assertSame(0.0, (float) $legacyStockStatus->getQty()); + } + + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php + * @magentoAdminConfigFixture cataloginventory/legacy_stock/async 1 + */ + public function testShouldSetOutOfStockOnDeleteAsynchronously(): void + { + $productSku = 'SKU-1'; + $product = $this->productRepository->get($productSku); + $productId = $product->getId(); + $websiteId = 0; + + /** @var StockStatusCriteriaInterface $legacyStockStatusCriteria */ + $legacyStockStatusCriteria = $this->legacyStockStatusCriteriaFactory->create(); + $legacyStockStatusCriteria->setProductsFilter($productId); + $legacyStockStatusCriteria->setScopeFilter($websiteId); + $legacyStockStatuses = $this->legacyStockStatusRepository->getList($legacyStockStatusCriteria)->getItems(); + self::assertCount(1, $legacyStockStatuses); + + $legacyStockStatus = reset($legacyStockStatuses); + self::assertSame(Status::STATUS_IN_STOCK, $legacyStockStatus->getStockStatus()); + self::assertSame(5.5, (float) $legacyStockStatus->getQty()); + + $searchCriteria = $this->searchCriteriaBuilder + ->addFilter(SourceItemInterface::SKU, $productSku) + ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) + ->create(); + $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); + self::assertCount(1, $sourceItems); + + $this->sourceItemsDelete->execute($sourceItems); + + // Before sync + $legacyStockStatuses = $this->legacyStockStatusRepository->getList($legacyStockStatusCriteria)->getItems(); + self::assertCount(1, $legacyStockStatuses); + + $legacyStockStatus = reset($legacyStockStatuses); + self::assertSame(Status::STATUS_IN_STOCK, $legacyStockStatus->getStockStatus()); + self::assertSame(5.5, (float) $legacyStockStatus->getQty()); + + $this->consumer->process(1); + + // After sync + $legacyStockStatuses = $this->legacyStockStatusRepository->getList($legacyStockStatusCriteria)->getItems(); + self::assertCount(1, $legacyStockStatuses); + + $legacyStockStatus = reset($legacyStockStatuses); + self::assertSame(Status::STATUS_OUT_OF_STOCK, $legacyStockStatus->getStockStatus()); + self::assertSame(0.0, (float) $legacyStockStatus->getQty()); } } diff --git a/InventoryCatalog/Test/Integration/SetToZeroLegacyStockItemAtSourceItemsDeleteTest.php b/InventoryLegacySynchronization/Test/Integration/SetToZeroLegacyStockItemAtSourceItemsDeleteTest.php similarity index 96% rename from InventoryCatalog/Test/Integration/SetToZeroLegacyStockItemAtSourceItemsDeleteTest.php rename to InventoryLegacySynchronization/Test/Integration/SetToZeroLegacyStockItemAtSourceItemsDeleteTest.php index b42984a2dc9f..d1ea328e8feb 100644 --- a/InventoryCatalog/Test/Integration/SetToZeroLegacyStockItemAtSourceItemsDeleteTest.php +++ b/InventoryLegacySynchronization/Test/Integration/SetToZeroLegacyStockItemAtSourceItemsDeleteTest.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Test\Integration; +namespace Magento\InventoryLegacySynchronization\Test\Integration; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Framework\Api\SearchCriteriaBuilder; @@ -77,7 +77,7 @@ protected function setUp() * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php */ - public function testSetToZero() + public function testShouldSetLegacyQuantityToZeroOnSourceItemDelete(): void { $productSku = 'SKU-1'; $product = $this->productRepository->get($productSku); diff --git a/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php b/InventoryLegacySynchronization/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php similarity index 97% rename from InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php rename to InventoryLegacySynchronization/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php index 3de7f228dde6..1fbaa646ac62 100644 --- a/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php +++ b/InventoryLegacySynchronization/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Test\Integration; +namespace Magento\InventoryLegacySynchronization\Test\Integration; use Magento\CatalogInventory\Api\StockRegistryInterface; use Magento\Framework\MessageQueue\ConsumerFactory; @@ -38,7 +38,7 @@ protected function setUp() $this->stockRegistry = Bootstrap::getObjectManager()->create(StockRegistryInterface::class); $this->getDefaultSourceItemBySku = Bootstrap::getObjectManager()->get(GetDefaultSourceItemBySku::class); $this->consumer = Bootstrap::getObjectManager()->create(ConsumerFactory::class) - ->get('legacyCatalogInventorySynchronization', 100); + ->get('legacyInventorySynchronization', 100); } /** @@ -49,7 +49,7 @@ protected function setUp() * @magentoDbIsolation enabled * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function testSaveLegacyStockItemAssignedToDefaultSource() + public function testSaveLegacyStockItemAssignedToDefaultSource(): void { $stockItem = $this->stockRegistry->getStockItemBySku('SKU-1'); $stockItem->setQty(10); @@ -73,7 +73,7 @@ public function testSaveLegacyStockItemAssignedToDefaultSource() * @magentoDbIsolation enabled * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function testSaveLegacyStockItemAssignedToDefaultSourceAsynchronously() + public function testSaveLegacyStockItemAssignedToDefaultSourceAsynchronously(): void { $stockItem = $this->stockRegistry->getStockItemBySku('SKU-1'); $stockItem->setQty(10); @@ -104,7 +104,7 @@ public function testSaveLegacyStockItemAssignedToDefaultSourceAsynchronously() * @magentoDbIsolation enabled * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function testSaveLegacyStockItemNotAssignedToDefaultSource() + public function testSaveLegacyStockItemNotAssignedToDefaultSource(): void { $stockItem = $this->stockRegistry->getStockItemBySku('SKU-2'); $stockItem->setQty(10); @@ -138,7 +138,7 @@ public function testSaveLegacyStockItemNotAssignedToDefaultSource() * @magentoDbIsolation enabled * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function testSaveLegacyStockItemWithoutDefaultSourceAssignment() + public function testSaveLegacyStockItemWithoutDefaultSourceAssignment(): void { // SKU-3 is out of stock and not assigned to default source $stockItem = $this->stockRegistry->getStockItemBySku('SKU-3'); diff --git a/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtProductSaveTest.php b/InventoryLegacySynchronization/Test/Integration/UpdateDefaultSourceItemAtProductSaveTest.php similarity index 95% rename from InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtProductSaveTest.php rename to InventoryLegacySynchronization/Test/Integration/UpdateDefaultSourceItemAtProductSaveTest.php index 49e4398bcd6f..117488aa65b4 100644 --- a/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtProductSaveTest.php +++ b/InventoryLegacySynchronization/Test/Integration/UpdateDefaultSourceItemAtProductSaveTest.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryCatalog\Test\Integration; +namespace Magento\InventoryLegacySynchronization\Test\Integration; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\InventoryCatalog\Model\GetDefaultSourceItemBySku; @@ -38,7 +38,7 @@ protected function setUp() * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php * @magentoDbIsolation enabled */ - public function testSaveOutOfStockProductNotAssignedToDefaultSource() + public function testSaveOutOfStockProductNotAssignedToDefaultSource(): void { // SKU-3 is out of stock $product = $this->productRepository->get('SKU-3'); diff --git a/InventoryLegacySynchronization/composer.json b/InventoryLegacySynchronization/composer.json index 1ec4e9aba225..0606a9b4f6e0 100644 --- a/InventoryLegacySynchronization/composer.json +++ b/InventoryLegacySynchronization/composer.json @@ -5,6 +5,8 @@ "php": "~7.1.3||~7.2.0", "magento/framework": "*", "magento/module-catalog-inventory": "*", + "magento/module-inventory-catalog": "*", + "magento/module-inventory-catalog-api": "*", "magento/module-asynchronous-operations": "*" }, "type": "magento2-module", diff --git a/InventoryLegacySynchronization/etc/config.xml b/InventoryLegacySynchronization/etc/config.xml new file mode 100644 index 000000000000..02f43010dcf2 --- /dev/null +++ b/InventoryLegacySynchronization/etc/config.xml @@ -0,0 +1,17 @@ +<?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> + <cataloginventory> + <legacy_stock> + <async>0</async> + </legacy_stock> + </cataloginventory> + </default> +</config> diff --git a/InventoryLegacySynchronization/etc/module.xml b/InventoryLegacySynchronization/etc/module.xml index f72b1d021bd1..3f0bba258b17 100644 --- a/InventoryLegacySynchronization/etc/module.xml +++ b/InventoryLegacySynchronization/etc/module.xml @@ -7,6 +7,6 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_InventoryLegacySynchronization" setup_version="0.1.0"> + <module name="Magento_InventoryLegacySynchronization" setup_version="1.0.0"> </module> </config> diff --git a/InventoryLegacySynchronizationAdminUi/composer.json b/InventoryLegacySynchronizationAdminUi/composer.json new file mode 100644 index 000000000000..8bb2631f646b --- /dev/null +++ b/InventoryLegacySynchronizationAdminUi/composer.json @@ -0,0 +1,22 @@ +{ + "name": "magento/module-inventory-legacy-synchronization-admin-ui", + "description": "N/A", + "require": { + "php": "~7.1.3||~7.2.0", + "magento/framework": "*", + "magento/module-backend": "*" + }, + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\InventoryLegacySynchronizationAdminUi\\": "" + } + } +} diff --git a/InventoryLegacySynchronizationAdminUi/etc/adminhtml/system.xml b/InventoryLegacySynchronizationAdminUi/etc/adminhtml/system.xml new file mode 100755 index 000000000000..92414b14c51c --- /dev/null +++ b/InventoryLegacySynchronizationAdminUi/etc/adminhtml/system.xml @@ -0,0 +1,25 @@ +<?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_Config:etc/system_file.xsd"> + + <system> + <section id="cataloginventory"> + <group id="legacy_stock" translate="label" type="text" sortOrder="600" showInDefault="1" + showInWebsite="1" showInStore="1"> + <label>Legacy stock alignment</label> + <field id="async" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" + showInStore="0" canRestore="1"> + <label>Run asynchronously</label> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <comment>An asynchronous queue manager must be configured</comment> + </field> + </group> + </section> + </system> +</config> diff --git a/InventoryLegacySynchronizationAdminUi/etc/module.xml b/InventoryLegacySynchronizationAdminUi/etc/module.xml new file mode 100644 index 000000000000..1f51943a3ebb --- /dev/null +++ b/InventoryLegacySynchronizationAdminUi/etc/module.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:Module/etc/module.xsd"> + <module name="Magento_InventoryLegacySynchronizationAdminUi" setup_version="1.0.0"> + </module> +</config> \ No newline at end of file diff --git a/InventoryLegacySynchronizationAdminUi/registration.php b/InventoryLegacySynchronizationAdminUi/registration.php new file mode 100644 index 000000000000..23c0be07216f --- /dev/null +++ b/InventoryLegacySynchronizationAdminUi/registration.php @@ -0,0 +1,12 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +\Magento\Framework\Component\ComponentRegistrar::register( + \Magento\Framework\Component\ComponentRegistrar::MODULE, + 'Magento_InventoryLegacySynchronizationAdminUi', + __DIR__ +); From 1220b5c7f746e0bab9604ed13c290030d4f5edef Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Fri, 22 Mar 2019 15:30:20 +0100 Subject: [PATCH 042/231] InventoryLegacySynchronization bulk operations support and test coverage --- InventoryCatalog/Model/BulkSourceUnassign.php | 44 +--- .../ResourceModel/BulkInventoryTransfer.php | 11 +- .../ResourceModel/BulkSourceUnassign.php | 29 +-- .../Model/AsyncConsumer.php | 2 +- .../SetZeroQuantityToLegacyStockItems.php | 63 +++++ ...SetZeroQuantityToLegacyStockItems.php.orig | 63 +++++ .../Model/Synchronize.php | 38 +-- .../Model/SynchronizeInventoryData.php | 2 +- .../Plugin/SetDataToLegacyAtBulkTransfer.php | 17 -- ...ToLegacyCatalogInventoryAtBulkTransfer.php | 98 ++++++++ .../SetZeroQuantityToLegacyAtBulkUnassign.php | 6 +- ...gacyCatalogInventoryAtBulkTransferTest.php | 221 ++++++++++++++++++ ...egacyStockItemAtSourceItemsDeleteTest.php} | 2 +- InventoryLegacySynchronization/etc/di.xml | 19 +- 14 files changed, 491 insertions(+), 124 deletions(-) create mode 100644 InventoryLegacySynchronization/Model/ResourceModel/SetZeroQuantityToLegacyStockItems.php create mode 100644 InventoryLegacySynchronization/Model/ResourceModel/SetZeroQuantityToLegacyStockItems.php.orig delete mode 100644 InventoryLegacySynchronization/Plugin/SetDataToLegacyAtBulkTransfer.php create mode 100644 InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtBulkTransfer.php create mode 100644 InventoryLegacySynchronization/Test/Integration/SetDataToLegacyCatalogInventoryAtBulkTransferTest.php rename InventoryLegacySynchronization/Test/Integration/{SetToZeroLegacyStockItemAtSourceItemsDeleteTest.php => SetZeroToLegacyStockItemAtSourceItemsDeleteTest.php} (98%) diff --git a/InventoryCatalog/Model/BulkSourceUnassign.php b/InventoryCatalog/Model/BulkSourceUnassign.php index 6b8f63e75d93..d9c67bec7360 100644 --- a/InventoryCatalog/Model/BulkSourceUnassign.php +++ b/InventoryCatalog/Model/BulkSourceUnassign.php @@ -35,29 +35,14 @@ class BulkSourceUnassign implements BulkSourceUnassignInterface */ private $sourceIndexer; - /** - * @var LegacyIndexer - */ - private $legacyIndexer; - - /** - * @var DefaultSourceProviderInterface - */ - private $defaultSourceProvider; - - /** - * @var GetProductIdsBySkus - */ - private $getProductIdsBySkus; - /** * MassProductSourceAssign constructor. * @param BulkSourceUnassignValidatorInterface $unassignValidator * @param BulkSourceUnassignResource $bulkSourceUnassign - * @param DefaultSourceProviderInterface $defaultSourceProvider - * @param GetProductIdsBySkus $getProductIdsBySkus + * @param DefaultSourceProviderInterface $defaultSourceProvider @deprecated + * @param GetProductIdsBySkus $getProductIdsBySkus @deprecated * @param SourceIndexer $sourceIndexer - * @param LegacyIndexer $legacyIndexer + * @param LegacyIndexer $legacyIndexer @deprecated * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( @@ -71,25 +56,14 @@ public function __construct( $this->unassignValidator = $unassignValidator; $this->bulkSourceUnassign = $bulkSourceUnassign; $this->sourceIndexer = $sourceIndexer; - $this->legacyIndexer = $legacyIndexer; - $this->defaultSourceProvider = $defaultSourceProvider; - $this->getProductIdsBySkus = $getProductIdsBySkus; - } - - /** - * Reindex legacy stock (for default source) - * @param array $skus - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ - private function reindexLegacy(array $skus): void - { - $productIds = array_values($this->getProductIdsBySkus->execute($skus)); - $this->legacyIndexer->executeList($productIds); } /** * @inheritdoc - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @param array $skus + * @param array $sourceCodes + * @return int + * @throws ValidationException */ public function execute(array $skus, array $sourceCodes): int { @@ -101,10 +75,6 @@ public function execute(array $skus, array $sourceCodes): int $res = $this->bulkSourceUnassign->execute($skus, $sourceCodes); $this->sourceIndexer->executeList($sourceCodes); - if (in_array($this->defaultSourceProvider->getCode(), $sourceCodes, true)) { - $this->reindexLegacy($skus); - } - return $res; } } diff --git a/InventoryCatalog/Model/ResourceModel/BulkInventoryTransfer.php b/InventoryCatalog/Model/ResourceModel/BulkInventoryTransfer.php index 807145b6db61..824e37cb1a25 100644 --- a/InventoryCatalog/Model/ResourceModel/BulkInventoryTransfer.php +++ b/InventoryCatalog/Model/ResourceModel/BulkInventoryTransfer.php @@ -10,7 +10,6 @@ use Magento\Framework\App\ResourceConnection; use Magento\Inventory\Model\ResourceModel\SourceItem; use Magento\InventoryApi\Api\Data\SourceItemInterface; -use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; use Magento\InventoryCatalogApi\Model\GetProductTypesBySkusInterface; use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForProductTypeInterface; @@ -37,28 +36,20 @@ class BulkInventoryTransfer */ private $isSourceItemManagementAllowedForProductType; - /** - * @var DefaultSourceProviderInterface - */ - private $defaultSourceProvider; - /** * @param ResourceConnection $resourceConnection * @param GetProductTypesBySkusInterface $getProductTypesBySkus * @param IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemManagementAllowedForProductType - * @param DefaultSourceProviderInterface $defaultSourceProvider * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( ResourceConnection $resourceConnection, GetProductTypesBySkusInterface $getProductTypesBySkus, - IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemManagementAllowedForProductType, - DefaultSourceProviderInterface $defaultSourceProvider + IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemManagementAllowedForProductType ) { $this->resourceConnection = $resourceConnection; $this->getProductTypesBySkus = $getProductTypesBySkus; $this->isSourceItemManagementAllowedForProductType = $isSourceItemManagementAllowedForProductType; - $this->defaultSourceProvider = $defaultSourceProvider; } /** diff --git a/InventoryCatalog/Model/ResourceModel/BulkSourceUnassign.php b/InventoryCatalog/Model/ResourceModel/BulkSourceUnassign.php index 59cc6c8318dd..9b4e975b12c2 100644 --- a/InventoryCatalog/Model/ResourceModel/BulkSourceUnassign.php +++ b/InventoryCatalog/Model/ResourceModel/BulkSourceUnassign.php @@ -10,7 +10,6 @@ use Magento\Framework\App\ResourceConnection; use Magento\Inventory\Model\ResourceModel\SourceItem; use Magento\InventoryApi\Api\Data\SourceItemInterface; -use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; /** * Implementation of bulk source assignment @@ -25,30 +24,14 @@ class BulkSourceUnassign */ private $resourceConnection; - /** - * @var BulkZeroLegacyStockItem - */ - private $bulkZeroLegacyStockItem; - - /** - * @var DefaultSourceProviderInterface - */ - private $defaultSourceProvider; - /** * @param ResourceConnection $resourceConnection - * @param DefaultSourceProviderInterface $defaultSourceProvider - * @param BulkZeroLegacyStockItem $bulkZeroLegacyStockItem * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( - ResourceConnection $resourceConnection, - DefaultSourceProviderInterface $defaultSourceProvider, - BulkZeroLegacyStockItem $bulkZeroLegacyStockItem + ResourceConnection $resourceConnection ) { $this->resourceConnection = $resourceConnection; - $this->bulkZeroLegacyStockItem = $bulkZeroLegacyStockItem; - $this->defaultSourceProvider = $defaultSourceProvider; } /** @@ -56,27 +39,17 @@ public function __construct( * @param array $skus * @param array $sourceCodes * @return int - * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function execute(array $skus, array $sourceCodes): int { $connection = $this->resourceConnection->getConnection(); $tableName = $this->resourceConnection->getTableName(SourceItem::TABLE_NAME_SOURCE_ITEM); - $connection->beginTransaction(); - $count = (int) $connection->delete($tableName, [ SourceItemInterface::SOURCE_CODE . ' IN (?)' => $sourceCodes, SourceItemInterface::SKU . ' IN (?)' => $skus, ]); - // Legacy stock update - if (in_array($this->defaultSourceProvider->getCode(), $sourceCodes)) { - $this->bulkZeroLegacyStockItem->execute($skus); - } - - $connection->commit(); - return $count; } } diff --git a/InventoryLegacySynchronization/Model/AsyncConsumer.php b/InventoryLegacySynchronization/Model/AsyncConsumer.php index 833829324625..bd6eddc80bef 100644 --- a/InventoryLegacySynchronization/Model/AsyncConsumer.php +++ b/InventoryLegacySynchronization/Model/AsyncConsumer.php @@ -49,6 +49,6 @@ public function __construct( public function processOperations(OperationInterface $operation): void { $data = $this->serializer->unserialize($operation->getSerializedData()); - $this->synchronizeInventoryData->execute($data['direction'], $data['items']); + $this->synchronizeInventoryData->execute($data['destination'], $data['items']); } } diff --git a/InventoryLegacySynchronization/Model/ResourceModel/SetZeroQuantityToLegacyStockItems.php b/InventoryLegacySynchronization/Model/ResourceModel/SetZeroQuantityToLegacyStockItems.php new file mode 100644 index 000000000000..e525e574e0d4 --- /dev/null +++ b/InventoryLegacySynchronization/Model/ResourceModel/SetZeroQuantityToLegacyStockItems.php @@ -0,0 +1,63 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryLegacySynchronization\Model\ResourceModel; + +use Magento\CatalogInventory\Api\Data\StockItemInterface; +use Magento\Framework\App\ResourceConnection; +use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface; + +/** + * Set quantity=0 to legacy cataloginventory_stock_item table for a set of skus via plain MySql query + */ +class SetZeroQuantityToLegacyStockItems +{ + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @var GetProductIdsBySkusInterface + */ + private $getProductIdsBySkus; + + /** + * @param ResourceConnection $resourceConnection + * @param GetProductIdsBySkusInterface $getProductIdsBySkus + */ + public function __construct( + ResourceConnection $resourceConnection, + GetProductIdsBySkusInterface $getProductIdsBySkus + ) { + $this->resourceConnection = $resourceConnection; + $this->getProductIdsBySkus = $getProductIdsBySkus; + } + + /** + * @param array $skus + * @return void + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function execute(array $skus): void + { + $productIds = array_values($this->getProductIdsBySkus->execute($skus)); + + $connection = $this->resourceConnection->getConnection(); + $connection->update( + $this->resourceConnection->getTableName('cataloginventory_stock_item'), + [ + StockItemInterface::QTY => 0, + StockItemInterface::IS_IN_STOCK => 0, + ], + [ + StockItemInterface::PRODUCT_ID . ' IN (?)' => $productIds, + 'website_id = ?' => 0, + ] + ); + } +} diff --git a/InventoryLegacySynchronization/Model/ResourceModel/SetZeroQuantityToLegacyStockItems.php.orig b/InventoryLegacySynchronization/Model/ResourceModel/SetZeroQuantityToLegacyStockItems.php.orig new file mode 100644 index 000000000000..e525e574e0d4 --- /dev/null +++ b/InventoryLegacySynchronization/Model/ResourceModel/SetZeroQuantityToLegacyStockItems.php.orig @@ -0,0 +1,63 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryLegacySynchronization\Model\ResourceModel; + +use Magento\CatalogInventory\Api\Data\StockItemInterface; +use Magento\Framework\App\ResourceConnection; +use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface; + +/** + * Set quantity=0 to legacy cataloginventory_stock_item table for a set of skus via plain MySql query + */ +class SetZeroQuantityToLegacyStockItems +{ + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @var GetProductIdsBySkusInterface + */ + private $getProductIdsBySkus; + + /** + * @param ResourceConnection $resourceConnection + * @param GetProductIdsBySkusInterface $getProductIdsBySkus + */ + public function __construct( + ResourceConnection $resourceConnection, + GetProductIdsBySkusInterface $getProductIdsBySkus + ) { + $this->resourceConnection = $resourceConnection; + $this->getProductIdsBySkus = $getProductIdsBySkus; + } + + /** + * @param array $skus + * @return void + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function execute(array $skus): void + { + $productIds = array_values($this->getProductIdsBySkus->execute($skus)); + + $connection = $this->resourceConnection->getConnection(); + $connection->update( + $this->resourceConnection->getTableName('cataloginventory_stock_item'), + [ + StockItemInterface::QTY => 0, + StockItemInterface::IS_IN_STOCK => 0, + ], + [ + StockItemInterface::PRODUCT_ID . ' IN (?)' => $productIds, + 'website_id = ?' => 0, + ] + ); + } +} diff --git a/InventoryLegacySynchronization/Model/Synchronize.php b/InventoryLegacySynchronization/Model/Synchronize.php index 786249293a15..03a0031d845e 100644 --- a/InventoryLegacySynchronization/Model/Synchronize.php +++ b/InventoryLegacySynchronization/Model/Synchronize.php @@ -24,14 +24,14 @@ class Synchronize private const TOPIC_NAME = 'inventory.legacy_synchronization.set_data'; /** - * Define synchronization from MSI source items to legacy catalog inventory + * Synchronization from MSI source items to legacy catalog inventory */ - public const MSI_TO_LEGACY = 'msi-to-legacy'; + public const MSI_TO_LEGACY = 'synchronize-msi-to-legacy'; /** - * Define synchronization from legacy catalog inventory to MSI source items + * Synchronization from legacy catalog inventory to MSI source items */ - public const LEGACY_TO_MSI = 'legacy-to-msi'; + public const LEGACY_TO_MSI = 'synchronize-legacy-to-msi'; /** * @var BulkManagementInterface @@ -96,12 +96,12 @@ public function __construct( } /** - * @param string $direction + * @param string $destination * @param array $items */ - private function executeAsync(string $direction, array $items): void + private function executeAsync(string $destination, array $items): void { - $operations = []; + $asyncOperations = []; $bulkUuid = $this->identityService->generateId(); @@ -113,7 +113,7 @@ private function executeAsync(string $direction, array $items): void 'topic_name' => self::TOPIC_NAME, 'serialized_data' => $this->serializer->serialize( [ - 'direction' => $direction, + 'destination' => $destination, 'items' => $chunk ] ), @@ -121,36 +121,36 @@ private function executeAsync(string $direction, array $items): void ] ]; - /** @var \Magento\AsynchronousOperations\Api\Data\OperationInterface $operation */ - $operation = $this->operationInterfaceFactory->create($data); - $operations[] = $operation; + /** @var \Magento\AsynchronousOperations\Api\Data\OperationInterface $asyncOperation */ + $asyncOperation = $this->operationInterfaceFactory->create($data); + $asyncOperations[] = $asyncOperation; } - $this->bulkManagement->scheduleBulk($bulkUuid, $operations, __('Synchronize legacy stock')); + $this->bulkManagement->scheduleBulk($bulkUuid, $asyncOperations, __('Synchronize legacy stock')); } /** - * @param string $direction + * @param string $destination * @param array $items * @throws \Magento\Framework\Exception\LocalizedException */ - private function executeSync(string $direction, array $items): void + private function executeSync(string $destination, array $items): void { - $this->synchronizeInventoryData->execute($direction, $items); + $this->synchronizeInventoryData->execute($destination, $items); } /** - * @param string $direction + * @param string $destination * @param array $items * @return void * @throws \Magento\Framework\Exception\LocalizedException */ - public function execute(string $direction, array $items): void + public function execute(string $destination, array $items): void { if ($this->isAsyncLegacyAlignment->execute()) { - $this->executeAsync($direction, $items); + $this->executeAsync($destination, $items); } else { - $this->executeSync($direction, $items); + $this->executeSync($destination, $items); } } } diff --git a/InventoryLegacySynchronization/Model/SynchronizeInventoryData.php b/InventoryLegacySynchronization/Model/SynchronizeInventoryData.php index 58a306a5ffeb..3f5a28c7cdb2 100644 --- a/InventoryLegacySynchronization/Model/SynchronizeInventoryData.php +++ b/InventoryLegacySynchronization/Model/SynchronizeInventoryData.php @@ -48,7 +48,7 @@ public function execute(string $destination, array $items): void { if ($destination === Synchronize::MSI_TO_LEGACY) { $this->setDataToLegacyInventory->execute($items); - } else { + } elseif ($destination === Synchronize::LEGACY_TO_MSI) { $this->setDataToSourceItem->execute($items); } } diff --git a/InventoryLegacySynchronization/Plugin/SetDataToLegacyAtBulkTransfer.php b/InventoryLegacySynchronization/Plugin/SetDataToLegacyAtBulkTransfer.php deleted file mode 100644 index b89778d46f3a..000000000000 --- a/InventoryLegacySynchronization/Plugin/SetDataToLegacyAtBulkTransfer.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php -/** - * Created by PhpStorm. - * User: riccardo - * Date: 22/03/19 - * Time: 14.35 - */ -namespace Magento\InventoryLegacySynchronization; - -class SetDataToLegacyAtBulkTransfer -{ - - - public function afterExecute(\Magento\InventoryCatalog\Model\ResourceModel\BulkInventoryTransfer $subject, $result) - { - } -} \ No newline at end of file diff --git a/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtBulkTransfer.php b/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtBulkTransfer.php new file mode 100644 index 000000000000..02c8c2f7b685 --- /dev/null +++ b/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtBulkTransfer.php @@ -0,0 +1,98 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryLegacySynchronization\Plugin; + +use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryCatalogApi\Api\BulkInventoryTransferInterface; +use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; +use Magento\InventoryLegacySynchronization\Model\GetDefaultSourceItemsBySkus; +use Magento\InventoryLegacySynchronization\Model\Synchronize; + +class SetDataToLegacyCatalogInventoryAtBulkTransfer +{ + /** + * @var GetDefaultSourceItemsBySkus + */ + private $getDefaultSourceItemsBySkus; + + /** + * @var DefaultSourceProviderInterface + */ + private $defaultSourceProvider; + + /** + * @var Synchronize + */ + private $synchronize; + + /** + * @param GetDefaultSourceItemsBySkus $getDefaultSourceItemsBySkus + * @param DefaultSourceProviderInterface $defaultSourceProvider + * @param Synchronize $synchronize + * @SuppressWarnings(PHPMD.LongVariable) + */ + public function __construct( + GetDefaultSourceItemsBySkus $getDefaultSourceItemsBySkus, + DefaultSourceProviderInterface $defaultSourceProvider, + Synchronize $synchronize + ) { + $this->getDefaultSourceItemsBySkus = $getDefaultSourceItemsBySkus; + $this->defaultSourceProvider = $defaultSourceProvider; + $this->synchronize = $synchronize; + } + + /** + * @param BulkInventoryTransferInterface $subject + * @param $result + * @param array $skus + * @param string $originSource + * @param string $destinationSource + * @param bool $unassignFromOrigin + * @return bool + * @throws \Magento\Framework\Exception\LocalizedException + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterExecute( + BulkInventoryTransferInterface $subject, + bool $result, + array $skus, + string $originSource, + string $destinationSource, + bool $unassignFromOrigin + ): bool { + $defaultSourceCode = $this->defaultSourceProvider->getCode(); + + $sourceItemsData = []; + if ($unassignFromOrigin && ($originSource === $defaultSourceCode)) { + foreach ($skus as $sku) { + $sourceItemsData[] = [ + SourceItemInterface::QUANTITY => 0.0, + SourceItemInterface::STATUS => SourceItemInterface::STATUS_OUT_OF_STOCK, + SourceItemInterface::SOURCE_CODE => $defaultSourceCode, + SourceItemInterface::SKU => $sku + ]; + } + } elseif (($destinationSource === $defaultSourceCode) || ($originSource === $defaultSourceCode)) { + $sourceItems = $this->getDefaultSourceItemsBySkus->execute($skus); + foreach ($sourceItems as $sourceItem) { + $sourceItemsData[] = [ + SourceItemInterface::QUANTITY => $sourceItem->getQuantity(), + SourceItemInterface::STATUS => $sourceItem->getStatus(), + SourceItemInterface::SOURCE_CODE => $defaultSourceCode, + SourceItemInterface::SKU => $sourceItem->getSku() + ]; + } + } + + if (!empty($sourceItemsData)) { + $this->synchronize->execute(Synchronize::MSI_TO_LEGACY, $sourceItemsData); + } + + return $result; + } +} diff --git a/InventoryLegacySynchronization/Plugin/SetZeroQuantityToLegacyAtBulkUnassign.php b/InventoryLegacySynchronization/Plugin/SetZeroQuantityToLegacyAtBulkUnassign.php index ad8651b62f8d..06da8078ae21 100644 --- a/InventoryLegacySynchronization/Plugin/SetZeroQuantityToLegacyAtBulkUnassign.php +++ b/InventoryLegacySynchronization/Plugin/SetZeroQuantityToLegacyAtBulkUnassign.php @@ -9,7 +9,7 @@ namespace Magento\InventoryLegacySynchronization\Plugin; use Magento\InventoryApi\Api\Data\SourceItemInterface; -use Magento\InventoryCatalog\Model\ResourceModel\BulkSourceUnassign; +use Magento\InventoryCatalogApi\Api\BulkSourceUnassignInterface; use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; use Magento\InventoryLegacySynchronization\Model\Synchronize; @@ -42,7 +42,7 @@ public function __construct( } /** - * @param BulkSourceUnassign $subject + * @param BulkSourceUnassignInterface $subject * @param int $result * @param array $skus * @param array $sourceCodes @@ -51,7 +51,7 @@ public function __construct( * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function afterExecute( - BulkSourceUnassign $subject, + BulkSourceUnassignInterface $subject, int $result, array $skus, array $sourceCodes diff --git a/InventoryLegacySynchronization/Test/Integration/SetDataToLegacyCatalogInventoryAtBulkTransferTest.php b/InventoryLegacySynchronization/Test/Integration/SetDataToLegacyCatalogInventoryAtBulkTransferTest.php new file mode 100644 index 000000000000..f8dd1909e1d9 --- /dev/null +++ b/InventoryLegacySynchronization/Test/Integration/SetDataToLegacyCatalogInventoryAtBulkTransferTest.php @@ -0,0 +1,221 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryLegacySynchronization\Test\Integration; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryApi\Api\SourceItemRepositoryInterface; +use Magento\InventoryCatalogApi\Api\BulkInventoryTransferInterface; +use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; +use PHPUnit\Framework\TestCase; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\CatalogInventory\Api\StockItemRepositoryInterface; +use Magento\CatalogInventory\Api\StockItemCriteriaInterface; +use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory; + +class SetDataToLegacyCatalogInventoryAtBulkTransferTest extends TestCase +{ + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var StockItemCriteriaInterfaceFactory + */ + private $legacyStockItemCriteriaFactory; + + /** + * @var StockItemRepositoryInterface + */ + private $legacyStockItemRepository; + + /** + * @var SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + + /** + * @var SourceItemRepositoryInterface + */ + private $sourceItemRepository; + + /** + * @var DefaultSourceProviderInterface + */ + private $defaultSourceProvider; + + /** + * @var BulkInventoryTransferInterface + */ + private $bulkInventoryTransfer; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); + + $this->legacyStockItemCriteriaFactory = Bootstrap::getObjectManager()->get( + StockItemCriteriaInterfaceFactory::class + ); + $this->legacyStockItemRepository = Bootstrap::getObjectManager()->get(StockItemRepositoryInterface::class); + + $this->searchCriteriaBuilder = Bootstrap::getObjectManager()->get(SearchCriteriaBuilder::class); + $this->sourceItemRepository = Bootstrap::getObjectManager()->get(SourceItemRepositoryInterface::class); + + $this->bulkInventoryTransfer = Bootstrap::getObjectManager()->get(BulkInventoryTransferInterface::class); + $this->defaultSourceProvider = Bootstrap::getObjectManager()->get(DefaultSourceProviderInterface::class); + } + + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php + * @SuppressWarnings(PHPMD.LongVariable) + */ + public function testShouldSetLegacyQuantityToZeroOnInventoryTransferAndDefaultSourceUnassign(): void + { + $productSku = 'SKU-1'; + $product = $this->productRepository->get($productSku); + $productId = $product->getId(); + $websiteId = 0; + + /** @var StockItemCriteriaInterface $legacyStockItemCriteria */ + $legacyStockItemCriteria = $this->legacyStockItemCriteriaFactory->create(); + $legacyStockItemCriteria->setProductsFilter($productId); + $legacyStockItemCriteria->setScopeFilter($websiteId); + $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); + self::assertCount(1, $legacyStockItems); + + $legacyStockItem = reset($legacyStockItems); + self::assertTrue($legacyStockItem->getIsInStock()); + self::assertSame(5.5, (float) $legacyStockItem->getQty()); + + $searchCriteria = $this->searchCriteriaBuilder + ->addFilter(SourceItemInterface::SKU, $productSku) + ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) + ->create(); + $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); + self::assertCount(1, $sourceItems); + + $this->bulkInventoryTransfer->execute( + ['SKU-1'], + $this->defaultSourceProvider->getCode(), + 'eu-1', + true + ); + + $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); + self::assertCount(1, $legacyStockItems); + + $legacyStockItem = reset($legacyStockItems); + self::assertFalse($legacyStockItem->getIsInStock()); + self::assertSame(0.0, (float) $legacyStockItem->getQty()); + } + + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php + * @SuppressWarnings(PHPMD.LongVariable) + */ + public function testShouldSetLegacyQuantityToZeroOnInventoryTransferToDefaultSource(): void + { + $productSku = 'SKU-1'; + $product = $this->productRepository->get($productSku); + $productId = $product->getId(); + $websiteId = 0; + + /** @var StockItemCriteriaInterface $legacyStockItemCriteria */ + $legacyStockItemCriteria = $this->legacyStockItemCriteriaFactory->create(); + $legacyStockItemCriteria->setProductsFilter($productId); + $legacyStockItemCriteria->setScopeFilter($websiteId); + $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); + self::assertCount(1, $legacyStockItems); + + $legacyStockItem = reset($legacyStockItems); + self::assertTrue($legacyStockItem->getIsInStock()); + self::assertEquals(5.5, $legacyStockItem->getQty()); + + $searchCriteria = $this->searchCriteriaBuilder + ->addFilter(SourceItemInterface::SKU, $productSku) + ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) + ->create(); + $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); + self::assertCount(1, $sourceItems); + + $this->bulkInventoryTransfer->execute( + ['SKU-1'], + 'eu-1', + $this->defaultSourceProvider->getCode(), + true + ); + + $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); + self::assertCount(1, $legacyStockItems); + + $legacyStockItem = reset($legacyStockItems); + self::assertTrue($legacyStockItem->getIsInStock()); + self::assertEquals(11.0, (float) $legacyStockItem->getQty()); + } + + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php + * @SuppressWarnings(PHPMD.LongVariable) + */ + public function testShouldSetLegacyQuantityToZeroOnInventoryTransferFromDefaultSource(): void + { + $productSku = 'SKU-1'; + $product = $this->productRepository->get($productSku); + $productId = $product->getId(); + $websiteId = 0; + + /** @var StockItemCriteriaInterface $legacyStockItemCriteria */ + $legacyStockItemCriteria = $this->legacyStockItemCriteriaFactory->create(); + $legacyStockItemCriteria->setProductsFilter($productId); + $legacyStockItemCriteria->setScopeFilter($websiteId); + $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); + self::assertCount(1, $legacyStockItems); + + $legacyStockItem = reset($legacyStockItems); + self::assertTrue($legacyStockItem->getIsInStock()); + self::assertSame(5.5, (float) $legacyStockItem->getQty()); + + $searchCriteria = $this->searchCriteriaBuilder + ->addFilter(SourceItemInterface::SKU, $productSku) + ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) + ->create(); + $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); + self::assertCount(1, $sourceItems); + + $this->bulkInventoryTransfer->execute( + ['SKU-1'], + $this->defaultSourceProvider->getCode(), + 'eu-1', + false + ); + + $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); + self::assertCount(1, $legacyStockItems); + + $legacyStockItem = reset($legacyStockItems); + self::assertFalse($legacyStockItem->getIsInStock()); + self::assertSame(0.0, (float) $legacyStockItem->getQty()); + } +} diff --git a/InventoryLegacySynchronization/Test/Integration/SetToZeroLegacyStockItemAtSourceItemsDeleteTest.php b/InventoryLegacySynchronization/Test/Integration/SetZeroToLegacyStockItemAtSourceItemsDeleteTest.php similarity index 98% rename from InventoryLegacySynchronization/Test/Integration/SetToZeroLegacyStockItemAtSourceItemsDeleteTest.php rename to InventoryLegacySynchronization/Test/Integration/SetZeroToLegacyStockItemAtSourceItemsDeleteTest.php index d1ea328e8feb..2f7b30659f67 100644 --- a/InventoryLegacySynchronization/Test/Integration/SetToZeroLegacyStockItemAtSourceItemsDeleteTest.php +++ b/InventoryLegacySynchronization/Test/Integration/SetZeroToLegacyStockItemAtSourceItemsDeleteTest.php @@ -19,7 +19,7 @@ use Magento\CatalogInventory\Api\StockItemCriteriaInterface; use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory; -class SetToZeroLegacyStockItemAtSourceItemsDeleteTest extends TestCase +class SetZeroToLegacyStockItemAtSourceItemsDeleteTest extends TestCase { /** * @var ProductRepositoryInterface diff --git a/InventoryLegacySynchronization/etc/di.xml b/InventoryLegacySynchronization/etc/di.xml index e7e1a18618a5..5ebf6d53b707 100644 --- a/InventoryLegacySynchronization/etc/di.xml +++ b/InventoryLegacySynchronization/etc/di.xml @@ -8,6 +8,12 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\InventoryLegacySynchronization\Model\Synchronize"> + <arguments> + <argument name="batchSize" xsi:type="number">100</argument> + </arguments> + </type> + <type name="Magento\CatalogInventory\Model\ResourceModel\QtyCounterInterface"> <plugin name="update_source_item_at_legacy_qty_counter" type="Magento\InventoryLegacySynchronization\Plugin\UpdateSourceItemAtLegacyQtyCounterPlugin"/> @@ -26,13 +32,12 @@ type="Magento\InventoryLegacySynchronization\Plugin\SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin"/> </type> - <type name="Magento\InventoryLegacySynchronization\Model\Synchronize"> - <arguments> - <argument name="batchSize" xsi:type="number">100</argument> - </arguments> + <type name="Magento\InventoryCatalogApi\Api\BulkSourceUnassignInterface"> + <plugin name="set_zero_quantity_to_legacy_at_bulk_unassign" + type="Magento\InventoryLegacySynchronization\Plugin\SetZeroQuantityToLegacyAtBulkUnassign"/> </type> - <type name="Magento\InventoryCatalog\Model\ResourceModel\BulkInventoryTransfer"> - <plugin sortOrder="1" name="magentoInventoryLegacySynchronizationBulkInventoryTransfer" - type="Magento\InventoryLegacySynchronization\SetDataToLegacyAtBulkTransfer"/> + <type name="Magento\InventoryCatalogApi\Api\BulkInventoryTransferInterface"> + <plugin name="set_data_to_legacy_catalog_inventory_at_bulk_unassign" + type="Magento\InventoryLegacySynchronization\Plugin\SetDataToLegacyCatalogInventoryAtBulkTransfer"/> </type> </config> From 16d89f1de79c33e5932aa8d9f1dc3ad77427637d Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Fri, 22 Mar 2019 15:37:50 +0100 Subject: [PATCH 043/231] Missing license and readme files --- InventoryLegacySynchronization/LICENSE.txt | 48 +++++++++++++++++++ .../LICENSE_AFL.txt | 48 +++++++++++++++++++ InventoryLegacySynchronization/README.md | 11 +++++ .../LICENSE.txt | 48 +++++++++++++++++++ .../LICENSE_AFL.txt | 48 +++++++++++++++++++ .../README.md | 11 +++++ 6 files changed, 214 insertions(+) create mode 100644 InventoryLegacySynchronization/LICENSE.txt create mode 100644 InventoryLegacySynchronization/LICENSE_AFL.txt create mode 100644 InventoryLegacySynchronization/README.md create mode 100644 InventoryLegacySynchronizationAdminUi/LICENSE.txt create mode 100644 InventoryLegacySynchronizationAdminUi/LICENSE_AFL.txt create mode 100644 InventoryLegacySynchronizationAdminUi/README.md diff --git a/InventoryLegacySynchronization/LICENSE.txt b/InventoryLegacySynchronization/LICENSE.txt new file mode 100644 index 000000000000..49525fd99da9 --- /dev/null +++ b/InventoryLegacySynchronization/LICENSE.txt @@ -0,0 +1,48 @@ + +Open Software License ("OSL") v. 3.0 + +This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Open Software License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/InventoryLegacySynchronization/LICENSE_AFL.txt b/InventoryLegacySynchronization/LICENSE_AFL.txt new file mode 100644 index 000000000000..f39d641b18a1 --- /dev/null +++ b/InventoryLegacySynchronization/LICENSE_AFL.txt @@ -0,0 +1,48 @@ + +Academic Free License ("AFL") v. 3.0 + +This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Academic Free License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/InventoryLegacySynchronization/README.md b/InventoryLegacySynchronization/README.md new file mode 100644 index 000000000000..caaaedf9fe5c --- /dev/null +++ b/InventoryLegacySynchronization/README.md @@ -0,0 +1,11 @@ +# InventoryLegacySynchronization module + +The `InventoryLegacySynchronization` module provides a backward compatible mechanism with Magento legacy inventory + +This module is part of the new inventory infrastructure. The +[Inventory Management overview](https://devdocs.magento.com/guides/v2.3/inventory/index.html) +describes the MSI (Multi-Source Inventory) project in more detail. + +## Installation details + +This module is installed as part of Magento Open Source. It cannot be deleted or disabled. diff --git a/InventoryLegacySynchronizationAdminUi/LICENSE.txt b/InventoryLegacySynchronizationAdminUi/LICENSE.txt new file mode 100644 index 000000000000..49525fd99da9 --- /dev/null +++ b/InventoryLegacySynchronizationAdminUi/LICENSE.txt @@ -0,0 +1,48 @@ + +Open Software License ("OSL") v. 3.0 + +This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Open Software License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/InventoryLegacySynchronizationAdminUi/LICENSE_AFL.txt b/InventoryLegacySynchronizationAdminUi/LICENSE_AFL.txt new file mode 100644 index 000000000000..f39d641b18a1 --- /dev/null +++ b/InventoryLegacySynchronizationAdminUi/LICENSE_AFL.txt @@ -0,0 +1,48 @@ + +Academic Free License ("AFL") v. 3.0 + +This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Academic Free License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/InventoryLegacySynchronizationAdminUi/README.md b/InventoryLegacySynchronizationAdminUi/README.md new file mode 100644 index 000000000000..7d98cc8ee2b6 --- /dev/null +++ b/InventoryLegacySynchronizationAdminUi/README.md @@ -0,0 +1,11 @@ +# InventoryLegacySynchronizationAdminUi module + +The `InventoryLegacySynchronizationAdminUi` module provides admin UI integration for `InventoryLegacySynchronization` + +This module is part of the new inventory infrastructure. The +[Inventory Management overview](https://devdocs.magento.com/guides/v2.3/inventory/index.html) +describes the MSI (Multi-Source Inventory) project in more detail. + +## Installation details + +This module is installed as part of Magento Open Source. It cannot be deleted or disabled. From d7466accf16b57385748e6c9b33092d55f2ea5ba Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Fri, 22 Mar 2019 16:01:33 +0100 Subject: [PATCH 044/231] Additional test coverage and fix for failing integration tests --- .../Model/BulkInventoryTransfer.php | 32 ++----------------- .../UpdateLegacyStockItemsData.php | 4 +++ .../ResourceModel/UpdateSourceItemsData.php | 4 +++ .../Model/ToMsi/SetDataToSourceItem.php | 21 ++++++++---- .../SetZeroQuantityToLegacyAtBulkUnassign.php | 6 ++-- 5 files changed, 28 insertions(+), 39 deletions(-) diff --git a/InventoryCatalog/Model/BulkInventoryTransfer.php b/InventoryCatalog/Model/BulkInventoryTransfer.php index b14c4788ad3b..f810d892d19a 100644 --- a/InventoryCatalog/Model/BulkInventoryTransfer.php +++ b/InventoryCatalog/Model/BulkInventoryTransfer.php @@ -31,33 +31,18 @@ class BulkInventoryTransfer implements BulkInventoryTransferInterface */ private $bulkInventoryTransfer; - /** - * @var GetProductIdsBySkusInterface - */ - private $getProductIdsBySkus; - /** * @var LegacyIndexer */ private $legacyIndexer; - /** - * @var DefaultSourceProviderInterface - */ - private $defaultSourceProvider; - - /** - * @var SourceIndexer - */ - private $sourceIndexer; - /** * MassProductSourceAssign constructor. * @param BulkInventoryTransferValidatorInterface $inventoryTransferValidator * @param BulkInventoryTransferResource $bulkInventoryTransfer - * @param SourceIndexer $sourceIndexer - * @param DefaultSourceProviderInterface $defaultSourceProvider - * @param GetProductIdsBySkusInterface $getProductIdsBySkus + * @param SourceIndexer $sourceIndexer @deprecated + * @param DefaultSourceProviderInterface $defaultSourceProvider @deprecated + * @param GetProductIdsBySkusInterface $getProductIdsBySkus @deprecated * @param LegacyIndexer $legacyIndexer * @SuppressWarnings(PHPMD.LongVariable) */ @@ -71,10 +56,7 @@ public function __construct( ) { $this->bulkInventoryTransferValidator = $inventoryTransferValidator; $this->bulkInventoryTransfer = $bulkInventoryTransfer; - $this->getProductIdsBySkus = $getProductIdsBySkus; $this->legacyIndexer = $legacyIndexer; - $this->defaultSourceProvider = $defaultSourceProvider; - $this->sourceIndexer = $sourceIndexer; } /** @@ -113,14 +95,6 @@ public function execute( $unassignFromOrigin ); - $this->sourceIndexer->executeList([$originSource, $destinationSource]); - - if (($this->defaultSourceProvider->getCode() === $originSource) || - ($this->defaultSourceProvider->getCode() === $destinationSource)) { - $productIds = array_values($this->getProductIdsBySkus->execute($skus)); - $this->reindexLegacy($productIds); - } - return true; } } diff --git a/InventoryLegacySynchronization/Model/ResourceModel/UpdateLegacyStockItemsData.php b/InventoryLegacySynchronization/Model/ResourceModel/UpdateLegacyStockItemsData.php index 67cd14ff5b1a..b292551dbda1 100644 --- a/InventoryLegacySynchronization/Model/ResourceModel/UpdateLegacyStockItemsData.php +++ b/InventoryLegacySynchronization/Model/ResourceModel/UpdateLegacyStockItemsData.php @@ -35,6 +35,10 @@ public function __construct( */ public function execute(array $legacyItemsData): void { + if (empty($legacyItemsData)) { + return; + } + $tableName = $this->resourceConnection->getTableName('cataloginventory_stock_item'); $connection = $this->resourceConnection->getConnection(); diff --git a/InventoryLegacySynchronization/Model/ResourceModel/UpdateSourceItemsData.php b/InventoryLegacySynchronization/Model/ResourceModel/UpdateSourceItemsData.php index 1dd068dc94c0..98411334e8ba 100644 --- a/InventoryLegacySynchronization/Model/ResourceModel/UpdateSourceItemsData.php +++ b/InventoryLegacySynchronization/Model/ResourceModel/UpdateSourceItemsData.php @@ -35,6 +35,10 @@ public function __construct( */ public function execute(array $sourceItemsData): void { + if (empty($sourceItemsData)) { + return; + } + $tableName = $this->resourceConnection->getTableName('inventory_source_item'); $connection = $this->resourceConnection->getConnection(); diff --git a/InventoryLegacySynchronization/Model/ToMsi/SetDataToSourceItem.php b/InventoryLegacySynchronization/Model/ToMsi/SetDataToSourceItem.php index 3b112ae3be4a..31095c0713ac 100644 --- a/InventoryLegacySynchronization/Model/ToMsi/SetDataToSourceItem.php +++ b/InventoryLegacySynchronization/Model/ToMsi/SetDataToSourceItem.php @@ -98,6 +98,11 @@ public function execute(array $legacyItemsData): void foreach ($legacyItemsData as $legacyItemData) { $productId = (int) $legacyItemData[StockItemInterface::PRODUCT_ID]; + + if (!isset($productSkusById[$productId], $legacyStockItemsByProductsIds[$productId])) { + continue; + } + $productSku = $productSkusById[$productId]; /** @var StockItemInterface $legacyStockItem */ @@ -111,14 +116,16 @@ public function execute(array $legacyItemsData): void ]; } - $this->updateSourceItemsData->execute($sourceItemsData); + if (!empty($sourceItemsData)) { + $this->updateSourceItemsData->execute($sourceItemsData); - $sourceItemsToReindex = $this->getDefaultSourceItemsBySkus->execute(array_values($productSkusById)); - $sourceItemsIdsToReindex = []; - foreach ($sourceItemsToReindex as $sourceItemToReindex) { - $sourceItemsIdsToReindex[] = (int) $sourceItemToReindex->getId(); - } + $sourceItemsToReindex = $this->getDefaultSourceItemsBySkus->execute(array_values($productSkusById)); + $sourceItemsIdsToReindex = []; + foreach ($sourceItemsToReindex as $sourceItemToReindex) { + $sourceItemsIdsToReindex[] = (int)$sourceItemToReindex->getId(); + } - $this->sourceItemIndexer->executeList($sourceItemsIdsToReindex); + $this->sourceItemIndexer->executeList($sourceItemsIdsToReindex); + } } } diff --git a/InventoryLegacySynchronization/Plugin/SetZeroQuantityToLegacyAtBulkUnassign.php b/InventoryLegacySynchronization/Plugin/SetZeroQuantityToLegacyAtBulkUnassign.php index 06da8078ae21..b744f0faec4c 100644 --- a/InventoryLegacySynchronization/Plugin/SetZeroQuantityToLegacyAtBulkUnassign.php +++ b/InventoryLegacySynchronization/Plugin/SetZeroQuantityToLegacyAtBulkUnassign.php @@ -9,7 +9,7 @@ namespace Magento\InventoryLegacySynchronization\Plugin; use Magento\InventoryApi\Api\Data\SourceItemInterface; -use Magento\InventoryCatalogApi\Api\BulkSourceUnassignInterface; +use Magento\InventoryCatalogApi\Api\BulkInventoryTransferInterface; use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; use Magento\InventoryLegacySynchronization\Model\Synchronize; @@ -42,7 +42,7 @@ public function __construct( } /** - * @param BulkSourceUnassignInterface $subject + * @param BulkInventoryTransferInterface $subject * @param int $result * @param array $skus * @param array $sourceCodes @@ -51,7 +51,7 @@ public function __construct( * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function afterExecute( - BulkSourceUnassignInterface $subject, + BulkInventoryTransferInterface $subject, int $result, array $skus, array $sourceCodes From b10450f89a3cdcbf1582cd064a5f687a42c2b22c Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Fri, 22 Mar 2019 16:06:11 +0100 Subject: [PATCH 045/231] Removed file --- ...SetZeroQuantityToLegacyStockItems.php.orig | 63 ------------------- 1 file changed, 63 deletions(-) delete mode 100644 InventoryLegacySynchronization/Model/ResourceModel/SetZeroQuantityToLegacyStockItems.php.orig diff --git a/InventoryLegacySynchronization/Model/ResourceModel/SetZeroQuantityToLegacyStockItems.php.orig b/InventoryLegacySynchronization/Model/ResourceModel/SetZeroQuantityToLegacyStockItems.php.orig deleted file mode 100644 index e525e574e0d4..000000000000 --- a/InventoryLegacySynchronization/Model/ResourceModel/SetZeroQuantityToLegacyStockItems.php.orig +++ /dev/null @@ -1,63 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryLegacySynchronization\Model\ResourceModel; - -use Magento\CatalogInventory\Api\Data\StockItemInterface; -use Magento\Framework\App\ResourceConnection; -use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface; - -/** - * Set quantity=0 to legacy cataloginventory_stock_item table for a set of skus via plain MySql query - */ -class SetZeroQuantityToLegacyStockItems -{ - /** - * @var ResourceConnection - */ - private $resourceConnection; - - /** - * @var GetProductIdsBySkusInterface - */ - private $getProductIdsBySkus; - - /** - * @param ResourceConnection $resourceConnection - * @param GetProductIdsBySkusInterface $getProductIdsBySkus - */ - public function __construct( - ResourceConnection $resourceConnection, - GetProductIdsBySkusInterface $getProductIdsBySkus - ) { - $this->resourceConnection = $resourceConnection; - $this->getProductIdsBySkus = $getProductIdsBySkus; - } - - /** - * @param array $skus - * @return void - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ - public function execute(array $skus): void - { - $productIds = array_values($this->getProductIdsBySkus->execute($skus)); - - $connection = $this->resourceConnection->getConnection(); - $connection->update( - $this->resourceConnection->getTableName('cataloginventory_stock_item'), - [ - StockItemInterface::QTY => 0, - StockItemInterface::IS_IN_STOCK => 0, - ], - [ - StockItemInterface::PRODUCT_ID . ' IN (?)' => $productIds, - 'website_id = ?' => 0, - ] - ); - } -} From 88c023540a1de152c069a854535d8c263f5df819 Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Fri, 22 Mar 2019 18:44:05 +0100 Subject: [PATCH 046/231] Data fetch optimization --- .../Model/Synchronize.php | 4 ++++ ...CatalogInventoryAtSourceItemsSavePlugin.php | 18 ++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/InventoryLegacySynchronization/Model/Synchronize.php b/InventoryLegacySynchronization/Model/Synchronize.php index 03a0031d845e..d60bde0a8706 100644 --- a/InventoryLegacySynchronization/Model/Synchronize.php +++ b/InventoryLegacySynchronization/Model/Synchronize.php @@ -147,6 +147,10 @@ private function executeSync(string $destination, array $items): void */ public function execute(string $destination, array $items): void { + if (empty($items)) { + return; + } + if ($this->isAsyncLegacyAlignment->execute()) { $this->executeAsync($destination, $items); } else { diff --git a/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php b/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php index 6d004b8d1818..23927a62a7df 100644 --- a/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php +++ b/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php @@ -70,20 +70,26 @@ public function __construct( public function afterExecute(SourceItemsSaveInterface $subject, $result, array $sourceItems): void { $sourceItemsData = []; + $skus = []; foreach ($sourceItems as $sourceItem) { - if ($sourceItem->getSourceCode() !== $this->defaultSourceProvider->getCode()) { + $skus[] = $sourceItem->getSku(); + } + + $productTypes = $this->getProductTypeBySku->execute($skus); + $defaultSourceCode = $this->defaultSourceProvider->getCode(); + + foreach ($sourceItems as $sourceItem) { + if ($sourceItem->getSourceCode() !== $defaultSourceCode) { continue; } $sku = $sourceItem->getSku(); - - $productTypes = $this->getProductTypeBySku->execute([$sku]); - if (isset($productTypes[$sku])) { - $typeId = $productTypes[$sku]; - } else { + if (!isset($productTypes[$sku])) { continue; } + $typeId = $productTypes[$sku]; + if (false === $this->isSourceItemsAllowedForProductType->execute($typeId)) { continue; } From 25faaf6fb1cf955cadc95543f7e9e324964d669e Mon Sep 17 00:00:00 2001 From: Maksym Novik <novik.kor@gmail.com> Date: Thu, 28 Mar 2019 21:00:13 +0200 Subject: [PATCH 047/231] Add "Ready for Pickup" button on the Order page in Admin Panel which notifies Customer that Order could be picked up #2034. Added the button itself --- .../Adminhtml/Order/View/ReadyForPickup.php | 59 +++++++++++++++++++ InventoryInStorePickupAdminUi/composer.json | 3 +- InventoryInStorePickupAdminUi/etc/module.xml | 1 + .../adminhtml/layout/sales_order_view.xml | 14 +++++ 4 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php create mode 100644 InventoryInStorePickupAdminUi/view/adminhtml/layout/sales_order_view.xml diff --git a/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php b/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php new file mode 100644 index 000000000000..2f578c2c6bad --- /dev/null +++ b/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php @@ -0,0 +1,59 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickupAdminUi\Block\Adminhtml\Order\View; + +/** + * TODO: is it possible to replace with UI Component? + * + * @package Magento\InventoryInStorePickupAdminUi\Block\Adminhtml\Order\View + */ +class ReadyForPickup extends \Magento\Backend\Block\Widget\Form\Container +{ + const ADMIN_SALES_EMAIL_RESOURCE = 'Magento_Sales::emails'; + + /** + * Block group + * + * @var string + */ + protected $_blockGroup = 'Magento_Sales'; + + /** + * Rendering Ready for Pickup button + */ + protected function _construct() + { + $this->_objectId = 'order_id'; + $this->_controller = 'adminhtml_order'; + $this->_mode = 'view'; + + if ($this->isEmailsSendingAllowed()) {/* TODO: add is order ready check */ + $message = __('Are you sure you want to notify the customer that order is ready for pickup?'); + $this->addButton( + 'ready_for_pickup', + [ + 'label' => __('Notify Order is Ready for Pickup'), + 'class' => 'action-default ready-for-pickup', + 'onclick' => sprintf( + "confirmSetLocation('%s', '%s')", + $message, + $this->getUrl('*/*/notifyPickup') + ) + ] + ); + } + } + + /** + * @return bool + */ + private function isEmailsSendingAllowed():bool + { + return $this->_authorization->isAllowed(self::ADMIN_SALES_EMAIL_RESOURCE); + } +} diff --git a/InventoryInStorePickupAdminUi/composer.json b/InventoryInStorePickupAdminUi/composer.json index 4a3b97443645..7a34814f04f8 100644 --- a/InventoryInStorePickupAdminUi/composer.json +++ b/InventoryInStorePickupAdminUi/composer.json @@ -6,7 +6,8 @@ "magento/framework": "*", "magento/module-ui": "*", "magento/module-inventory-in-store-pickup-api": "*", - "magento/module-inventory-admin-ui": "*" + "magento/module-inventory-admin-ui": "*", + "magento/module-sales": "*" }, "type": "magento2-module", "license": [ diff --git a/InventoryInStorePickupAdminUi/etc/module.xml b/InventoryInStorePickupAdminUi/etc/module.xml index 0f5187cf90da..2378b4cd90e5 100644 --- a/InventoryInStorePickupAdminUi/etc/module.xml +++ b/InventoryInStorePickupAdminUi/etc/module.xml @@ -10,6 +10,7 @@ <sequence> <module name="Magento_InventoryInStorePickup" /> <module name="Magento_Shipping" /> + <module name="Magento_Sales" /> </sequence> </module> </config> diff --git a/InventoryInStorePickupAdminUi/view/adminhtml/layout/sales_order_view.xml b/InventoryInStorePickupAdminUi/view/adminhtml/layout/sales_order_view.xml new file mode 100644 index 000000000000..57c32390ad65 --- /dev/null +++ b/InventoryInStorePickupAdminUi/view/adminhtml/layout/sales_order_view.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> + <body> + <referenceContainer name="content"> + <block class="Magento\InventoryInStorePickupAdminUi\Block\Adminhtml\Order\View\ReadyForPickup" name="sales_order_ready_for_pickup"/> + </referenceContainer> + </body> +</page> From ec47ef9aaa86a0bafa11afde93ff4dc76e400da2 Mon Sep 17 00:00:00 2001 From: Bettina Cerban <bettinacerban@gmail.com> Date: Thu, 28 Mar 2019 12:11:41 -0300 Subject: [PATCH 048/231] implement partial stock transfer API --- .../Model/BulkPartialInventoryTransfer.php | 125 ++++++++++++ .../GetSourceItemsBySkuAndSourceCodes.php | 50 +++++ .../Model/PartialInventoryTransfer.php | 79 ++++++++ .../TransferInventoryPartially.php | 94 +++++++++ .../Validator/PartialTransferValidator.php | 105 ++++++++++ .../Api/Bulk/PartialInventoryTransferTest.php | 183 ++++++++++++++++++ .../GetSourceItemsBySkuAndSourceCodesTest.php | 39 ++++ InventoryCatalog/etc/di.xml | 7 + .../BulkPartialInventoryTransferInterface.php | 22 +++ .../PartialInventoryTransferInterface.php | 63 ++++++ ...ialInventoryTransferValidatorInterface.php | 22 +++ InventoryCatalogApi/etc/webapi.xml | 6 + 12 files changed, 795 insertions(+) create mode 100644 InventoryCatalog/Model/BulkPartialInventoryTransfer.php create mode 100644 InventoryCatalog/Model/GetSourceItemsBySkuAndSourceCodes.php create mode 100644 InventoryCatalog/Model/PartialInventoryTransfer.php create mode 100644 InventoryCatalog/Model/ResourceModel/TransferInventoryPartially.php create mode 100644 InventoryCatalog/Model/Source/Validator/PartialTransferValidator.php create mode 100644 InventoryCatalog/Test/Api/Bulk/PartialInventoryTransferTest.php create mode 100644 InventoryCatalog/Test/Integration/GetSourceItemsBySkuAndSourceCodesTest.php create mode 100644 InventoryCatalogApi/Api/BulkPartialInventoryTransferInterface.php create mode 100644 InventoryCatalogApi/Api/Data/PartialInventoryTransferInterface.php create mode 100644 InventoryCatalogApi/Model/PartialInventoryTransferValidatorInterface.php diff --git a/InventoryCatalog/Model/BulkPartialInventoryTransfer.php b/InventoryCatalog/Model/BulkPartialInventoryTransfer.php new file mode 100644 index 000000000000..d83a10adc312 --- /dev/null +++ b/InventoryCatalog/Model/BulkPartialInventoryTransfer.php @@ -0,0 +1,125 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalog\Model; + +use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\CatalogInventory\Model\Indexer\Stock as LegacyIndexer; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\InventoryCatalog\Model\ResourceModel\TransferInventoryPartially; +use Magento\InventoryCatalogApi\Api\BulkPartialInventoryTransferInterface; +use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; +use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface; +use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface; +use Magento\InventoryCatalogApi\Model\PartialInventoryTransferValidatorInterface; +use Magento\InventoryIndexer\Indexer\Source\SourceIndexer; + +class BulkPartialInventoryTransfer implements BulkPartialInventoryTransferInterface +{ + /** @var PartialInventoryTransferValidatorInterface */ + private $transferValidator; + + /** @var TransferInventoryPartially */ + private $transferCommand; + + /** @var GetProductIdsBySkusInterface */ + private $productIdsBySkus; + + /** @var GetSourceItemsBySkuAndSourceCodes */ + private $sourceItemsBySku; + + /** @var DefaultSourceProviderInterface */ + private $defaultSourceProvider; + + /** @var SourceIndexer */ + private $sourceIndexer; + + /** @var LegacyIndexer */ + private $legacyIndexer; + + /** + * BulkPartialInventoryTransfer constructor. + * @param PartialInventoryTransferValidatorInterface $partialInventoryTransferValidator + * @param TransferInventoryPartially $transferInventoryPartiallyCommand + * @param GetProductIdsBySkusInterface $getProductIdsBySkus + * @param GetSourceItemsBySkuAndSourceCodes $getSourceItemsBySkuAndSourceCodes + * @param DefaultSourceProviderInterface $defaultSourceProvider + * @param SourceIndexer $sourceIndexer + * @param LegacyIndexer $legacyIndexer + */ + public function __construct( + PartialInventoryTransferValidatorInterface $partialInventoryTransferValidator, + TransferInventoryPartially $transferInventoryPartiallyCommand, + GetProductIdsBySkusInterface $getProductIdsBySkus, + GetSourceItemsBySkuAndSourceCodes $getSourceItemsBySkuAndSourceCodes, + DefaultSourceProviderInterface $defaultSourceProvider, + SourceIndexer $sourceIndexer, + LegacyIndexer $legacyIndexer + ) + { + $this->transferValidator = $partialInventoryTransferValidator; + $this->transferCommand = $transferInventoryPartiallyCommand; + $this->productIdsBySkus = $getProductIdsBySkus; + $this->sourceItemsBySku = $getSourceItemsBySkuAndSourceCodes; + $this->defaultSourceProvider = $defaultSourceProvider; + $this->sourceIndexer = $sourceIndexer; + $this->legacyIndexer = $legacyIndexer; + } + + /** + * Run bulk partial inventory transfer for specified items. + * + * @param PartialInventoryTransferInterface[] $items + * @return SourceItemInterface[] + */ + public function execute(array $items): array + { + $sources = []; + $processedSkus = []; + $sourceItems = []; + + foreach ($items as $item) { + $validationResult = $this->transferValidator->validate($item); + if ($validationResult->isValid()) { + $this->transferCommand->execute($item); + + $processedSkus[] = $item->getSku(); + $sources[] = $item->getOriginSourceCode(); + $sources[] = $item->getDestinationSourceCode(); + } + $sourceItems += $this->sourceItemsBySku->execute($item->getSku(), [$item->getOriginSourceCode(), $item->getDestinationSourceCode()]); + } + + $this->updateIndexes($sources, $processedSkus); + return $sourceItems; + } + + /** + * @param string[] $sources + * @param string[] $skus + */ + private function updateIndexes(array $sources, array $skus) + { + $sources = array_unique($sources); + $this->sourceIndexer->executeList($sources); + + if (in_array($this->defaultSourceProvider->getCode(), $sources)) { + $this->updateLegacyIndex($skus); + } + } + + /** + * @param string[] $skus + */ + private function updateLegacyIndex(array $skus) + { + try { + $productIds = $this->productIdsBySkus->execute($skus); + $this->legacyIndexer->executeList($productIds); + } catch (NoSuchEntityException $e) {} + } +} \ No newline at end of file diff --git a/InventoryCatalog/Model/GetSourceItemsBySkuAndSourceCodes.php b/InventoryCatalog/Model/GetSourceItemsBySkuAndSourceCodes.php new file mode 100644 index 000000000000..a8cc954d9af8 --- /dev/null +++ b/InventoryCatalog/Model/GetSourceItemsBySkuAndSourceCodes.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalog\Model; + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryApi\Api\SourceItemRepositoryInterface; + +class GetSourceItemsBySkuAndSourceCodes +{ + /** @var SearchCriteriaBuilder */ + private $searchCriteriaBuilder; + + /** @var SourceItemRepositoryInterface */ + private $sourceItemRepository; + + /** + * GetSourceItemsBySkuAndSourceCodes constructor. + * @param SearchCriteriaBuilder $searchCriteriaBuilder + * @param SourceItemRepositoryInterface $sourceItemRepository + */ + public function __construct( + SearchCriteriaBuilder $searchCriteriaBuilder, + SourceItemRepositoryInterface $sourceItemRepository + ) + { + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->sourceItemRepository = $sourceItemRepository; + } + + /** + * @param string $sku + * @param array $sourceCodes + * @return SourceItemInterface[] + */ + public function execute(string $sku, array $sourceCodes) + { + $searchCriteria = $this->searchCriteriaBuilder + ->addFilter(SourceItemInterface::SKU, $sku) + ->addFilter(SourceItemInterface::SOURCE_CODE, [$sourceCodes], 'in') + ->create(); + + return $this->sourceItemRepository->getList($searchCriteria)->getItems(); + } +} \ No newline at end of file diff --git a/InventoryCatalog/Model/PartialInventoryTransfer.php b/InventoryCatalog/Model/PartialInventoryTransfer.php new file mode 100644 index 000000000000..0918062410bd --- /dev/null +++ b/InventoryCatalog/Model/PartialInventoryTransfer.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalog\Model; + +use Magento\Framework\Api\AbstractSimpleObject; +use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface; + +class PartialInventoryTransfer extends AbstractSimpleObject implements PartialInventoryTransferInterface +{ + + /** + * @return string + */ + public function getSku(): string + { + return $this->_get(self::SKU); + } + + /** + * @param string $sku + */ + public function setSku(string $sku): void + { + $this->setData(self::SKU, $sku); + } + + /** + * @return float + */ + public function getQty(): float + { + return $this->_get(self::QTY); + } + + /** + * @param float $qty + */ + public function setQty(float $qty): void + { + $this->setData(self::QTY, $qty); + } + + /** + * @return string + */ + public function getOriginSourceCode(): string + { + return $this->_get(self::ORIGIN_SOURCE_CODE); + } + + /** + * @param string $code + */ + public function setOriginSourceCode(string $code): void + { + $this->setData(self::ORIGIN_SOURCE_CODE, $code); + } + + /** + * @return string + */ + public function getDestinationSourceCode(): string + { + return $this->_get(self::DESTINATION_SOURCE_CODE); + } + + /** + * @param string $code + */ + public function setDestinationSourceCode(string $code): void + { + $this->setData(self::DESTINATION_SOURCE_CODE, $code); + } +} \ No newline at end of file diff --git a/InventoryCatalog/Model/ResourceModel/TransferInventoryPartially.php b/InventoryCatalog/Model/ResourceModel/TransferInventoryPartially.php new file mode 100644 index 000000000000..275b8294407e --- /dev/null +++ b/InventoryCatalog/Model/ResourceModel/TransferInventoryPartially.php @@ -0,0 +1,94 @@ +<?php +/** + * + * Copyright © 2019 ebizmarts. All rights reserved. + * See LICENSE.txt for license details. + * + */ + +namespace Magento\InventoryCatalog\Model\ResourceModel; + +use Magento\Framework\App\ResourceConnection; +use Magento\Inventory\Model\ResourceModel\SourceItem; +use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; +use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface; + +class TransferInventoryPartially +{ + /** @var ResourceConnection */ + private $resourceConnection; + + /** @var DefaultSourceProviderInterface */ + private $defaultSourceProvider; + + /** @var SetDataToLegacyStockItem */ + private $setDataToLegacyStockItemCommand; + + /** + * TransferInventoryPartially constructor. + * @param ResourceConnection $resourceConnection + * @param DefaultSourceProviderInterface $defaultSourceProvider + * @param SetDataToLegacyStockItem $setDataToLegacyCatalogInventoryCommand + */ + public function __construct( + ResourceConnection $resourceConnection, + DefaultSourceProviderInterface $defaultSourceProvider, + SetDataToLegacyStockItem $setDataToLegacyCatalogInventoryCommand + ) + { + $this->resourceConnection = $resourceConnection; + $this->defaultSourceProvider = $defaultSourceProvider; + $this->setDataToLegacyStockItemCommand = $setDataToLegacyCatalogInventoryCommand; + } + + public function execute(PartialInventoryTransferInterface $transfer) + { + $tableName = $this->resourceConnection->getTableName(SourceItem::TABLE_NAME_SOURCE_ITEM); + $connection = $this->resourceConnection->getConnection(); + $connection->beginTransaction(); + + $originSourceItemData = $this->getSourceItemData($transfer->getSku(), $transfer->getOriginSourceCode()); + $destSourceItemData = $this->getSourceItemData($transfer->getSku(), $transfer->getDestinationSourceCode()); + + $updatedQtyAtOrigin = $originSourceItemData === null ? 0.0 : (float) $originSourceItemData[SourceItemInterface::QUANTITY] - $transfer->getQty(); + $updatedQtyAtDest = $destSourceItemData === null ? 0.0 : (float) $destSourceItemData[SourceItemInterface::QUANTITY] + $transfer->getQty(); + + $originUpdate = [SourceItemInterface::QUANTITY => $updatedQtyAtOrigin]; + $destUpdate = [SourceItemInterface::QUANTITY => $updatedQtyAtDest, SourceItemInterface::STATUS => SourceItemInterface::STATUS_IN_STOCK]; + + $connection->update($tableName, $originUpdate, [ + SourceItemInterface::SOURCE_CODE . '=?' => $transfer->getOriginSourceCode(), + SourceItemInterface::SKU . '=?' => $transfer->getSku(), + ]); + $connection->update($tableName, $destUpdate, [ + SourceItemInterface::SOURCE_CODE . '=?' => $transfer->getDestinationSourceCode(), + SourceItemInterface::SKU . '=?' => $transfer->getSku(), + ]); + + if ($transfer->getOriginSourceCode() === $this->defaultSourceProvider->getCode()) { + $this->setDataToLegacyStockItemCommand->execute($transfer->getSku(), $updatedQtyAtOrigin, $originSourceItemData[SourceItemInterface::STATUS]); + } elseif ($transfer->getDestinationSourceCode() === $this->defaultSourceProvider->getCode()) { + $this->setDataToLegacyStockItemCommand->execute($transfer->getSku(), $updatedQtyAtDest, SourceItemInterface::STATUS_IN_STOCK); + } + + $connection->commit(); + } + + private function getSourceItemData(string $sku, string $source): ?array + { + $connection = $this->resourceConnection->getConnection(); + $tableName = $this->resourceConnection->getTableName(SourceItem::TABLE_NAME_SOURCE_ITEM); + + $query = $connection->select()->from($tableName) + ->where(SourceItemInterface::SOURCE_CODE . ' = ?', $source) + ->where(SourceItemInterface::SKU . ' = ?', $sku); + + $res = $connection->fetchRow($query); + if ($res === false) { + return null; + } + + return $res; + } +} \ No newline at end of file diff --git a/InventoryCatalog/Model/Source/Validator/PartialTransferValidator.php b/InventoryCatalog/Model/Source/Validator/PartialTransferValidator.php new file mode 100644 index 000000000000..8a949802e906 --- /dev/null +++ b/InventoryCatalog/Model/Source/Validator/PartialTransferValidator.php @@ -0,0 +1,105 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalog\Model\Source\Validator; + +use Magento\Framework\Validation\ValidationResult; +use Magento\Framework\Validation\ValidationResultFactory; +use Magento\Framework\Api\SearchCriteriaBuilder; + +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryApi\Api\SourceRepositoryInterface; +use Magento\InventoryApi\Api\SourceItemRepositoryInterface; +use Magento\InventoryCatalog\Model\GetSourceItemsBySkuAndSourceCodes; +use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface; +use Magento\InventoryCatalogApi\Model\PartialInventoryTransferValidatorInterface; + +class PartialTransferValidator implements PartialInventoryTransferValidatorInterface +{ + /** @var ValidationResultFactory */ + private $validationResultFactory; + + /** @var SourceRepositoryInterface */ + private $sourceRepository; + + /** @var GetSourceItemsBySkuAndSourceCodes */ + private $getSourceItem; + + /** + * PartialTransferValidator constructor. + * @param ValidationResultFactory $validationResultFactory + * @param SourceRepositoryInterface $sourceRepository + * @param GetSourceItemsBySkuAndSourceCodes $getSourceItemsBySkuAndSourceCodes + */ + public function __construct( + ValidationResultFactory $validationResultFactory, + SourceRepositoryInterface $sourceRepository, + GetSourceItemsBySkuAndSourceCodes $getSourceItemsBySkuAndSourceCodes + ) + { + $this->validationResultFactory = $validationResultFactory; + $this->getSourceItem = $getSourceItemsBySkuAndSourceCodes; + $this->sourceRepository = $sourceRepository; + } + + /** + * Validates a partial transfer request. + * + * @param PartialInventoryTransferInterface $item + * @return ValidationResult + */ + public function validate(PartialInventoryTransferInterface $item): ValidationResult + { + $errors = []; + + try { + $this->sourceRepository->get($item->getOriginSourceCode()); + } catch (NoSuchEntityException $e) { + $errors[] = __('Origin source %sourceCode does not exist', ['sourceCode' => $item->getOriginSourceCode()]); + } + + try { + $this->sourceRepository->get($item->getDestinationSourceCode()); + } catch (NoSuchEntityException $e) { + $errors[] = __('Destination source %sourceCode does not exist', ['sourceCode' => $item->getDestinationSourceCode()]); + } + + if ($item->getOriginSourceCode() === $item->getDestinationSourceCode()) { + $errors[] = __('Cannot transfer a source on itself'); + } + + try { + $originSourceItem = $this->getSourceItemBySkuAndSource($item->getSku(), $item->getOriginSourceCode()); + if ($originSourceItem->getQuantity() < $item->getQty()) { + $errors[] = __('Requested transfer amount for sku %sku is not available'); + } + + $this->getSourceItemBySkuAndSource($item->getSku(), $item->getOriginSourceCode()); + } catch (NoSuchEntityException $e) { + $errors[] = $e->getMessage(); + } + + return $this->validationResultFactory->create(['errors' => $errors]); + } + + /** + * @param string $sku + * @param string $sourceCode + * @return SourceItemInterface + * @throws NoSuchEntityException + */ + private function getSourceItemBySkuAndSource(string $sku, string $sourceCode): SourceItemInterface + { + $result = $this->getSourceItem->execute($sku, [$sourceCode]); + if (!count($result)) { + throw new NoSuchEntityException(__('Source item for %sku and %sourceCode does not exist', ['sku' => $sku, 'sourceCode' => $sourceCode])); + } + + return array_shift($result); + } +} \ No newline at end of file diff --git a/InventoryCatalog/Test/Api/Bulk/PartialInventoryTransferTest.php b/InventoryCatalog/Test/Api/Bulk/PartialInventoryTransferTest.php new file mode 100644 index 000000000000..66028a1839c2 --- /dev/null +++ b/InventoryCatalog/Test/Api/Bulk/PartialInventoryTransferTest.php @@ -0,0 +1,183 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalog\Test\Api\Bulk; + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Webapi\Rest\Request; +use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryApi\Api\SourceItemRepositoryInterface; +use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\WebapiAbstract; + +class PartialInventoryTransferTest extends WebapiAbstract +{ + const RESOURCE_PATH = '/V1/inventory/bulk-partial-source-transfer'; + + /** @var SourceItemRepositoryInterface */ + private $sourceItemRepository; + + /** @var SearchCriteriaBuilder */ + private $searchCriteriaBuilder; + + public function setUp() + { + $this->sourceItemRepository = Bootstrap::getObjectManager()->get(SourceItemRepositoryInterface::class); + $this->searchCriteriaBuilder = Bootstrap::getObjectManager()->get(SearchCriteriaBuilder::class); + } + + /** + * @magentoApiDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php + * @magentoApiDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php + * @magentoApiDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php + */ + public function testValidTransfer() + { + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH, + 'httpMethod' => Request::HTTP_METHOD_POST + ] + ]; + + $this->_webApiCall($serviceInfo, ['items' => [$this->getTransferItem('SKU-1', 1, 'eu-3', 'eu-2')]]); + + $originSourceItem = $this->getSourceItem('SKU-1', 'eu-3'); + $destinationSourceItem = $this->getSourceItem('SKU-1', 'eu-2'); + + if ($originSourceItem === null || $destinationSourceItem === null) { + $this->fail('Both source items should exist.'); + } + + $this->assertEquals(9, $originSourceItem->getQuantity()); + $this->assertEquals(4, $destinationSourceItem->getQuantity()); + } + + /** + * @magentoApiDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php + * @magentoApiDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php + * @magentoApiDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php + */ + public function testInvalidTransferOrigin() + { + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH, + 'httpMethod' => Request::HTTP_METHOD_POST + ] + ]; + + $result = $this->_webApiCall($serviceInfo, ['items' => [$this->getTransferItem('SKU-1', 1, 'eu-999', 'eu-2')]]); + $this->assertEquals(1, count($result)); + + $destinationItem = array_shift($result); + $this->assertEquals(3, $destinationItem[SourceItemInterface::QUANTITY]); + } + + /** + * @magentoApiDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php + * @magentoApiDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php + * @magentoApiDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php + */ + public function testInvalidTransferDestination() + { + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH, + 'httpMethod' => Request::HTTP_METHOD_POST + ] + ]; + + $result = $this->_webApiCall($serviceInfo, ['items' => [$this->getTransferItem('SKU-1', 1, 'eu-3', 'eu-999')]]); + $this->assertEquals(1, count($result)); + + $originItem = array_shift($result); + $this->assertEquals(10, $originItem[SourceItemInterface::QUANTITY]); + } + + /** + * @magentoApiDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php + * @magentoApiDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php + * @magentoApiDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php + */ + public function testInvalidTransferOriginAndDestinationAreTheSame() + { + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH, + 'httpMethod' => Request::HTTP_METHOD_POST + ] + ]; + + $result = $this->_webApiCall($serviceInfo, ['items' => [$this->getTransferItem('SKU-1', 1, 'eu-3', 'eu-3')]]); + $this->assertEquals(1, count($result)); + + $originItem = array_shift($result); + $this->assertEquals(10, $originItem[SourceItemInterface::QUANTITY]); + } + + /** + * @magentoApiDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php + * @magentoApiDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php + * @magentoApiDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php + */ + public function testInvalidTransferQuantityGreaterThanAvailable() + { + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH, + 'httpMethod' => Request::HTTP_METHOD_POST + ] + ]; + + $this->_webApiCall($serviceInfo, ['items' => [$this->getTransferItem('SKU-1', 100, 'eu-3', 'eu-2')]]); + + $originSourceItem = $this->getSourceItem('SKU-1', 'eu-3'); + $destinationSourceItem = $this->getSourceItem('SKU-1', 'eu-2'); + + if ($originSourceItem === null || $destinationSourceItem === null) { + $this->fail('Both source items should exist.'); + } + + $this->assertEquals(10, $originSourceItem->getQuantity()); + $this->assertEquals(3, $destinationSourceItem->getQuantity()); + } + + /** + * @param string $sku + * @param float $qty + * @param string $origin + * @param string $destination + * @return array + */ + private function getTransferItem(string $sku, float $qty, string $origin, string $destination): array + { + return [ + PartialInventoryTransferInterface::SKU => $sku, + PartialInventoryTransferInterface::QTY => $qty, + PartialInventoryTransferInterface::ORIGIN_SOURCE_CODE => $origin, + PartialInventoryTransferInterface::DESTINATION_SOURCE_CODE => $destination + ]; + } + + /** + * @param string $sku + * @param string $sourceCode + * @return SourceItemInterface|null + */ + private function getSourceItem(string $sku, string $sourceCode): ?SourceItemInterface + { + $searchCriteria = $this->searchCriteriaBuilder + ->addFilter(SourceItemInterface::SKU, $sku) + ->addFilter(SourceItemInterface::SOURCE_CODE, $sourceCode) + ->create(); + + $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); + return empty($sourceItems) ? null : array_shift($sourceItems); + } +} diff --git a/InventoryCatalog/Test/Integration/GetSourceItemsBySkuAndSourceCodesTest.php b/InventoryCatalog/Test/Integration/GetSourceItemsBySkuAndSourceCodesTest.php new file mode 100644 index 000000000000..f286d3bd9828 --- /dev/null +++ b/InventoryCatalog/Test/Integration/GetSourceItemsBySkuAndSourceCodesTest.php @@ -0,0 +1,39 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalog\Test\Integration; + +use Magento\InventoryCatalog\Model\GetSourceItemsBySkuAndSourceCodes; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +class GetSourceItemsBySkuAndSourceCodesTest extends TestCase +{ + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php + */ + public function testExecuteSkuAssignedToSources() + { + $getSourceItems = Bootstrap::getObjectManager()->get(GetSourceItemsBySkuAndSourceCodes::class); + $items = $getSourceItems->execute('SKU-1', ['eu-1', 'eu-2']); + $this->assertEquals(2, count($items)); + } + + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php + */ + public function testExecuteSkuNotAssignedToSources() + { + $getSourceItems = Bootstrap::getObjectManager()->get(GetSourceItemsBySkuAndSourceCodes::class); + $items = $getSourceItems->execute('SKU-2', ['eu-1']); + $this->assertEmpty($items); + } +} diff --git a/InventoryCatalog/etc/di.xml b/InventoryCatalog/etc/di.xml index beab754f1e6f..967574f9f040 100644 --- a/InventoryCatalog/etc/di.xml +++ b/InventoryCatalog/etc/di.xml @@ -103,6 +103,13 @@ type="Magento\InventoryCatalog\Model\BulkSourceUnassign"/> <preference for="Magento\InventoryCatalogApi\Api\BulkInventoryTransferInterface" type="Magento\InventoryCatalog\Model\BulkInventoryTransfer"/> + <preference for="Magento\InventoryCatalogApi\Api\BulkPartialInventoryTransferInterface" + type="Magento\InventoryCatalog\Model\BulkPartialInventoryTransfer"/> + <preference for="Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface" + type="Magento\InventoryCatalog\Model\PartialInventoryTransfer"/> + <preference for="Magento\InventoryCatalogApi\Model\PartialInventoryTransferValidatorInterface" + type="Magento\InventoryCatalog\Model\Source\Validator\PartialTransferValidator"/> + <type name="\Magento\InventoryCatalogApi\Model\BulkSourceAssignValidatorChain"> <arguments> <argument name="validators" xsi:type="array"> diff --git a/InventoryCatalogApi/Api/BulkPartialInventoryTransferInterface.php b/InventoryCatalogApi/Api/BulkPartialInventoryTransferInterface.php new file mode 100644 index 000000000000..73046ee6b39c --- /dev/null +++ b/InventoryCatalogApi/Api/BulkPartialInventoryTransferInterface.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalogApi\Api; + +use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface; + +interface BulkPartialInventoryTransferInterface +{ + /** + * Run bulk partial inventory transfer for specified items. + * + * @param PartialInventoryTransferInterface[] $items + * @return SourceItemInterface[] + */ + public function execute(array $items): array; +} \ No newline at end of file diff --git a/InventoryCatalogApi/Api/Data/PartialInventoryTransferInterface.php b/InventoryCatalogApi/Api/Data/PartialInventoryTransferInterface.php new file mode 100644 index 000000000000..e78950e17203 --- /dev/null +++ b/InventoryCatalogApi/Api/Data/PartialInventoryTransferInterface.php @@ -0,0 +1,63 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalogApi\Api\Data; + +/** + * @api + * + * DTO to handle partial stock transfers from origin source to destination source, + * specifying the quantity to be transferred. + * @see \Magento\InventoryCatalogApi\Api\BulkPartialInventoryTransferInterface + */ +interface PartialInventoryTransferInterface extends \Magento\Framework\Api\ExtensibleDataInterface +{ + const SKU = 'sku'; + const QTY = 'qty'; + const ORIGIN_SOURCE_CODE = 'origin_source_code'; + const DESTINATION_SOURCE_CODE = 'destination_source_code'; + + /** + * @return string + */ + public function getSku(): string; + + /** + * @param string $sku + */ + public function setSku(string $sku): void; + + /** + * @return float + */ + public function getQty(): float; + + /** + * @param float $qty + */ + public function setQty(float $qty): void; + + /** + * @return string + */ + public function getOriginSourceCode(): string; + + /** + * @param string $code + */ + public function setOriginSourceCode(string $code): void; + + /** + * @return string + */ + public function getDestinationSourceCode(): string; + + /** + * @param string $code + */ + public function setDestinationSourceCode(string $code): void; +} \ No newline at end of file diff --git a/InventoryCatalogApi/Model/PartialInventoryTransferValidatorInterface.php b/InventoryCatalogApi/Model/PartialInventoryTransferValidatorInterface.php new file mode 100644 index 000000000000..356cbd3f18cb --- /dev/null +++ b/InventoryCatalogApi/Model/PartialInventoryTransferValidatorInterface.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalogApi\Model; + +use Magento\Framework\Validation\ValidationResult; +use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface; + +interface PartialInventoryTransferValidatorInterface +{ + /** + * Validates a partial transfer request. + * + * @param PartialInventoryTransferInterface $item + * @return ValidationResult + */ + public function validate(PartialInventoryTransferInterface $item): ValidationResult; +} \ No newline at end of file diff --git a/InventoryCatalogApi/etc/webapi.xml b/InventoryCatalogApi/etc/webapi.xml index 80897e53849d..76b14f4be02a 100644 --- a/InventoryCatalogApi/etc/webapi.xml +++ b/InventoryCatalogApi/etc/webapi.xml @@ -25,4 +25,10 @@ <resource ref="Magento_Catalog::products"/> </resources> </route> + <route url="/V1/inventory/bulk-partial-source-transfer" method="POST"> + <service class="Magento\InventoryCatalogApi\Api\BulkPartialInventoryTransferInterface" method="execute"/> + <resources> + <resource ref="Magento_Catalog::products"/> + </resources> + </route> </routes> \ No newline at end of file From 2ccfeb7ebb11a6ad08f980e3d0affa0677732ada Mon Sep 17 00:00:00 2001 From: Maksym Novik <novik.kor@gmail.com> Date: Thu, 28 Mar 2019 22:34:50 +0200 Subject: [PATCH 049/231] Add "Ready for Pickup" button on the Order page in Admin Panel which notifies Customer that Order could be picked up #2034. Added check if order is ready for pickup --- .../Model/Order/IsReadyForPickup.php | 93 +++++++++++++++++++ ...php => GetPickupLocationCodeByOrderId.php} | 2 +- .../SaveOrderPickupLocation.php | 2 - .../Order/GetPickupLocationForOrderPlugin.php | 10 +- .../Adminhtml/Order/View/ReadyForPickup.php | 41 +++++++- 5 files changed, 139 insertions(+), 9 deletions(-) create mode 100644 InventoryInStorePickup/Model/Order/IsReadyForPickup.php rename InventoryInStorePickup/Model/ResourceModel/OrderPickupLocation/{GetPickupLocationByOrderId.php => GetPickupLocationCodeByOrderId.php} (97%) diff --git a/InventoryInStorePickup/Model/Order/IsReadyForPickup.php b/InventoryInStorePickup/Model/Order/IsReadyForPickup.php new file mode 100644 index 000000000000..522db4a1178c --- /dev/null +++ b/InventoryInStorePickup/Model/Order/IsReadyForPickup.php @@ -0,0 +1,93 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickup\Model\Order; + +use Magento\InventoryApi\Api\Data\SourceItemInterface; + +class IsReadyForPickup +{ + /** + * @var \Magento\Sales\Api\OrderRepositoryInterface + */ + private $orderRepository; + + /** + * @var \Magento\InventoryApi\Api\SourceItemRepositoryInterface + */ + private $sourceItemRepository; + + /** + * @var \Magento\Framework\Api\SearchCriteriaBuilderFactory + */ + private $searchCriteriaBuilderFactory; + + /** + * IsReadyForPickup constructor. + * + * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository + * @param \Magento\InventoryApi\Api\SourceItemRepositoryInterface $sourceItemRepository + * @param \Magento\Framework\Api\SearchCriteriaBuilderFactory $searchCriteriaBuilder + */ + public function __construct( + \Magento\Sales\Api\OrderRepositoryInterface $orderRepository, + \Magento\InventoryApi\Api\SourceItemRepositoryInterface $sourceItemRepository, + \Magento\Framework\Api\SearchCriteriaBuilderFactory $searchCriteriaBuilder + ) { + $this->orderRepository = $orderRepository; + $this->sourceItemRepository = $sourceItemRepository; + $this->searchCriteriaBuilderFactory = $searchCriteriaBuilder; + } + + /** + * @param int $orderId + * + * @return bool + */ + public function execute(int $orderId):bool + { + $order = $this->orderRepository->get($orderId); + + if ($sourceCode = $order->getExtensionAttributes()->getPickupLocationCode()) { + foreach ($order->getItems() as $item) { + if (!$this->isItemFulfilled($item->getSku(), $sourceCode, (float)$item->getQtyOrdered())) { + return false; + } + } + + return true; + } + + return false; + } + + /** + * @param string $sku + * @param string $sourceCode + * @param float $qtyOrdered + * + * @return bool + */ + private function isItemFulfilled(string $sku, string $sourceCode, float $qtyOrdered):bool + { + $searchCriteria = $this->searchCriteriaBuilderFactory + ->create() + ->addFilter(SourceItemInterface::SOURCE_CODE, $sourceCode) + ->addFilter(SourceItemInterface::SKU, $sku) + ->create(); + + $sourceItems = $this->sourceItemRepository->getList($searchCriteria); + if ($sourceItems->getTotalCount()) { + /** @var SourceItemInterface $sourceItem */ + $sourceItem = current($sourceItems->getItems()); + + return $sourceItem->getQuantity() >= $qtyOrdered; + } + + return false; + } +} diff --git a/InventoryInStorePickup/Model/ResourceModel/OrderPickupLocation/GetPickupLocationByOrderId.php b/InventoryInStorePickup/Model/ResourceModel/OrderPickupLocation/GetPickupLocationCodeByOrderId.php similarity index 97% rename from InventoryInStorePickup/Model/ResourceModel/OrderPickupLocation/GetPickupLocationByOrderId.php rename to InventoryInStorePickup/Model/ResourceModel/OrderPickupLocation/GetPickupLocationCodeByOrderId.php index 307b0bc7fe7b..eb286b3c833b 100644 --- a/InventoryInStorePickup/Model/ResourceModel/OrderPickupLocation/GetPickupLocationByOrderId.php +++ b/InventoryInStorePickup/Model/ResourceModel/OrderPickupLocation/GetPickupLocationCodeByOrderId.php @@ -12,7 +12,7 @@ /** * Get Pickup Location identifier by order identifier. */ -class GetPickupLocationByOrderId +class GetPickupLocationCodeByOrderId { private const ORDER_ID = 'order_id'; diff --git a/InventoryInStorePickup/Model/ResourceModel/OrderPickupLocation/SaveOrderPickupLocation.php b/InventoryInStorePickup/Model/ResourceModel/OrderPickupLocation/SaveOrderPickupLocation.php index 15b07555f4e1..47ec145f2200 100644 --- a/InventoryInStorePickup/Model/ResourceModel/OrderPickupLocation/SaveOrderPickupLocation.php +++ b/InventoryInStorePickup/Model/ResourceModel/OrderPickupLocation/SaveOrderPickupLocation.php @@ -23,8 +23,6 @@ class SaveOrderPickupLocation private $connection; /** - * GetPickupLocationByOrderId constructor. - * * @param \Magento\Framework\App\ResourceConnection $connection */ public function __construct( diff --git a/InventoryInStorePickup/Plugin/Sales/Order/GetPickupLocationForOrderPlugin.php b/InventoryInStorePickup/Plugin/Sales/Order/GetPickupLocationForOrderPlugin.php index fa34698bcebc..e50095e695cb 100644 --- a/InventoryInStorePickup/Plugin/Sales/Order/GetPickupLocationForOrderPlugin.php +++ b/InventoryInStorePickup/Plugin/Sales/Order/GetPickupLocationForOrderPlugin.php @@ -7,7 +7,7 @@ namespace Magento\InventoryInStorePickup\Plugin\Sales\Order; -use Magento\InventoryInStorePickup\Model\ResourceModel\OrderPickupLocation\GetPickupLocationByOrderId; +use Magento\InventoryInStorePickup\Model\ResourceModel\OrderPickupLocation\GetPickupLocationCodeByOrderId; use Magento\Sales\Api\Data\OrderExtensionFactory; use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\OrderRepositoryInterface; @@ -23,17 +23,17 @@ class GetPickupLocationForOrderPlugin private $orderExtensionFactory; /** - * @var GetPickupLocationByOrderId + * @var GetPickupLocationCodeByOrderId */ private $getPickupLocationByOrderId; /** - * @param OrderExtensionFactory $orderExtensionFactory - * @param GetPickupLocationByOrderId $getPickupLocationByOrderId + * @param OrderExtensionFactory $orderExtensionFactory + * @param GetPickupLocationCodeByOrderId $getPickupLocationByOrderId */ public function __construct( OrderExtensionFactory $orderExtensionFactory, - GetPickupLocationByOrderId $getPickupLocationByOrderId + GetPickupLocationCodeByOrderId $getPickupLocationByOrderId ) { $this->orderExtensionFactory = $orderExtensionFactory; $this->getPickupLocationByOrderId = $getPickupLocationByOrderId; diff --git a/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php b/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php index 2f578c2c6bad..f54dd61b769b 100644 --- a/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php +++ b/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php @@ -23,6 +23,36 @@ class ReadyForPickup extends \Magento\Backend\Block\Widget\Form\Container */ protected $_blockGroup = 'Magento_Sales'; + /** + * @var \Magento\Sales\Block\Adminhtml\Order\View + */ + private $viewBlock; + + /** + * @var \Magento\InventoryInStorePickup\Model\Order\IsReadyForPickup + */ + private $isReadyForPickup; + + /** + * ReadyForPickup constructor. + * + * @param \Magento\Backend\Block\Widget\Context $context + * @param \Magento\Sales\Block\Adminhtml\Order\View $viewBlock + * @param \Magento\InventoryInStorePickup\Model\Order\IsReadyForPickup $isReadyForPickup + * @param array $data + */ + public function __construct( + \Magento\Backend\Block\Widget\Context $context, + \Magento\Sales\Block\Adminhtml\Order\View $viewBlock, + \Magento\InventoryInStorePickup\Model\Order\IsReadyForPickup $isReadyForPickup, + array $data = [] + ) { + $this->viewBlock = $viewBlock; + $this->isReadyForPickup = $isReadyForPickup; + + parent::__construct($context, $data); + } + /** * Rendering Ready for Pickup button */ @@ -32,7 +62,7 @@ protected function _construct() $this->_controller = 'adminhtml_order'; $this->_mode = 'view'; - if ($this->isEmailsSendingAllowed()) {/* TODO: add is order ready check */ + if ($this->isDisplayButton()) { $message = __('Are you sure you want to notify the customer that order is ready for pickup?'); $this->addButton( 'ready_for_pickup', @@ -56,4 +86,13 @@ private function isEmailsSendingAllowed():bool { return $this->_authorization->isAllowed(self::ADMIN_SALES_EMAIL_RESOURCE); } + + /** + * @return bool + */ + private function isDisplayButton():bool + { + return $this->isEmailsSendingAllowed() + && $this->isReadyForPickup->execute((int)$this->viewBlock->getOrderId()); + } } From 2fd5e55b7fb65663c7038872f4d91e9f3980c96f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szyma=C5=84ski?= <bartlomiej.szymanski@bold.net.pl> Date: Sat, 30 Mar 2019 13:47:38 +0100 Subject: [PATCH 050/231] Get Rid of MySQL View usage on Default Stock --- .../AddIsInStockFieldToCollection.php | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/InventoryCatalog/Model/ResourceModel/AddIsInStockFieldToCollection.php b/InventoryCatalog/Model/ResourceModel/AddIsInStockFieldToCollection.php index 033c9cb1d3bd..58aa54de9fc0 100644 --- a/InventoryCatalog/Model/ResourceModel/AddIsInStockFieldToCollection.php +++ b/InventoryCatalog/Model/ResourceModel/AddIsInStockFieldToCollection.php @@ -8,6 +8,7 @@ namespace Magento\InventoryCatalog\Model\ResourceModel; use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface; use Magento\InventoryIndexer\Indexer\IndexStructure; use Magento\InventoryIndexer\Model\StockIndexTableNameResolverInterface; @@ -21,28 +22,45 @@ class AddIsInStockFieldToCollection */ private $stockIndexTableProvider; + /** + * @var DefaultStockProviderInterface + */ + private $defaultStockProvider; + /** * @param StockIndexTableNameResolverInterface $stockIndexTableProvider + * @param DefaultStockProviderInterface $defaultStockProvider */ public function __construct( - StockIndexTableNameResolverInterface $stockIndexTableProvider + StockIndexTableNameResolverInterface $stockIndexTableProvider, + DefaultStockProviderInterface $defaultStockProvider ) { $this->stockIndexTableProvider = $stockIndexTableProvider; + $this->defaultStockProvider = $defaultStockProvider; } /** * @param Collection $collection * @param int $stockId + * * @return void */ - public function execute($collection, int $stockId) + public function execute($collection, int $stockId): void { - $tableName = $this->stockIndexTableProvider->execute($stockId); + if ($stockId === $this->defaultStockProvider->getId()) { + $isSalableColumnName = 'is_in_stock'; + $resource = $collection->getResource(); + $collection->getSelect()->join( + ['inventory_in_stock' => $resource->getTable('cataloginventory_stock_item')], + sprintf('%s.entity_id = inventory_in_stock.product_id', Collection::MAIN_TABLE_ALIAS), + [IndexStructure::IS_SALABLE => $isSalableColumnName] + ); + } else { + $tableName = $this->stockIndexTableProvider->execute($stockId); - $collection->getSelect()->join( - ['inventory_in_stock' => $tableName], - 'e.sku = inventory_in_stock.sku', - [] - )->where('inventory_in_stock.' . IndexStructure::IS_SALABLE . ' = ?', 1); + $collection->getSelect()->join( + ['inventory_in_stock' => $tableName], 'e.sku = inventory_in_stock.sku', [] + )->where('inventory_in_stock.' . IndexStructure::IS_SALABLE . ' = ?', 1); + } } } From ad7373ad523063c8b5dd410ec44bd5f39e356b3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szyma=C5=84ski?= <bartlomiej.szymanski@bold.net.pl> Date: Sat, 30 Mar 2019 13:51:04 +0100 Subject: [PATCH 051/231] Get Rid of MySQL View usage on Default Stock --- .../Model/ResourceModel/AddIsInStockFieldToCollection.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/InventoryCatalog/Model/ResourceModel/AddIsInStockFieldToCollection.php b/InventoryCatalog/Model/ResourceModel/AddIsInStockFieldToCollection.php index 58aa54de9fc0..068261e27f8e 100644 --- a/InventoryCatalog/Model/ResourceModel/AddIsInStockFieldToCollection.php +++ b/InventoryCatalog/Model/ResourceModel/AddIsInStockFieldToCollection.php @@ -59,7 +59,9 @@ public function execute($collection, int $stockId): void $tableName = $this->stockIndexTableProvider->execute($stockId); $collection->getSelect()->join( - ['inventory_in_stock' => $tableName], 'e.sku = inventory_in_stock.sku', [] + ['inventory_in_stock' => $tableName], + 'e.sku = inventory_in_stock.sku', + [] )->where('inventory_in_stock.' . IndexStructure::IS_SALABLE . ' = ?', 1); } } From dee53f64a03b5e43db92bbbe38cca714e153e97c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szyma=C5=84ski?= <bartlomiej.szymanski@bold.net.pl> Date: Sat, 30 Mar 2019 15:20:04 +0100 Subject: [PATCH 052/231] Add Backward Compatible --- .../Model/ResourceModel/AddIsInStockFieldToCollection.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/InventoryCatalog/Model/ResourceModel/AddIsInStockFieldToCollection.php b/InventoryCatalog/Model/ResourceModel/AddIsInStockFieldToCollection.php index 068261e27f8e..25b13e426902 100644 --- a/InventoryCatalog/Model/ResourceModel/AddIsInStockFieldToCollection.php +++ b/InventoryCatalog/Model/ResourceModel/AddIsInStockFieldToCollection.php @@ -8,6 +8,7 @@ namespace Magento\InventoryCatalog\Model\ResourceModel; use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\Framework\App\ObjectManager; use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface; use Magento\InventoryIndexer\Indexer\IndexStructure; use Magento\InventoryIndexer\Model\StockIndexTableNameResolverInterface; @@ -29,14 +30,15 @@ class AddIsInStockFieldToCollection /** * @param StockIndexTableNameResolverInterface $stockIndexTableProvider - * @param DefaultStockProviderInterface $defaultStockProvider + * @param DefaultStockProviderInterface|null $defaultStockProvider */ public function __construct( StockIndexTableNameResolverInterface $stockIndexTableProvider, - DefaultStockProviderInterface $defaultStockProvider + ?DefaultStockProviderInterface $defaultStockProvider = null ) { $this->stockIndexTableProvider = $stockIndexTableProvider; - $this->defaultStockProvider = $defaultStockProvider; + $this->defaultStockProvider = $defaultStockProvider ?: ObjectManager::getInstance() + ->get(DefaultStockProviderInterface::class); } /** From 40bd3402bf92ca8ef934c90a41e99bf34da15bd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szyma=C5=84ski?= <bartlomiej.szymanski@bold.net.pl> Date: Sat, 30 Mar 2019 16:15:21 +0100 Subject: [PATCH 053/231] GraphQL support for the new Inventory --- InventoryGraphQl/LICENSE.txt | 48 ++++++++ InventoryGraphQl/LICENSE_AFL.txt | 48 ++++++++ .../Resolver/OnlyXLeftInStockResolver.php | 107 ++++++++++++++++++ .../Model/Resolver/StockStatusProvider.php | 62 ++++++++++ InventoryGraphQl/README.md | 16 +++ InventoryGraphQl/composer.json | 22 ++++ InventoryGraphQl/etc/di.xml | 11 ++ InventoryGraphQl/etc/module.xml | 18 +++ InventoryGraphQl/registration.php | 12 ++ 9 files changed, 344 insertions(+) create mode 100644 InventoryGraphQl/LICENSE.txt create mode 100644 InventoryGraphQl/LICENSE_AFL.txt create mode 100644 InventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php create mode 100644 InventoryGraphQl/Model/Resolver/StockStatusProvider.php create mode 100644 InventoryGraphQl/README.md create mode 100644 InventoryGraphQl/composer.json create mode 100644 InventoryGraphQl/etc/di.xml create mode 100644 InventoryGraphQl/etc/module.xml create mode 100644 InventoryGraphQl/registration.php diff --git a/InventoryGraphQl/LICENSE.txt b/InventoryGraphQl/LICENSE.txt new file mode 100644 index 000000000000..49525fd99da9 --- /dev/null +++ b/InventoryGraphQl/LICENSE.txt @@ -0,0 +1,48 @@ + +Open Software License ("OSL") v. 3.0 + +This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Open Software License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/InventoryGraphQl/LICENSE_AFL.txt b/InventoryGraphQl/LICENSE_AFL.txt new file mode 100644 index 000000000000..f39d641b18a1 --- /dev/null +++ b/InventoryGraphQl/LICENSE_AFL.txt @@ -0,0 +1,48 @@ + +Academic Free License ("AFL") v. 3.0 + +This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Academic Free License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/InventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php b/InventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php new file mode 100644 index 000000000000..f122207d0976 --- /dev/null +++ b/InventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php @@ -0,0 +1,107 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryGraphQl\Model\Resolver; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Exception\InputException; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\InventoryCatalog\Model\GetStockIdForCurrentWebsite; +use Magento\InventoryConfigurationApi\Api\GetStockItemConfigurationInterface; +use Magento\InventoryConfigurationApi\Exception\SkuIsNotAssignedToStockException; +use Magento\InventorySalesApi\Api\GetProductSalableQtyInterface; + +/** + * @inheritdoc + */ +class OnlyXLeftInStockResolver implements ResolverInterface +{ + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var GetProductSalableQtyInterface + */ + private $getProductSalableQty; + + /** + * @var GetStockIdForCurrentWebsite + */ + private $getStockIdForCurrentWebsite; + + /** + * @var GetStockItemConfigurationInterface + */ + private $getStockItemConfiguration; + + /** + * @param ScopeConfigInterface $scopeConfig + * @param GetProductSalableQtyInterface $getProductSalableQty + * @param GetStockIdForCurrentWebsite $getStockIdForCurrentWebsite + * @param GetStockItemConfigurationInterface $getStockItemConfiguration + */ + public function __construct( + ScopeConfigInterface $scopeConfig, + GetProductSalableQtyInterface $getProductSalableQty, + GetStockIdForCurrentWebsite $getStockIdForCurrentWebsite, + GetStockItemConfigurationInterface $getStockItemConfiguration + ) { + $this->scopeConfig = $scopeConfig; + $this->getProductSalableQty = $getProductSalableQty; + $this->getStockIdForCurrentWebsite = $getStockIdForCurrentWebsite; + $this->getStockItemConfiguration = $getStockItemConfiguration; + } + + /** + * @inheritDoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + /* @var $product ProductInterface */ + $product = $value['model']; + $onlyXLeftQty = $this->getOnlyXLeftQty($product->getSku()); + + return $onlyXLeftQty; + } + + /** + * @param string $sku + * + * @return null|float + * @throws SkuIsNotAssignedToStockException + * @throws LocalizedException + */ + private function getOnlyXLeftQty(string $sku): ?float + { + $stockId = $this->getStockIdForCurrentWebsite->execute(); + $stockItemConfiguration = $this->getStockItemConfiguration->execute($sku, $stockId); + + $thresholdQty = $stockItemConfiguration->getStockThresholdQty(); + if ($thresholdQty === 0) { + return null; + } + + try { + $productSalableQty = $this->getProductSalableQty->execute($sku, $stockId); + $stockLeft = $productSalableQty - $stockItemConfiguration->getMinQty(); + + if ($productSalableQty > 0 && $stockLeft <= $thresholdQty) { + return (float)$stockLeft; + } + } catch (InputException | LocalizedException $e) { + return null; + } + + return null; + } +} diff --git a/InventoryGraphQl/Model/Resolver/StockStatusProvider.php b/InventoryGraphQl/Model/Resolver/StockStatusProvider.php new file mode 100644 index 000000000000..1378dac1f00f --- /dev/null +++ b/InventoryGraphQl/Model/Resolver/StockStatusProvider.php @@ -0,0 +1,62 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryGraphQl\Model\Resolver; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\InventoryCatalog\Model\GetStockIdForCurrentWebsite; +use Magento\InventorySalesApi\Api\IsProductSalableInterface; + +/** + * @inheritdoc + */ +class StockStatusProvider implements ResolverInterface +{ + /** + * @var IsProductSalableInterface + */ + private $isProductSalable; + + /** + * @var GetStockIdForCurrentWebsite + */ + private $getStockIdForCurrentWebsite; + + /** + * @param IsProductSalableInterface $isProductSalable + * @param GetStockIdForCurrentWebsite $getStockIdForCurrentWebsite + */ + public function __construct( + IsProductSalableInterface $isProductSalable, + GetStockIdForCurrentWebsite $getStockIdForCurrentWebsite + ) { + $this->isProductSalable = $isProductSalable; + $this->getStockIdForCurrentWebsite = $getStockIdForCurrentWebsite; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + if (!array_key_exists('model', $value) || !$value['model'] instanceof ProductInterface) { + throw new LocalizedException(__('"model" value should be specified')); + } + + /* @var $product ProductInterface */ + $product = $value['model']; + + $stockId = $this->getStockIdForCurrentWebsite->execute(); + $isProductSalable = $this->isProductSalable->execute($product->getSku(), $stockId); + + return $isProductSalable ? 'IN_STOCK' : 'OUT_OF_STOCK'; + } +} diff --git a/InventoryGraphQl/README.md b/InventoryGraphQl/README.md new file mode 100644 index 000000000000..493638348325 --- /dev/null +++ b/InventoryGraphQl/README.md @@ -0,0 +1,16 @@ +# InventoryGraphQl module + +The `InventoryGraphQl` provides type information for the GraphQl module + to generate inventory stock fields for product information endpoints. + +This module is part of the new inventory infrastructure. The +[Inventory Management overview](https://devdocs.magento.com/guides/v2.3/inventory/index.html) +describes the MSI (Multi-Source Inventory) project in more detail. + +## Installation details + +This module is installed as part of Magento Open Source. It cannot be deleted or disabled. + +## Extension points and service contracts + +There are no extension points or service contracts for this module. diff --git a/InventoryGraphQl/composer.json b/InventoryGraphQl/composer.json new file mode 100644 index 000000000000..b173994fbc9a --- /dev/null +++ b/InventoryGraphQl/composer.json @@ -0,0 +1,22 @@ +{ + "name": "magento/module-inventory-graph-ql", + "description": "N/A", + "type": "magento2-module", + "require": { + "php": "~7.1.3||~7.2.0", + "magento/framework": "*", + "magento/module-catalog-inventory-graph-ql": "*" + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\InventoryGraphQl\\": "" + } + } +} diff --git a/InventoryGraphQl/etc/di.xml b/InventoryGraphQl/etc/di.xml new file mode 100644 index 000000000000..1dbe6bbdf2be --- /dev/null +++ b/InventoryGraphQl/etc/di.xml @@ -0,0 +1,11 @@ +<?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"> + <preference for="Magento\CatalogInventoryGraphQl\Model\Resolver\OnlyXLeftInStockResolver" type="Magento\InventoryGraphQl\Model\Resolver\OnlyXLeftInStockResolver"/> + <preference for="Magento\CatalogInventoryGraphQl\Model\Resolver\StockStatusProvider" type="Magento\InventoryGraphQl\Model\Resolver\StockStatusProvider"/> +</config> diff --git a/InventoryGraphQl/etc/module.xml b/InventoryGraphQl/etc/module.xml new file mode 100644 index 000000000000..891e15bef9e4 --- /dev/null +++ b/InventoryGraphQl/etc/module.xml @@ -0,0 +1,18 @@ +<?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:Module/etc/module.xsd"> + <module name="Magento_InventoryGraphQl"> + <sequence> + <module name="Magento_Catalog"/> + <module name="Magento_InventoryCatalog"/> + <module name="Magento_InventoryConfigurationApi"/> + <module name="Magento_InventorySalesApi"/> + <module name="Magento_CatalogInventoryGraphQl"/> + </sequence> + </module> +</config> diff --git a/InventoryGraphQl/registration.php b/InventoryGraphQl/registration.php new file mode 100644 index 000000000000..7e6cd8606b6c --- /dev/null +++ b/InventoryGraphQl/registration.php @@ -0,0 +1,12 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +\Magento\Framework\Component\ComponentRegistrar::register( + \Magento\Framework\Component\ComponentRegistrar::MODULE, + 'Magento_InventoryGraphQl', + __DIR__ +); From ce468eeb45b2d163dc41bc46bad731e3ac459dee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szyma=C5=84ski?= <bartlomiej.szymanski@bold.net.pl> Date: Sat, 30 Mar 2019 16:39:10 +0100 Subject: [PATCH 054/231] Add comment --- InventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php | 1 + 1 file changed, 1 insertion(+) diff --git a/InventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php b/InventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php index f122207d0976..3bde5126bbb2 100644 --- a/InventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php +++ b/InventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php @@ -99,6 +99,7 @@ private function getOnlyXLeftQty(string $sku): ?float return (float)$stockLeft; } } catch (InputException | LocalizedException $e) { + // this is expected behavior because ex. Group product doesn't have own quantity return null; } From 3277d05aad4cbd3a315fa8ab071cd774c172a3df Mon Sep 17 00:00:00 2001 From: Maksym Novik <novik.kor@gmail.com> Date: Thu, 28 Mar 2019 23:27:28 +0200 Subject: [PATCH 055/231] Add "Ready for Pickup" button on the Order page in Admin Panel which notifies Customer that Order could be picked up #2034. Moved IsOrderReadyForPickup to API level. Created an init point for NotifyPickup controller. --- .../Model/IsOrderReadyForPickup.php | 40 +++++++++++++++ .../{IsReadyForPickup.php => IsFulfilled.php} | 2 +- .../Adminhtml/Order/View/ReadyForPickup.php | 21 ++++---- .../Adminhtml/Order/NotifyPickup.php | 49 +++++++++++++++++++ .../etc/adminhtml/routes.xml | 14 ++++++ .../Api/IsOrderReadyForPickupInterface.php | 18 +++++++ InventoryInStorePickupApi/etc/di.xml | 4 +- 7 files changed, 136 insertions(+), 12 deletions(-) create mode 100644 InventoryInStorePickup/Model/IsOrderReadyForPickup.php rename InventoryInStorePickup/Model/Order/{IsReadyForPickup.php => IsFulfilled.php} (99%) create mode 100644 InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php create mode 100644 InventoryInStorePickupAdminUi/etc/adminhtml/routes.xml create mode 100644 InventoryInStorePickupApi/Api/IsOrderReadyForPickupInterface.php diff --git a/InventoryInStorePickup/Model/IsOrderReadyForPickup.php b/InventoryInStorePickup/Model/IsOrderReadyForPickup.php new file mode 100644 index 000000000000..0a5f47e20212 --- /dev/null +++ b/InventoryInStorePickup/Model/IsOrderReadyForPickup.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickup\Model; + +use Magento\InventoryInStorePickup\Model\Order\IsFulfilled; +use Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface; + +class IsOrderReadyForPickup implements IsOrderReadyForPickupInterface +{ + /** + * @var \Magento\InventoryInStorePickup\Model\Order\IsFulfilled + */ + private $isFulfilled; + + /** + * IsReadyForPickup constructor. + * + * @param \Magento\InventoryInStorePickup\Model\Order\IsFulfilled $isFulfilled + */ + public function __construct( + IsFulfilled $isFulfilled + ) { + $this->isFulfilled = $isFulfilled; + } + + /** + * @param int $orderId + * + * @return bool + */ + public function execute(int $orderId):bool + { + return $this->isFulfilled->execute($orderId); + } +} diff --git a/InventoryInStorePickup/Model/Order/IsReadyForPickup.php b/InventoryInStorePickup/Model/Order/IsFulfilled.php similarity index 99% rename from InventoryInStorePickup/Model/Order/IsReadyForPickup.php rename to InventoryInStorePickup/Model/Order/IsFulfilled.php index 522db4a1178c..eaf16114152b 100644 --- a/InventoryInStorePickup/Model/Order/IsReadyForPickup.php +++ b/InventoryInStorePickup/Model/Order/IsFulfilled.php @@ -9,7 +9,7 @@ use Magento\InventoryApi\Api\Data\SourceItemInterface; -class IsReadyForPickup +class IsFulfilled { /** * @var \Magento\Sales\Api\OrderRepositoryInterface diff --git a/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php b/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php index f54dd61b769b..ee523b1751bc 100644 --- a/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php +++ b/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php @@ -7,6 +7,9 @@ namespace Magento\InventoryInStorePickupAdminUi\Block\Adminhtml\Order\View; +use Magento\InventoryInStorePickupAdminUi\Controller\Adminhtml\Order\NotifyPickup; +use Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface; + /** * TODO: is it possible to replace with UI Component? * @@ -14,8 +17,6 @@ */ class ReadyForPickup extends \Magento\Backend\Block\Widget\Form\Container { - const ADMIN_SALES_EMAIL_RESOURCE = 'Magento_Sales::emails'; - /** * Block group * @@ -29,22 +30,22 @@ class ReadyForPickup extends \Magento\Backend\Block\Widget\Form\Container private $viewBlock; /** - * @var \Magento\InventoryInStorePickup\Model\Order\IsReadyForPickup + * @var \Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface */ private $isReadyForPickup; /** * ReadyForPickup constructor. * - * @param \Magento\Backend\Block\Widget\Context $context - * @param \Magento\Sales\Block\Adminhtml\Order\View $viewBlock - * @param \Magento\InventoryInStorePickup\Model\Order\IsReadyForPickup $isReadyForPickup - * @param array $data + * @param \Magento\Backend\Block\Widget\Context $context + * @param \Magento\Sales\Block\Adminhtml\Order\View $viewBlock + * @param \Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface $isReadyForPickup + * @param array $data */ public function __construct( \Magento\Backend\Block\Widget\Context $context, \Magento\Sales\Block\Adminhtml\Order\View $viewBlock, - \Magento\InventoryInStorePickup\Model\Order\IsReadyForPickup $isReadyForPickup, + IsOrderReadyForPickupInterface $isReadyForPickup, array $data = [] ) { $this->viewBlock = $viewBlock; @@ -72,7 +73,7 @@ protected function _construct() 'onclick' => sprintf( "confirmSetLocation('%s', '%s')", $message, - $this->getUrl('*/*/notifyPickup') + $this->viewBlock->getUrl('sales/*/notifyPickup') ) ] ); @@ -84,7 +85,7 @@ protected function _construct() */ private function isEmailsSendingAllowed():bool { - return $this->_authorization->isAllowed(self::ADMIN_SALES_EMAIL_RESOURCE); + return $this->_authorization->isAllowed(NotifyPickup::ADMIN_RESOURCE); } /** diff --git a/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php b/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php new file mode 100644 index 000000000000..ae52a9c68cf8 --- /dev/null +++ b/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickupAdminUi\Controller\Adminhtml\Order; + +class NotifyPickup extends \Magento\Sales\Controller\Adminhtml\Order +{ + /** + * Authorization level of a basic admin session + * + * @see _isAllowed() + */ + const ADMIN_RESOURCE = 'Magento_Sales::emails'; + + /** + * Notify user + * + * @return \Magento\Framework\Controller\ResultInterface + */ + public function execute():\Magento\Framework\Controller\ResultInterface + { + /*TODO*/ + /*$order = $this->_initOrder(); + if ($order) { + try { + $this->orderManagement->notify($order->getEntityId()); + $this->messageManager->addSuccessMessage(__('You sent the order email.')); + } catch (\Magento\Framework\Exception\LocalizedException $e) { + $this->messageManager->addErrorMessage($e->getMessage()); + } catch (\Exception $e) { + $this->messageManager->addErrorMessage(__('We can\'t send the email order right now.')); + $this->logger->critical($e); + } + + return $this->resultRedirectFactory->create()->setPath( + 'sales/order/view', + [ + 'order_id' => $order->getEntityId() + ] + ); + }*/ + + return $this->resultRedirectFactory->create()->setPath('sales/*/'); + } +} diff --git a/InventoryInStorePickupAdminUi/etc/adminhtml/routes.xml b/InventoryInStorePickupAdminUi/etc/adminhtml/routes.xml new file mode 100644 index 000000000000..c0d0cd0aaa11 --- /dev/null +++ b/InventoryInStorePickupAdminUi/etc/adminhtml/routes.xml @@ -0,0 +1,14 @@ +<?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:App/etc/routes.xsd"> + <router id="admin"> + <route id="sales" frontName="sales"> + <module name="Magento_InventoryInStorePickupAdminUi" before="Magento_Backend" /> + </route> + </router> +</config> diff --git a/InventoryInStorePickupApi/Api/IsOrderReadyForPickupInterface.php b/InventoryInStorePickupApi/Api/IsOrderReadyForPickupInterface.php new file mode 100644 index 000000000000..d03998bd63ba --- /dev/null +++ b/InventoryInStorePickupApi/Api/IsOrderReadyForPickupInterface.php @@ -0,0 +1,18 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickupApi\Api; + +interface IsOrderReadyForPickupInterface +{ + /** + * @param int $orderId + * + * @return bool + */ + public function execute(int $orderId):bool; +} diff --git a/InventoryInStorePickupApi/etc/di.xml b/InventoryInStorePickupApi/etc/di.xml index c3dcdd78878b..ce98e501b703 100644 --- a/InventoryInStorePickupApi/etc/di.xml +++ b/InventoryInStorePickupApi/etc/di.xml @@ -8,5 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <preference for="Magento\InventoryInStorePickupApi\Api\GetNearbySourcesByPostcodeInterface" - type="Magento\InventoryInStorePickupApi\Model\GetNearbySourcesByPostcode"/> + type="Magento\InventoryInStorePickupApi\Model\GetNearbySourcesByPostcode"/> + <preference for="\Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface" + type="\Magento\InventoryInStorePickup\Model\IsOrderReadyForPickup"/> </config> From dcab1899f7307b3be80a4acd9ddc9020d34efb6d Mon Sep 17 00:00:00 2001 From: Maksym Novik <novik.kor@gmail.com> Date: Sun, 31 Mar 2019 23:29:18 +0300 Subject: [PATCH 056/231] Add "Ready for Pickup" button on the Order page in Admin Panel which notifies Customer that Order could be picked up #2034. NotifyOrderIsReadyAndShipInterface API; Implemented NotifyPickup controller; Added some TODOs --- .../Model/IsOrderReadyForPickup.php | 1 + .../Model/NotifyOrderIsReadyAndShip.php | 53 +++++++++++ InventoryInStorePickup/etc/di.xml | 6 ++ .../Adminhtml/Order/View/ReadyForPickup.php | 5 +- .../Adminhtml/Order/NotifyPickup.php | 88 +++++++++++++++++-- .../NotifyOrderIsReadyAndShipInterface.php | 22 +++++ .../OrderIsNotReadyForPickupException.php | 34 +++++++ InventoryInStorePickupApi/etc/di.xml | 2 - 8 files changed, 201 insertions(+), 10 deletions(-) create mode 100644 InventoryInStorePickup/Model/NotifyOrderIsReadyAndShip.php create mode 100644 InventoryInStorePickupApi/Api/NotifyOrderIsReadyAndShipInterface.php create mode 100644 InventoryInStorePickupApi/Exception/OrderIsNotReadyForPickupException.php diff --git a/InventoryInStorePickup/Model/IsOrderReadyForPickup.php b/InventoryInStorePickup/Model/IsOrderReadyForPickup.php index 0a5f47e20212..bb9db5b58f89 100644 --- a/InventoryInStorePickup/Model/IsOrderReadyForPickup.php +++ b/InventoryInStorePickup/Model/IsOrderReadyForPickup.php @@ -35,6 +35,7 @@ public function __construct( */ public function execute(int $orderId):bool { + /*TODO: add $order->canShip() check */ return $this->isFulfilled->execute($orderId); } } diff --git a/InventoryInStorePickup/Model/NotifyOrderIsReadyAndShip.php b/InventoryInStorePickup/Model/NotifyOrderIsReadyAndShip.php new file mode 100644 index 000000000000..8857f1484f84 --- /dev/null +++ b/InventoryInStorePickup/Model/NotifyOrderIsReadyAndShip.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickup\Model; + +use Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface; +use Magento\InventoryInStorePickupApi\Api\NotifyOrderIsReadyAndShipInterface; +use Magento\InventoryInStorePickupApi\Exception\OrderIsNotReadyForPickupException; + +class NotifyOrderIsReadyAndShip implements NotifyOrderIsReadyAndShipInterface +{ + /** + * @var \Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface + */ + private $isOrderReadyForPickup; + + /** + * @var \Magento\Sales\Api\ShipOrderInterface + */ + private $shipOrder; + + /** + * NotifyOrderIsReadyAndShip constructor. + * + * @param \Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface $isOrderReadyForPickup + * @param \Magento\Sales\Api\ShipOrderInterface $shipOrder + */ + public function __construct( + IsOrderReadyForPickupInterface $isOrderReadyForPickup, + \Magento\Sales\Api\ShipOrderInterface $shipOrder + ) { + $this->isOrderReadyForPickup = $isOrderReadyForPickup; + $this->shipOrder = $shipOrder; + } + + /** + * {@inheritdoc} + */ + public function execute(int $orderId):int + { + if (!$this->isOrderReadyForPickup->execute($orderId)) { + throw new OrderIsNotReadyForPickupException(); + } + + /* TODO: send email */ + + return $this->shipOrder->execute($orderId); + } +} diff --git a/InventoryInStorePickup/etc/di.xml b/InventoryInStorePickup/etc/di.xml index 04f52ef77cdf..f76750e559df 100644 --- a/InventoryInStorePickup/etc/di.xml +++ b/InventoryInStorePickup/etc/di.xml @@ -25,4 +25,10 @@ </argument> </arguments> </type> + + <preference for="\Magento\InventoryInStorePickupApi\Api\NotifyOrderIsReadyAndShipInterface" + type="\Magento\InventoryInStorePickup\Model\NotifyOrderIsReadyAndShip"/> + <preference for="\Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface" + type="\Magento\InventoryInStorePickup\Model\IsOrderReadyForPickup"/> + </config> diff --git a/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php b/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php index ee523b1751bc..54b508a21fbb 100644 --- a/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php +++ b/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php @@ -63,8 +63,11 @@ protected function _construct() $this->_controller = 'adminhtml_order'; $this->_mode = 'view'; + /* TODO: always display but throw warnings? */ if ($this->isDisplayButton()) { - $message = __('Are you sure you want to notify the customer that order is ready for pickup?'); + $message = __( + 'Are you sure you want to notify the customer that order is ready for pickup and create shipment?' + ); $this->addButton( 'ready_for_pickup', [ diff --git a/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php b/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php index ae52a9c68cf8..8577ccdf7291 100644 --- a/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php +++ b/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php @@ -7,7 +7,18 @@ namespace Magento\InventoryInStorePickupAdminUi\Controller\Adminhtml\Order; -class NotifyPickup extends \Magento\Sales\Controller\Adminhtml\Order +use Magento\Framework\Exception\InputException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\InventoryInStorePickupApi\Api\NotifyOrderIsReadyAndShipInterface; +use Magento\Sales\Api\OrderRepositoryInterface; +use Psr\Log\LoggerInterface; + +/** + * Class NotifyPickup + * + * @package Magento\InventoryInStorePickupAdminUi\Controller\Adminhtml\Order + */ +class NotifyPickup extends \Magento\Backend\App\Action { /** * Authorization level of a basic admin session @@ -16,6 +27,42 @@ class NotifyPickup extends \Magento\Sales\Controller\Adminhtml\Order */ const ADMIN_RESOURCE = 'Magento_Sales::emails'; + /** + * @var \Magento\InventoryInStorePickupApi\Api\NotifyOrderIsReadyAndShipInterface + */ + private $notifyOrderIsReadyAndShip; + + /** + * @var \Magento\Sales\Api\OrderRepositoryInterface + */ + private $orderRepository; + + /** + * @var \Psr\Log\LoggerInterface + */ + private $logger; + + /** + * NotifyPickup constructor. + * + * @param \Magento\Backend\App\Action\Context $context + * @param \Magento\InventoryInStorePickupApi\Api\NotifyOrderIsReadyAndShipInterface $notifyOrderIsReadyAndShip + * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository + * @param \Psr\Log\LoggerInterface $logger + */ + public function __construct( + \Magento\Backend\App\Action\Context $context, + NotifyOrderIsReadyAndShipInterface $notifyOrderIsReadyAndShip, + OrderRepositoryInterface $orderRepository, + LoggerInterface $logger + ) { + $this->notifyOrderIsReadyAndShip = $notifyOrderIsReadyAndShip; + $this->orderRepository = $orderRepository; + $this->logger = $logger; + + parent::__construct($context); + } + /** * Notify user * @@ -23,16 +70,16 @@ class NotifyPickup extends \Magento\Sales\Controller\Adminhtml\Order */ public function execute():\Magento\Framework\Controller\ResultInterface { - /*TODO*/ - /*$order = $this->_initOrder(); + $order = $this->initOrder(); + if ($order) { try { - $this->orderManagement->notify($order->getEntityId()); - $this->messageManager->addSuccessMessage(__('You sent the order email.')); + $this->notifyOrderIsReadyAndShip->execute((int)$order->getEntityId()); + $this->messageManager->addSuccessMessage(__('The customer have been notified and shipment created.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addErrorMessage(__('We can\'t send the email order right now.')); + $this->messageManager->addErrorMessage(__('We can\'t notify the customer right now.')); $this->logger->critical($e); } @@ -42,8 +89,35 @@ public function execute():\Magento\Framework\Controller\ResultInterface 'order_id' => $order->getEntityId() ] ); - }*/ + } return $this->resultRedirectFactory->create()->setPath('sales/*/'); } + + /** + * Initialize order model instance + * + * @see \Magento\Sales\Controller\Adminhtml\Order::_initOrder + * + * @return \Magento\Sales\Api\Data\OrderInterface|false + */ + private function initOrder() + { + $id = $this->getRequest()->getParam('order_id'); + try { + $order = $this->orderRepository->get($id); + } catch (NoSuchEntityException $e) { + $this->messageManager->addErrorMessage(__('This order no longer exists.')); + $this->_actionFlag->set('', self::FLAG_NO_DISPATCH, true); + + return false; + } catch (InputException $e) { + $this->messageManager->addErrorMessage(__('This order no longer exists.')); + $this->_actionFlag->set('', self::FLAG_NO_DISPATCH, true); + + return false; + } + + return $order; + } } diff --git a/InventoryInStorePickupApi/Api/NotifyOrderIsReadyAndShipInterface.php b/InventoryInStorePickupApi/Api/NotifyOrderIsReadyAndShipInterface.php new file mode 100644 index 000000000000..0f06a28fa99e --- /dev/null +++ b/InventoryInStorePickupApi/Api/NotifyOrderIsReadyAndShipInterface.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickupApi\Api; + +interface NotifyOrderIsReadyAndShipInterface +{ + /** + * @param int $orderId + * + * @return int Id of created Shipment + * + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws \Magento\InventoryInStorePickupApi\Exception\OrderIsNotReadyForPickupException + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function execute(int $orderId):int; +} diff --git a/InventoryInStorePickupApi/Exception/OrderIsNotReadyForPickupException.php b/InventoryInStorePickupApi/Exception/OrderIsNotReadyForPickupException.php new file mode 100644 index 000000000000..0511e6f0bd6c --- /dev/null +++ b/InventoryInStorePickupApi/Exception/OrderIsNotReadyForPickupException.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickupApi\Exception; + +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Phrase; + +/** + * Class OrderIsNotReadyForPickupException + * + * @package Magento\InventoryInStorePickupApi\Exception + * @api + */ +class OrderIsNotReadyForPickupException extends LocalizedException +{ + /** + * @param \Magento\Framework\Phrase $phrase + * @param \Exception $cause + * @param int $code + */ + public function __construct(Phrase $phrase = null, \Exception $cause = null, $code = 0) + { + if ($phrase === null) { + $phrase = new Phrase('The order is not ready for pickup'); + } + + parent::__construct($phrase, $cause, $code); + } +} diff --git a/InventoryInStorePickupApi/etc/di.xml b/InventoryInStorePickupApi/etc/di.xml index ce98e501b703..0ff53d9d7177 100644 --- a/InventoryInStorePickupApi/etc/di.xml +++ b/InventoryInStorePickupApi/etc/di.xml @@ -9,6 +9,4 @@ xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <preference for="Magento\InventoryInStorePickupApi\Api\GetNearbySourcesByPostcodeInterface" type="Magento\InventoryInStorePickupApi\Model\GetNearbySourcesByPostcode"/> - <preference for="\Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface" - type="\Magento\InventoryInStorePickup\Model\IsOrderReadyForPickup"/> </config> From fd3afea64a0f9466deae8c45c6b34d97eebf6a20 Mon Sep 17 00:00:00 2001 From: Maksym Novik <novik.kor@gmail.com> Date: Sun, 31 Mar 2019 23:34:15 +0300 Subject: [PATCH 057/231] Add "Ready for Pickup" button on the Order page in Admin Panel which notifies Customer that Order could be picked up #2034. Added canShip() check to IsOrderReadyForPickup; Added TODO --- .../Model/IsOrderReadyForPickup.php | 29 +++++++++++++++++-- .../Model/NotifyOrderIsReadyAndShip.php | 1 + 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/InventoryInStorePickup/Model/IsOrderReadyForPickup.php b/InventoryInStorePickup/Model/IsOrderReadyForPickup.php index bb9db5b58f89..e2b3dca15fa3 100644 --- a/InventoryInStorePickup/Model/IsOrderReadyForPickup.php +++ b/InventoryInStorePickup/Model/IsOrderReadyForPickup.php @@ -9,6 +9,7 @@ use Magento\InventoryInStorePickup\Model\Order\IsFulfilled; use Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface; +use Magento\Sales\Api\OrderRepositoryInterface; class IsOrderReadyForPickup implements IsOrderReadyForPickupInterface { @@ -17,15 +18,23 @@ class IsOrderReadyForPickup implements IsOrderReadyForPickupInterface */ private $isFulfilled; + /** + * @var \Magento\Sales\Api\OrderRepositoryInterface + */ + private $orderRepository; + /** * IsReadyForPickup constructor. * * @param \Magento\InventoryInStorePickup\Model\Order\IsFulfilled $isFulfilled + * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository */ public function __construct( - IsFulfilled $isFulfilled + IsFulfilled $isFulfilled, + OrderRepositoryInterface $orderRepository ) { $this->isFulfilled = $isFulfilled; + $this->orderRepository = $orderRepository; } /** @@ -35,7 +44,21 @@ public function __construct( */ public function execute(int $orderId):bool { - /*TODO: add $order->canShip() check */ - return $this->isFulfilled->execute($orderId); + return $this->canShip($orderId) && $this->isFulfilled->execute($orderId); + } + + /** + * @param int $orderId + * + * @return bool + */ + private function canShip(int $orderId):bool + { + $order = $this->orderRepository->get($orderId); + if ($order instanceof \Magento\Sales\Model\Order) { + return $order->canShip(); + } + + return true; } } diff --git a/InventoryInStorePickup/Model/NotifyOrderIsReadyAndShip.php b/InventoryInStorePickup/Model/NotifyOrderIsReadyAndShip.php index 8857f1484f84..c065ee5ba523 100644 --- a/InventoryInStorePickup/Model/NotifyOrderIsReadyAndShip.php +++ b/InventoryInStorePickup/Model/NotifyOrderIsReadyAndShip.php @@ -47,6 +47,7 @@ public function execute(int $orderId):int } /* TODO: send email */ + /* TODO: add order comment? */ return $this->shipOrder->execute($orderId); } From 77eed7036ecc5d1981512b8670ffba2f9aebf001 Mon Sep 17 00:00:00 2001 From: Maksym Novik <novik.kor@gmail.com> Date: Mon, 1 Apr 2019 23:10:54 +0300 Subject: [PATCH 058/231] Add "Ready for Pickup" button on the Order page in Admin Panel which notifies Customer that Order could be picked up #2034. Implemented synchronous email sending with custom email template for storepickup. Added even more TODOs --- .../Model/NotifyOrderIsReadyAndShip.php | 38 ++++++-- .../Container/ReadyForPickupIdentity.php | 91 +++++++++++++++++++ .../Order/Email/ReadyForPickupNotifier.php | 51 +++++++++++ .../Order/Email/ReadyForPickupSender.php | 88 ++++++++++++++++++ InventoryInStorePickup/composer.json | 3 +- InventoryInStorePickup/etc/config.xml | 26 ++++++ InventoryInStorePickup/etc/di.xml | 6 +- .../etc/email_templates.xml | 12 +++ InventoryInStorePickup/etc/module.xml | 1 + .../email/order_ready_for_pickup.html | 53 +++++++++++ .../NotifyOrderIsReadyAndShipInterface.php | 4 +- 11 files changed, 363 insertions(+), 10 deletions(-) create mode 100644 InventoryInStorePickup/Model/Order/Email/Container/ReadyForPickupIdentity.php create mode 100644 InventoryInStorePickup/Model/Order/Email/ReadyForPickupNotifier.php create mode 100644 InventoryInStorePickup/Model/Order/Email/ReadyForPickupSender.php create mode 100644 InventoryInStorePickup/etc/config.xml create mode 100644 InventoryInStorePickup/etc/email_templates.xml create mode 100644 InventoryInStorePickup/view/frontend/email/order_ready_for_pickup.html diff --git a/InventoryInStorePickup/Model/NotifyOrderIsReadyAndShip.php b/InventoryInStorePickup/Model/NotifyOrderIsReadyAndShip.php index c065ee5ba523..276d2ef5b7dc 100644 --- a/InventoryInStorePickup/Model/NotifyOrderIsReadyAndShip.php +++ b/InventoryInStorePickup/Model/NotifyOrderIsReadyAndShip.php @@ -23,32 +23,58 @@ class NotifyOrderIsReadyAndShip implements NotifyOrderIsReadyAndShipInterface */ private $shipOrder; + /** + * @var \Magento\InventoryInStorePickup\Model\Order\Email\ReadyForPickupNotifier + */ + private $emailNotifier; + + /** + * @var \Magento\Sales\Api\OrderRepositoryInterface + */ + private $orderRepository; + /** * NotifyOrderIsReadyAndShip constructor. * - * @param \Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface $isOrderReadyForPickup - * @param \Magento\Sales\Api\ShipOrderInterface $shipOrder + * @param \Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface $isOrderReadyForPickup + * @param \Magento\Sales\Api\ShipOrderInterface $shipOrder + * @param \Magento\InventoryInStorePickup\Model\Order\Email\ReadyForPickupNotifier $emailNotifier + * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository */ public function __construct( IsOrderReadyForPickupInterface $isOrderReadyForPickup, - \Magento\Sales\Api\ShipOrderInterface $shipOrder + \Magento\Sales\Api\ShipOrderInterface $shipOrder, + Order\Email\ReadyForPickupNotifier $emailNotifier, + \Magento\Sales\Api\OrderRepositoryInterface $orderRepository ) { $this->isOrderReadyForPickup = $isOrderReadyForPickup; $this->shipOrder = $shipOrder; + $this->emailNotifier = $emailNotifier; + $this->orderRepository = $orderRepository; } /** * {@inheritdoc} */ - public function execute(int $orderId):int + public function execute(int $orderId):?int { if (!$this->isOrderReadyForPickup->execute($orderId)) { throw new OrderIsNotReadyForPickupException(); } - /* TODO: send email */ + $this->emailNotifier->notify($this->getOrder($orderId)); /* TODO: add order comment? */ - return $this->shipOrder->execute($orderId); + return (int)$this->shipOrder->execute($orderId); + } + + /** + * @param int $orderId + * + * @return \Magento\Sales\Api\Data\OrderInterface|\Magento\Sales\Model\Order + */ + private function getOrder(int $orderId) + { + return $this->orderRepository->get($orderId); } } diff --git a/InventoryInStorePickup/Model/Order/Email/Container/ReadyForPickupIdentity.php b/InventoryInStorePickup/Model/Order/Email/Container/ReadyForPickupIdentity.php new file mode 100644 index 000000000000..88719b363600 --- /dev/null +++ b/InventoryInStorePickup/Model/Order/Email/Container/ReadyForPickupIdentity.php @@ -0,0 +1,91 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickup\Model\Order\Email\Container; + +use Magento\Sales\Model\Order\Email\Container\Container; +use Magento\Sales\Model\Order\Email\Container\IdentityInterface; + +class ReadyForPickupIdentity extends Container implements IdentityInterface +{ + /** + * Configuration paths + */ + const XML_PATH_EMAIL_COPY_METHOD = 'storepickup_email/order_ready_for_pickup/copy_method'; + const XML_PATH_EMAIL_COPY_TO = 'storepickup_email/order_ready_for_pickup/copy_to'; + const XML_PATH_EMAIL_IDENTITY = 'storepickup_email/order_ready_for_pickup/identity'; + const XML_PATH_EMAIL_GUEST_TEMPLATE = 'storepickup_email/order_ready_for_pickup/guest_template'; + const XML_PATH_EMAIL_TEMPLATE = 'storepickup_email/order_ready_for_pickup/template'; + const XML_PATH_EMAIL_ENABLED = 'storepickup_email/order_ready_for_pickup/enabled'; + + /** + * @return bool + */ + public function isEnabled() + { + return $this->scopeConfig->isSetFlag( + self::XML_PATH_EMAIL_ENABLED, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + $this->getStore()->getStoreId() + ); + } + + /** + * Return email copy_to list + * + * @return array|bool + */ + public function getEmailCopyTo() + { + $data = $this->getConfigValue(self::XML_PATH_EMAIL_COPY_TO, $this->getStore()->getStoreId()); + if (!empty($data)) { + return explode(',', $data); + } + + return false; + } + + /** + * Return copy method + * + * @return mixed + */ + public function getCopyMethod() + { + return $this->getConfigValue(self::XML_PATH_EMAIL_COPY_METHOD, $this->getStore()->getStoreId()); + } + + /** + * Return guest template id + * + * @return mixed + */ + public function getGuestTemplateId() + { + return $this->getConfigValue(self::XML_PATH_EMAIL_GUEST_TEMPLATE, $this->getStore()->getStoreId()); + } + + /** + * Return template id + * + * @return mixed + */ + public function getTemplateId() + { + return $this->getConfigValue(self::XML_PATH_EMAIL_TEMPLATE, $this->getStore()->getStoreId()); + } + + /** + * Return email identity + * + * @return mixed + */ + public function getEmailIdentity() + { + return $this->getConfigValue(self::XML_PATH_EMAIL_IDENTITY, $this->getStore()->getStoreId()); + } +} diff --git a/InventoryInStorePickup/Model/Order/Email/ReadyForPickupNotifier.php b/InventoryInStorePickup/Model/Order/Email/ReadyForPickupNotifier.php new file mode 100644 index 000000000000..786362f81399 --- /dev/null +++ b/InventoryInStorePickup/Model/Order/Email/ReadyForPickupNotifier.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickup\Model\Order\Email; + +use Magento\Sales\Model\Order\Email\Sender\OrderSender; +use Magento\Sales\Model\ResourceModel\Order\Status\History\CollectionFactory; +use Psr\Log\LoggerInterface as Logger; + +/** + * Class ReadyForPickupNotifier + * + * @package Magento\InventoryInStorePickup\Model\Order\Email + * TODO: probaly remove this class + */ +class ReadyForPickupNotifier extends \Magento\Sales\Model\AbstractNotifier +{ + /** + * @var CollectionFactory + */ + protected $historyCollectionFactory; + + /** + * @var \Psr\Log\LoggerInterface + */ + protected $logger; + + /** + * @var OrderSender + */ + protected $sender; + + /** + * @param CollectionFactory $historyCollectionFactory + * @param Logger $logger + * @param \Magento\InventoryInStorePickup\Model\Order\Email\ReadyForPickupSender $sender + */ + public function __construct( + CollectionFactory $historyCollectionFactory, + Logger $logger, + ReadyForPickupSender $sender + ) { + $this->historyCollectionFactory = $historyCollectionFactory; + $this->logger = $logger; + $this->sender = $sender; + } +} diff --git a/InventoryInStorePickup/Model/Order/Email/ReadyForPickupSender.php b/InventoryInStorePickup/Model/Order/Email/ReadyForPickupSender.php new file mode 100644 index 000000000000..45a9985b4a44 --- /dev/null +++ b/InventoryInStorePickup/Model/Order/Email/ReadyForPickupSender.php @@ -0,0 +1,88 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickup\Model\Order\Email; + +use Magento\Framework\DataObject; + +/** + * Class ReadyForPickupSender + * + * @package Magento\InventoryInStorePickup\Model\Order\Email + * TODO: refactor + * TODO: Implement asynchronous email sending + */ +class ReadyForPickupSender extends \Magento\Sales\Model\Order\Email\Sender +{ + /** + * @var \Magento\Framework\Event\ManagerInterface + */ + private $eventManager; + + /** + * ReadyForPickupSender constructor. + * + * @param \Magento\Sales\Model\Order\Email\Container\Template $templateContainer + * @param \Magento\Sales\Model\Order\Email\Container\IdentityInterface $identityContainer + * @param \Magento\Sales\Model\Order\Email\SenderBuilderFactory $senderBuilderFactory + * @param \Psr\Log\LoggerInterface $logger + * @param \Magento\Sales\Model\Order\Address\Renderer $addressRenderer + * @param \Magento\Framework\Event\ManagerInterface $eventManager + */ + public function __construct( + \Magento\Sales\Model\Order\Email\Container\Template $templateContainer, + \Magento\Sales\Model\Order\Email\Container\IdentityInterface $identityContainer, + \Magento\Sales\Model\Order\Email\SenderBuilderFactory $senderBuilderFactory, + \Psr\Log\LoggerInterface $logger, + \Magento\Sales\Model\Order\Address\Renderer $addressRenderer, + \Magento\Framework\Event\ManagerInterface $eventManager + ) { + parent::__construct($templateContainer, $identityContainer, $senderBuilderFactory, $logger, $addressRenderer); + + $this->eventManager = $eventManager; + } + + /** + * @param \Magento\Sales\Model\Order $order + * + * @return bool + */ + public function send(\Magento\Sales\Model\Order $order):bool + { + return $this->checkAndSend($order); + } + + /** + * Prepare email template with variables + * + * @param \Magento\Sales\Model\ $order + * + * @return void + */ + protected function prepareTemplate(\Magento\Sales\Model\Order $order) + { + $transport = [ + 'order' => $order, + 'store' => $order->getStore(), + 'formattedShippingAddress' => $this->getFormattedShippingAddress($order), + ]; + $transportObject = new DataObject($transport); + + /** + * Event argument `transport` is @deprecated. Use `transportObject` instead. + */ + $this->eventManager->dispatch( + 'email_ready_for_pickup_set_template_vars_before', + ['sender' => $this, 'transport' => $transportObject, 'transportObject' => $transportObject] + ); + + $this->templateContainer->setTemplateVars($transportObject->getData()); + + parent::prepareTemplate($order); + } + +} diff --git a/InventoryInStorePickup/composer.json b/InventoryInStorePickup/composer.json index caa00eafee06..8d438f55a232 100644 --- a/InventoryInStorePickup/composer.json +++ b/InventoryInStorePickup/composer.json @@ -6,7 +6,8 @@ "magento/framework": "*", "magento/module-inventory-in-store-pickup-api": "*", "magento/module-inventory-api": "*", - "magento/module-sales": "*" + "magento/module-sales": "*", + "magento/module-store": "*" }, "type": "magento2-module", "license": [ diff --git a/InventoryInStorePickup/etc/config.xml b/InventoryInStorePickup/etc/config.xml new file mode 100644 index 000000000000..33c4380f25c2 --- /dev/null +++ b/InventoryInStorePickup/etc/config.xml @@ -0,0 +1,26 @@ +<?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> + <storepickup_email> + <order_ready_for_pickup> + <enabled>1</enabled> + <template>inventory_instorepickup_order_ready_for_pickup_template</template> + <guest_template>inventory_instorepickup_order_ready_for_pickup_template</guest_template> + <identity>storepickup</identity> + <copy_method>bcc</copy_method> + </order_ready_for_pickup> + </storepickup_email> + <trans_email> + <ident_storepickup> + <email>sales@example.com</email> + <name>Store pickup</name> + </ident_storepickup> + </trans_email> + </default> +</config> diff --git a/InventoryInStorePickup/etc/di.xml b/InventoryInStorePickup/etc/di.xml index f76750e559df..3c4229917676 100644 --- a/InventoryInStorePickup/etc/di.xml +++ b/InventoryInStorePickup/etc/di.xml @@ -30,5 +30,9 @@ type="\Magento\InventoryInStorePickup\Model\NotifyOrderIsReadyAndShip"/> <preference for="\Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface" type="\Magento\InventoryInStorePickup\Model\IsOrderReadyForPickup"/> - + <type name="\Magento\InventoryInStorePickup\Model\Order\Email\ReadyForPickupSender"> + <arguments> + <argument name="identityContainer" xsi:type="object">\Magento\InventoryInStorePickup\Model\Order\Email\Container\ReadyForPickupIdentity</argument> + </arguments> + </type> </config> diff --git a/InventoryInStorePickup/etc/email_templates.xml b/InventoryInStorePickup/etc/email_templates.xml new file mode 100644 index 000000000000..58fb410e0239 --- /dev/null +++ b/InventoryInStorePickup/etc/email_templates.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:module:Magento_Email:etc/email_templates.xsd"> + <template id="inventory_instorepickup_order_ready_for_pickup_template" label="Order is Ready for Pickup" + file="order_ready_for_pickup.html" type="html" module="Magento_InventoryInStorePickup" area="frontend" + /> +</config> diff --git a/InventoryInStorePickup/etc/module.xml b/InventoryInStorePickup/etc/module.xml index 345728250831..3d5b65c23e23 100644 --- a/InventoryInStorePickup/etc/module.xml +++ b/InventoryInStorePickup/etc/module.xml @@ -11,6 +11,7 @@ <module name="Magento_Inventory"/> <module name="Magento_InventoryInStorePickupApi"/> <module name="Magento_Sales" /> + <module name="Magento_Store" /> </sequence> </module> </config> diff --git a/InventoryInStorePickup/view/frontend/email/order_ready_for_pickup.html b/InventoryInStorePickup/view/frontend/email/order_ready_for_pickup.html new file mode 100644 index 000000000000..908ec0a30a70 --- /dev/null +++ b/InventoryInStorePickup/view/frontend/email/order_ready_for_pickup.html @@ -0,0 +1,53 @@ +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<!--@subject {{trans "Your %store_name order is ready for pickup" store_name=$store.getFrontendName()}} @--> +<!--@vars { +"var order.getEmailCustomerNote()":"Email Order Note", +"var order.increment_id":"Order Id", +"var formattedShippingAddress|raw":"Shipping Address", +"var order.getShippingDescription()":"Shipping Description", +} @--> + +{{template config_path="design/email/header_template"}} + +<table> + <tr class="email-intro"> + <td> + <p class="greeting">{{trans "%customer_name," customer_name=$order.getCustomerName()}}</p> + <p> + {{trans "Your order placed from %store_name is ready for pickup." store_name=$store.getFrontendName()}} + </p> + </td> + </tr> + <tr class="email-summary"> + <td> + <h1>{{trans 'Your Order <span class="no-link">#%increment_id</span> is complete' + increment_id=$order.increment_id |raw}}</h1> + <p>{{trans "Please fell free to pickup your order now."}}</p> + </td> + </tr> + <tr class="email-information"> + <td> + <table class="order-details"> + <tr> + <td class="address-details"> + <h3>{{trans "Shipping Info"}}</h3> + <p>{{var formattedShippingAddress|raw}}</p> + </td> + </tr> + <tr> + <td class="method-info"> + <h3>{{trans "Shipping Method"}}</h3> + <p>{{var order.getShippingDescription()}}</p> + </td> + </tr> + </table> + </td> + </tr> +</table> + +{{template config_path="design/email/footer_template"}} diff --git a/InventoryInStorePickupApi/Api/NotifyOrderIsReadyAndShipInterface.php b/InventoryInStorePickupApi/Api/NotifyOrderIsReadyAndShipInterface.php index 0f06a28fa99e..db82ef862b82 100644 --- a/InventoryInStorePickupApi/Api/NotifyOrderIsReadyAndShipInterface.php +++ b/InventoryInStorePickupApi/Api/NotifyOrderIsReadyAndShipInterface.php @@ -12,11 +12,11 @@ interface NotifyOrderIsReadyAndShipInterface /** * @param int $orderId * - * @return int Id of created Shipment + * @return int|null Id of created Shipment * * @throws \Magento\Framework\Exception\NoSuchEntityException * @throws \Magento\InventoryInStorePickupApi\Exception\OrderIsNotReadyForPickupException * @throws \Magento\Framework\Exception\LocalizedException */ - public function execute(int $orderId):int; + public function execute(int $orderId):?int; } From 1cae2bad70b515047b23b30ece0b3196883ffcab Mon Sep 17 00:00:00 2001 From: Maksym Novik <novik.kor@gmail.com> Date: Mon, 1 Apr 2019 23:22:04 +0300 Subject: [PATCH 059/231] Add "Ready for Pickup" button on the Order page in Admin Panel which notifies Customer that Order could be picked up #2034. Do not show button is order is not placed using storepickup or it can not be shipped --- .../Adminhtml/Order/View/ReadyForPickup.php | 19 ++++++++-------- .../Model/IsDisplayReadyForPickupButton.php | 22 +++++++++++++++++++ 2 files changed, 31 insertions(+), 10 deletions(-) create mode 100644 InventoryInStorePickupAdminUi/Model/IsDisplayReadyForPickupButton.php diff --git a/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php b/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php index 54b508a21fbb..d908535f09ea 100644 --- a/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php +++ b/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php @@ -30,26 +30,26 @@ class ReadyForPickup extends \Magento\Backend\Block\Widget\Form\Container private $viewBlock; /** - * @var \Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface + * @var \Magento\InventoryInStorePickupAdminUi\Model\IsDisplayReadyForPickupButton */ - private $isReadyForPickup; + private $isDisplayButton; /** * ReadyForPickup constructor. * - * @param \Magento\Backend\Block\Widget\Context $context - * @param \Magento\Sales\Block\Adminhtml\Order\View $viewBlock - * @param \Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface $isReadyForPickup - * @param array $data + * @param \Magento\Backend\Block\Widget\Context $context + * @param \Magento\Sales\Block\Adminhtml\Order\View $viewBlock + * @param \Magento\InventoryInStorePickupAdminUi\Model\IsDisplayReadyForPickupButton $isDisplayButton + * @param array $data */ public function __construct( \Magento\Backend\Block\Widget\Context $context, \Magento\Sales\Block\Adminhtml\Order\View $viewBlock, - IsOrderReadyForPickupInterface $isReadyForPickup, + \Magento\InventoryInStorePickupAdminUi\Model\IsDisplayReadyForPickupButton $isDisplayButton, array $data = [] ) { $this->viewBlock = $viewBlock; - $this->isReadyForPickup = $isReadyForPickup; + $this->isDisplayButton = $isDisplayButton; parent::__construct($context, $data); } @@ -63,7 +63,6 @@ protected function _construct() $this->_controller = 'adminhtml_order'; $this->_mode = 'view'; - /* TODO: always display but throw warnings? */ if ($this->isDisplayButton()) { $message = __( 'Are you sure you want to notify the customer that order is ready for pickup and create shipment?' @@ -97,6 +96,6 @@ private function isEmailsSendingAllowed():bool private function isDisplayButton():bool { return $this->isEmailsSendingAllowed() - && $this->isReadyForPickup->execute((int)$this->viewBlock->getOrderId()); + && $this->isDisplayButton->execute($this->viewBlock->getOrder()); } } diff --git a/InventoryInStorePickupAdminUi/Model/IsDisplayReadyForPickupButton.php b/InventoryInStorePickupAdminUi/Model/IsDisplayReadyForPickupButton.php new file mode 100644 index 000000000000..7eacc05ba2fb --- /dev/null +++ b/InventoryInStorePickupAdminUi/Model/IsDisplayReadyForPickupButton.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickupAdminUi\Model; + +class IsDisplayReadyForPickupButton +{ + /** + * @param \Magento\Sales\Model\Order $order + * + * @return bool + */ + public function execute(\Magento\Sales\Model\Order $order):bool + { + return $order->getExtensionAttributes()->getPickupLocationCode() + && $order->canShip(); + } +} From e1597cc9068c0318fb6f9df44922aecf4b5dc779 Mon Sep 17 00:00:00 2001 From: Maksym Novik <m.novik@ism-ukraine.com> Date: Wed, 3 Apr 2019 17:40:10 +0300 Subject: [PATCH 060/231] Add ready for pickup button on the order page in admin panel which notifies customer that order could be picked up 2034. Added Magento_Backend to Magento_InventoryInStorePickupAdminUi dependencies --- InventoryInStorePickupAdminUi/etc/module.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/InventoryInStorePickupAdminUi/etc/module.xml b/InventoryInStorePickupAdminUi/etc/module.xml index 2378b4cd90e5..df41c09771b8 100644 --- a/InventoryInStorePickupAdminUi/etc/module.xml +++ b/InventoryInStorePickupAdminUi/etc/module.xml @@ -11,6 +11,7 @@ <module name="Magento_InventoryInStorePickup" /> <module name="Magento_Shipping" /> <module name="Magento_Sales" /> + <module name="Magento_Backend" /> </sequence> </module> </config> From cf190bd3c34e89821c9e5619adca4140df008f41 Mon Sep 17 00:00:00 2001 From: Maksym Novik <m.novik@ism-ukraine.com> Date: Wed, 3 Apr 2019 18:10:28 +0300 Subject: [PATCH 061/231] Add ready for pickup button on the order page in admin panel which notifies customer that order could be picked up 2034. Reformated code a little bit --- .../Model/IsOrderReadyForPickup.php | 6 +++--- .../Model/NotifyOrderIsReadyAndShip.php | 9 +++++---- .../Model/Order/Email/ReadyForPickupNotifier.php | 4 ++-- .../Model/Order/Email/ReadyForPickupSender.php | 12 ++++++------ InventoryInStorePickup/Model/Order/IsFulfilled.php | 10 +++++----- .../Sales/Order/GetPickupLocationForOrderPlugin.php | 2 +- .../Block/Adminhtml/Order/View/ReadyForPickup.php | 11 +++++------ .../Controller/Adminhtml/Order/NotifyPickup.php | 10 +++++----- .../Model/IsDisplayReadyForPickupButton.php | 2 +- .../Api/IsOrderReadyForPickupInterface.php | 2 +- .../Api/NotifyOrderIsReadyAndShipInterface.php | 2 +- .../Exception/OrderIsNotReadyForPickupException.php | 4 ++-- 12 files changed, 37 insertions(+), 37 deletions(-) diff --git a/InventoryInStorePickup/Model/IsOrderReadyForPickup.php b/InventoryInStorePickup/Model/IsOrderReadyForPickup.php index e2b3dca15fa3..2d2567d7e48e 100644 --- a/InventoryInStorePickup/Model/IsOrderReadyForPickup.php +++ b/InventoryInStorePickup/Model/IsOrderReadyForPickup.php @@ -27,7 +27,7 @@ class IsOrderReadyForPickup implements IsOrderReadyForPickupInterface * IsReadyForPickup constructor. * * @param \Magento\InventoryInStorePickup\Model\Order\IsFulfilled $isFulfilled - * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository + * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository */ public function __construct( IsFulfilled $isFulfilled, @@ -42,7 +42,7 @@ public function __construct( * * @return bool */ - public function execute(int $orderId):bool + public function execute(int $orderId): bool { return $this->canShip($orderId) && $this->isFulfilled->execute($orderId); } @@ -52,7 +52,7 @@ public function execute(int $orderId):bool * * @return bool */ - private function canShip(int $orderId):bool + private function canShip(int $orderId): bool { $order = $this->orderRepository->get($orderId); if ($order instanceof \Magento\Sales\Model\Order) { diff --git a/InventoryInStorePickup/Model/NotifyOrderIsReadyAndShip.php b/InventoryInStorePickup/Model/NotifyOrderIsReadyAndShip.php index 276d2ef5b7dc..0ba020cbe807 100644 --- a/InventoryInStorePickup/Model/NotifyOrderIsReadyAndShip.php +++ b/InventoryInStorePickup/Model/NotifyOrderIsReadyAndShip.php @@ -36,10 +36,10 @@ class NotifyOrderIsReadyAndShip implements NotifyOrderIsReadyAndShipInterface /** * NotifyOrderIsReadyAndShip constructor. * - * @param \Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface $isOrderReadyForPickup - * @param \Magento\Sales\Api\ShipOrderInterface $shipOrder + * @param \Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface $isOrderReadyForPickup + * @param \Magento\Sales\Api\ShipOrderInterface $shipOrder * @param \Magento\InventoryInStorePickup\Model\Order\Email\ReadyForPickupNotifier $emailNotifier - * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository + * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository */ public function __construct( IsOrderReadyForPickupInterface $isOrderReadyForPickup, @@ -56,13 +56,14 @@ public function __construct( /** * {@inheritdoc} */ - public function execute(int $orderId):?int + public function execute(int $orderId): ?int { if (!$this->isOrderReadyForPickup->execute($orderId)) { throw new OrderIsNotReadyForPickupException(); } $this->emailNotifier->notify($this->getOrder($orderId)); + /* TODO: add order comment? */ return (int)$this->shipOrder->execute($orderId); diff --git a/InventoryInStorePickup/Model/Order/Email/ReadyForPickupNotifier.php b/InventoryInStorePickup/Model/Order/Email/ReadyForPickupNotifier.php index 786362f81399..f3af8fdacb69 100644 --- a/InventoryInStorePickup/Model/Order/Email/ReadyForPickupNotifier.php +++ b/InventoryInStorePickup/Model/Order/Email/ReadyForPickupNotifier.php @@ -35,8 +35,8 @@ class ReadyForPickupNotifier extends \Magento\Sales\Model\AbstractNotifier protected $sender; /** - * @param CollectionFactory $historyCollectionFactory - * @param Logger $logger + * @param CollectionFactory $historyCollectionFactory + * @param Logger $logger * @param \Magento\InventoryInStorePickup\Model\Order\Email\ReadyForPickupSender $sender */ public function __construct( diff --git a/InventoryInStorePickup/Model/Order/Email/ReadyForPickupSender.php b/InventoryInStorePickup/Model/Order/Email/ReadyForPickupSender.php index 45a9985b4a44..acd962afa916 100644 --- a/InventoryInStorePickup/Model/Order/Email/ReadyForPickupSender.php +++ b/InventoryInStorePickup/Model/Order/Email/ReadyForPickupSender.php @@ -26,12 +26,12 @@ class ReadyForPickupSender extends \Magento\Sales\Model\Order\Email\Sender /** * ReadyForPickupSender constructor. * - * @param \Magento\Sales\Model\Order\Email\Container\Template $templateContainer + * @param \Magento\Sales\Model\Order\Email\Container\Template $templateContainer * @param \Magento\Sales\Model\Order\Email\Container\IdentityInterface $identityContainer - * @param \Magento\Sales\Model\Order\Email\SenderBuilderFactory $senderBuilderFactory - * @param \Psr\Log\LoggerInterface $logger - * @param \Magento\Sales\Model\Order\Address\Renderer $addressRenderer - * @param \Magento\Framework\Event\ManagerInterface $eventManager + * @param \Magento\Sales\Model\Order\Email\SenderBuilderFactory $senderBuilderFactory + * @param \Psr\Log\LoggerInterface $logger + * @param \Magento\Sales\Model\Order\Address\Renderer $addressRenderer + * @param \Magento\Framework\Event\ManagerInterface $eventManager */ public function __construct( \Magento\Sales\Model\Order\Email\Container\Template $templateContainer, @@ -51,7 +51,7 @@ public function __construct( * * @return bool */ - public function send(\Magento\Sales\Model\Order $order):bool + public function send(\Magento\Sales\Model\Order $order): bool { return $this->checkAndSend($order); } diff --git a/InventoryInStorePickup/Model/Order/IsFulfilled.php b/InventoryInStorePickup/Model/Order/IsFulfilled.php index eaf16114152b..a4d91a62d5a5 100644 --- a/InventoryInStorePickup/Model/Order/IsFulfilled.php +++ b/InventoryInStorePickup/Model/Order/IsFulfilled.php @@ -29,9 +29,9 @@ class IsFulfilled /** * IsReadyForPickup constructor. * - * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository + * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository * @param \Magento\InventoryApi\Api\SourceItemRepositoryInterface $sourceItemRepository - * @param \Magento\Framework\Api\SearchCriteriaBuilderFactory $searchCriteriaBuilder + * @param \Magento\Framework\Api\SearchCriteriaBuilderFactory $searchCriteriaBuilder */ public function __construct( \Magento\Sales\Api\OrderRepositoryInterface $orderRepository, @@ -48,7 +48,7 @@ public function __construct( * * @return bool */ - public function execute(int $orderId):bool + public function execute(int $orderId): bool { $order = $this->orderRepository->get($orderId); @@ -68,11 +68,11 @@ public function execute(int $orderId):bool /** * @param string $sku * @param string $sourceCode - * @param float $qtyOrdered + * @param float $qtyOrdered * * @return bool */ - private function isItemFulfilled(string $sku, string $sourceCode, float $qtyOrdered):bool + private function isItemFulfilled(string $sku, string $sourceCode, float $qtyOrdered): bool { $searchCriteria = $this->searchCriteriaBuilderFactory ->create() diff --git a/InventoryInStorePickup/Plugin/Sales/Order/GetPickupLocationForOrderPlugin.php b/InventoryInStorePickup/Plugin/Sales/Order/GetPickupLocationForOrderPlugin.php index e50095e695cb..84c026a819c2 100644 --- a/InventoryInStorePickup/Plugin/Sales/Order/GetPickupLocationForOrderPlugin.php +++ b/InventoryInStorePickup/Plugin/Sales/Order/GetPickupLocationForOrderPlugin.php @@ -28,7 +28,7 @@ class GetPickupLocationForOrderPlugin private $getPickupLocationByOrderId; /** - * @param OrderExtensionFactory $orderExtensionFactory + * @param OrderExtensionFactory $orderExtensionFactory * @param GetPickupLocationCodeByOrderId $getPickupLocationByOrderId */ public function __construct( diff --git a/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php b/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php index d908535f09ea..ec8761fb3aa8 100644 --- a/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php +++ b/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php @@ -8,7 +8,6 @@ namespace Magento\InventoryInStorePickupAdminUi\Block\Adminhtml\Order\View; use Magento\InventoryInStorePickupAdminUi\Controller\Adminhtml\Order\NotifyPickup; -use Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface; /** * TODO: is it possible to replace with UI Component? @@ -37,10 +36,10 @@ class ReadyForPickup extends \Magento\Backend\Block\Widget\Form\Container /** * ReadyForPickup constructor. * - * @param \Magento\Backend\Block\Widget\Context $context - * @param \Magento\Sales\Block\Adminhtml\Order\View $viewBlock + * @param \Magento\Backend\Block\Widget\Context $context + * @param \Magento\Sales\Block\Adminhtml\Order\View $viewBlock * @param \Magento\InventoryInStorePickupAdminUi\Model\IsDisplayReadyForPickupButton $isDisplayButton - * @param array $data + * @param array $data */ public function __construct( \Magento\Backend\Block\Widget\Context $context, @@ -85,7 +84,7 @@ protected function _construct() /** * @return bool */ - private function isEmailsSendingAllowed():bool + private function isEmailsSendingAllowed(): bool { return $this->_authorization->isAllowed(NotifyPickup::ADMIN_RESOURCE); } @@ -93,7 +92,7 @@ private function isEmailsSendingAllowed():bool /** * @return bool */ - private function isDisplayButton():bool + private function isDisplayButton(): bool { return $this->isEmailsSendingAllowed() && $this->isDisplayButton->execute($this->viewBlock->getOrder()); diff --git a/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php b/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php index 8577ccdf7291..491da361fc64 100644 --- a/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php +++ b/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php @@ -45,10 +45,10 @@ class NotifyPickup extends \Magento\Backend\App\Action /** * NotifyPickup constructor. * - * @param \Magento\Backend\App\Action\Context $context + * @param \Magento\Backend\App\Action\Context $context * @param \Magento\InventoryInStorePickupApi\Api\NotifyOrderIsReadyAndShipInterface $notifyOrderIsReadyAndShip - * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository - * @param \Psr\Log\LoggerInterface $logger + * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository + * @param \Psr\Log\LoggerInterface $logger */ public function __construct( \Magento\Backend\App\Action\Context $context, @@ -68,7 +68,7 @@ public function __construct( * * @return \Magento\Framework\Controller\ResultInterface */ - public function execute():\Magento\Framework\Controller\ResultInterface + public function execute(): \Magento\Framework\Controller\ResultInterface { $order = $this->initOrder(); @@ -97,9 +97,9 @@ public function execute():\Magento\Framework\Controller\ResultInterface /** * Initialize order model instance * + * @return \Magento\Sales\Api\Data\OrderInterface|false * @see \Magento\Sales\Controller\Adminhtml\Order::_initOrder * - * @return \Magento\Sales\Api\Data\OrderInterface|false */ private function initOrder() { diff --git a/InventoryInStorePickupAdminUi/Model/IsDisplayReadyForPickupButton.php b/InventoryInStorePickupAdminUi/Model/IsDisplayReadyForPickupButton.php index 7eacc05ba2fb..1ea28fa20390 100644 --- a/InventoryInStorePickupAdminUi/Model/IsDisplayReadyForPickupButton.php +++ b/InventoryInStorePickupAdminUi/Model/IsDisplayReadyForPickupButton.php @@ -14,7 +14,7 @@ class IsDisplayReadyForPickupButton * * @return bool */ - public function execute(\Magento\Sales\Model\Order $order):bool + public function execute(\Magento\Sales\Model\Order $order): bool { return $order->getExtensionAttributes()->getPickupLocationCode() && $order->canShip(); diff --git a/InventoryInStorePickupApi/Api/IsOrderReadyForPickupInterface.php b/InventoryInStorePickupApi/Api/IsOrderReadyForPickupInterface.php index d03998bd63ba..b62dd2ec8e51 100644 --- a/InventoryInStorePickupApi/Api/IsOrderReadyForPickupInterface.php +++ b/InventoryInStorePickupApi/Api/IsOrderReadyForPickupInterface.php @@ -14,5 +14,5 @@ interface IsOrderReadyForPickupInterface * * @return bool */ - public function execute(int $orderId):bool; + public function execute(int $orderId): bool; } diff --git a/InventoryInStorePickupApi/Api/NotifyOrderIsReadyAndShipInterface.php b/InventoryInStorePickupApi/Api/NotifyOrderIsReadyAndShipInterface.php index db82ef862b82..d73f8ff238a7 100644 --- a/InventoryInStorePickupApi/Api/NotifyOrderIsReadyAndShipInterface.php +++ b/InventoryInStorePickupApi/Api/NotifyOrderIsReadyAndShipInterface.php @@ -18,5 +18,5 @@ interface NotifyOrderIsReadyAndShipInterface * @throws \Magento\InventoryInStorePickupApi\Exception\OrderIsNotReadyForPickupException * @throws \Magento\Framework\Exception\LocalizedException */ - public function execute(int $orderId):?int; + public function execute(int $orderId): ?int; } diff --git a/InventoryInStorePickupApi/Exception/OrderIsNotReadyForPickupException.php b/InventoryInStorePickupApi/Exception/OrderIsNotReadyForPickupException.php index 0511e6f0bd6c..800d0a4baa75 100644 --- a/InventoryInStorePickupApi/Exception/OrderIsNotReadyForPickupException.php +++ b/InventoryInStorePickupApi/Exception/OrderIsNotReadyForPickupException.php @@ -20,8 +20,8 @@ class OrderIsNotReadyForPickupException extends LocalizedException { /** * @param \Magento\Framework\Phrase $phrase - * @param \Exception $cause - * @param int $code + * @param \Exception $cause + * @param int $code */ public function __construct(Phrase $phrase = null, \Exception $cause = null, $code = 0) { From 9152ce794cafefb1ee39365118e8b645c5726361 Mon Sep 17 00:00:00 2001 From: vad1m777 <gam.vadik@gmail.com> Date: Wed, 3 Apr 2019 18:36:20 +0300 Subject: [PATCH 062/231] MSI-2113: In stock, 0 qty and backorders allowed products not visible on category pages --- .../BackordersCondition.php | 11 ++- .../BackorderConditionTest.php | 75 ++++++++++++++++--- 2 files changed, 73 insertions(+), 13 deletions(-) diff --git a/InventorySales/Model/ResourceModel/IsStockItemSalableCondition/BackordersCondition.php b/InventorySales/Model/ResourceModel/IsStockItemSalableCondition/BackordersCondition.php index 5e8bbf77f4d7..6405a3cd833c 100644 --- a/InventorySales/Model/ResourceModel/IsStockItemSalableCondition/BackordersCondition.php +++ b/InventorySales/Model/ResourceModel/IsStockItemSalableCondition/BackordersCondition.php @@ -35,11 +35,14 @@ public function __construct(StockConfigurationInterface $configuration) public function execute(Select $select): string { $globalBackorders = (int)$this->configuration->getBackorders(); + $itemBackordersCondition = 'legacy_stock_item.backorders <> 0'; + $useDefaultBackorders = 'legacy_stock_item.use_config_backorders'; + $itemMinQty = 'legacy_stock_item.min_qty'; - $condition = (1 === $globalBackorders) - ? 'legacy_stock_item.use_config_backorders = 1' - : 'legacy_stock_item.use_config_backorders = 0 AND legacy_stock_item.backorders = 1'; - $condition .= ' AND (legacy_stock_item.min_qty >= 0 OR legacy_stock_item.qty > legacy_stock_item.min_qty)'; + $condition = $globalBackorders === 0 + ? "$useDefaultBackorders = 0 AND $itemBackordersCondition" + : "$useDefaultBackorders = 1 OR $itemBackordersCondition"; + $condition .= " AND ($itemMinQty >= 0 OR legacy_stock_item.qty > $itemMinQty)"; return $condition; } diff --git a/InventorySales/Test/Integration/GetStockItemData/BackorderConditionTest.php b/InventorySales/Test/Integration/GetStockItemData/BackorderConditionTest.php index a60373bf2e0b..8be71d14d031 100644 --- a/InventorySales/Test/Integration/GetStockItemData/BackorderConditionTest.php +++ b/InventorySales/Test/Integration/GetStockItemData/BackorderConditionTest.php @@ -16,6 +16,7 @@ use Magento\InventoryApi\Api\SourceItemsSaveInterface; use Magento\InventorySalesApi\Model\GetStockItemDataInterface; use Magento\TestFramework\Helper\Bootstrap; +use Magento\InventoryConfigurationApi\Api\Data\StockItemConfigurationInterface; use PHPUnit\Framework\TestCase; /** @@ -62,7 +63,7 @@ class BackorderConditionTest extends TestCase /** * @inheritdoc */ - protected function setUp() + protected function setUp(): void { parent::setUp(); @@ -93,7 +94,7 @@ protected function setUp() * @param int $stockId * @param array|null $expectedData */ - public function testBackordersDisabled(string $sku, int $stockId, $expectedData) + public function testBackordersDisabled(string $sku, int $stockId, $expectedData): void { $stockItemData = $this->getStockItemData->execute($sku, $stockId); @@ -110,13 +111,13 @@ public function testBackordersDisabled(string $sku, int $stockId, $expectedData) * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stock_source_links.php * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php * @magentoConfigFixture current_store cataloginventory/item_options/backorders 1 - * @dataProvider backordersEnabledDataProvider + * @dataProvider backordersGlobalEnabledDataProvider * * @param string $sku * @param int $stockId * @param array|null $expectedData */ - public function testGlobalBackordersEnabled(string $sku, int $stockId, $expectedData) + public function testGlobalBackordersEnabled(string $sku, int $stockId, $expectedData): void { $stockItemData = $this->getStockItemData->execute($sku, $stockId); @@ -139,7 +140,7 @@ public function testGlobalBackordersEnabled(string $sku, int $stockId, $expected * @param int $stockId * @param array|null $expectedData */ - public function testStockItemBackordersDisabled(string $sku, int $stockId, $expectedData) + public function testStockItemBackordersDisabled(string $sku, int $stockId, $expectedData): void { $this->setStockItemBackorders($sku, 0); @@ -162,11 +163,12 @@ public function testStockItemBackordersDisabled(string $sku, int $stockId, $expe * * @param string $sku * @param int $stockId + * @param int $itemBackorders * @param array|null $expectedData */ - public function testStockItemBackordersEnabled(string $sku, int $stockId, $expectedData) + public function testStockItemBackordersEnabled(string $sku, int $stockId, int $itemBackorders, $expectedData): void { - $this->setStockItemBackorders($sku, 1); + $this->setStockItemBackorders($sku, $itemBackorders); $stockItemData = $this->getStockItemData->execute($sku, $stockId); @@ -174,11 +176,11 @@ public function testStockItemBackordersEnabled(string $sku, int $stockId, $expec } /** - * Data provider for test with enabled backorders. + * Data provider for test with global enabled backorders. * * @return array */ - public function backordersEnabledDataProvider(): array + public function backordersGlobalEnabledDataProvider(): array { return [ ['SKU-1', 10, [GetStockItemDataInterface::QUANTITY => 8.5, GetStockItemDataInterface::IS_SALABLE => 1]], @@ -187,6 +189,61 @@ public function backordersEnabledDataProvider(): array ]; } + /** + * Data provider for test with enabled backorders. + * + * @return array + */ + public function backordersEnabledDataProvider(): array + { + return [ + [ + 'SKU-1', + 10, + StockItemConfigurationInterface::BACKORDERS_YES_NONOTIFY, + [ + GetStockItemDataInterface::QUANTITY => 8.5, GetStockItemDataInterface::IS_SALABLE => 1 + ] + ], + [ + 'SKU-1', + 10, + StockItemConfigurationInterface::BACKORDERS_YES_NOTIFY, + [ + GetStockItemDataInterface::QUANTITY => 8.5, GetStockItemDataInterface::IS_SALABLE => 1 + ] + ], + [ + 'SKU-2', + 10, + StockItemConfigurationInterface::BACKORDERS_YES_NONOTIFY, + null + ], + [ + 'SKU-2', + 10, + StockItemConfigurationInterface::BACKORDERS_YES_NOTIFY, + null + ], + [ + 'SKU-3', + 10, + StockItemConfigurationInterface::BACKORDERS_YES_NONOTIFY, + [ + GetStockItemDataInterface::QUANTITY => 0, GetStockItemDataInterface::IS_SALABLE => 1 + ] + ], + [ + 'SKU-3', + 10, + StockItemConfigurationInterface::BACKORDERS_YES_NOTIFY, + [ + GetStockItemDataInterface::QUANTITY => 0, GetStockItemDataInterface::IS_SALABLE => 1 + ] + ], + ]; + } + /** * Data provider for test with disabled backorders. * From 897ed59265bcf30c7c4a270509ea39f0d7c8c20f Mon Sep 17 00:00:00 2001 From: vad1m777 <gam.vadik@gmail.com> Date: Fri, 5 Apr 2019 11:14:06 +0300 Subject: [PATCH 063/231] MSI-2113: In stock, 0 qty and backorders allowed products not visible on category pages --- .../Integration/GetStockItemData/BackorderConditionTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/InventorySales/Test/Integration/GetStockItemData/BackorderConditionTest.php b/InventorySales/Test/Integration/GetStockItemData/BackorderConditionTest.php index 8be71d14d031..ca2f4577dd64 100644 --- a/InventorySales/Test/Integration/GetStockItemData/BackorderConditionTest.php +++ b/InventorySales/Test/Integration/GetStockItemData/BackorderConditionTest.php @@ -142,7 +142,7 @@ public function testGlobalBackordersEnabled(string $sku, int $stockId, $expected */ public function testStockItemBackordersDisabled(string $sku, int $stockId, $expectedData): void { - $this->setStockItemBackorders($sku, 0); + $this->setStockItemBackorders($sku, StockItemConfigurationInterface::BACKORDERS_NO); $stockItemData = $this->getStockItemData->execute($sku, $stockId); @@ -274,7 +274,7 @@ private function setStockItemBackorders(string $sku, int $backordersStatus): voi /** @var StockItemInterface $legacyStockItem */ $legacyStockItem = current($stockItemsCollection->getItems()); $legacyStockItem->setBackorders($backordersStatus); - $legacyStockItem->setUseConfigBackorders(0); + $legacyStockItem->setUseConfigBackorders(false); $this->stockItemRepository->save($legacyStockItem); $sourceItem = $this->getSourceItemBySku($sku); From 6d8d97141973ddf7c118e2ee2e194f1feda58f5d Mon Sep 17 00:00:00 2001 From: vad1m777 <gam.vadik@gmail.com> Date: Fri, 5 Apr 2019 12:15:39 +0300 Subject: [PATCH 064/231] MSI-2113: In stock, 0 qty and backorders allowed products not visible on category pages --- .../BackordersCondition.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/InventorySales/Model/ResourceModel/IsStockItemSalableCondition/BackordersCondition.php b/InventorySales/Model/ResourceModel/IsStockItemSalableCondition/BackordersCondition.php index 6405a3cd833c..775a3e4e9499 100644 --- a/InventorySales/Model/ResourceModel/IsStockItemSalableCondition/BackordersCondition.php +++ b/InventorySales/Model/ResourceModel/IsStockItemSalableCondition/BackordersCondition.php @@ -9,6 +9,7 @@ use Magento\CatalogInventory\Api\StockConfigurationInterface; use Magento\Framework\DB\Select; +use Magento\InventoryConfigurationApi\Api\Data\StockItemConfigurationInterface; /** * Condition for backorders configuration. @@ -35,13 +36,15 @@ public function __construct(StockConfigurationInterface $configuration) public function execute(Select $select): string { $globalBackorders = (int)$this->configuration->getBackorders(); - $itemBackordersCondition = 'legacy_stock_item.backorders <> 0'; + $itemBackordersCondition = 'legacy_stock_item.backorders <> ' . StockItemConfigurationInterface::BACKORDERS_NO; $useDefaultBackorders = 'legacy_stock_item.use_config_backorders'; $itemMinQty = 'legacy_stock_item.min_qty'; - $condition = $globalBackorders === 0 - ? "$useDefaultBackorders = 0 AND $itemBackordersCondition" - : "$useDefaultBackorders = 1 OR $itemBackordersCondition"; + $condition = $globalBackorders === StockItemConfigurationInterface::BACKORDERS_NO + ? $useDefaultBackorders . ' = ' . StockItemConfigurationInterface::BACKORDERS_NO . ' AND ' . + $itemBackordersCondition + : $useDefaultBackorders . ' = ' . StockItemConfigurationInterface::BACKORDERS_YES_NONOTIFY . + ' OR ' . $itemBackordersCondition; $condition .= " AND ($itemMinQty >= 0 OR legacy_stock_item.qty > $itemMinQty)"; return $condition; From 6865ccb736619cb600e4b40789235504156b5ce1 Mon Sep 17 00:00:00 2001 From: Maksym Novik <m.novik@ism-ukraine.com> Date: Fri, 5 Apr 2019 15:59:36 +0300 Subject: [PATCH 065/231] Add ready for pickup button on the order page in admin panel which notifies customer that order could be picked up 2034. Code review adjustments & static tests fix --- .../Model/IsOrderReadyForPickup.php | 27 +++++----- ...ip.php => NotifyOrderIsReadyForPickup.php} | 17 ++---- .../{IsFulfilled.php => CanBeFulfilled.php} | 29 ++++------ .../Container/ReadyForPickupIdentity.php | 12 ++--- .../Order/Email/ReadyForPickupSender.php | 11 ++-- InventoryInStorePickup/etc/di.xml | 4 +- .../Adminhtml/Order/NotifyPickup.php | 53 ++++++++++--------- InventoryInStorePickupAdminUi/composer.json | 3 +- InventoryInStorePickupAdminUi/i18n/en_US.csv | 5 +- ... NotifyOrderIsReadyForPickupInterface.php} | 2 +- 10 files changed, 78 insertions(+), 85 deletions(-) rename InventoryInStorePickup/Model/{NotifyOrderIsReadyAndShip.php => NotifyOrderIsReadyForPickup.php} (81%) rename InventoryInStorePickup/Model/Order/{IsFulfilled.php => CanBeFulfilled.php} (69%) rename InventoryInStorePickupApi/Api/{NotifyOrderIsReadyAndShipInterface.php => NotifyOrderIsReadyForPickupInterface.php} (92%) diff --git a/InventoryInStorePickup/Model/IsOrderReadyForPickup.php b/InventoryInStorePickup/Model/IsOrderReadyForPickup.php index 2d2567d7e48e..941c13561ca5 100644 --- a/InventoryInStorePickup/Model/IsOrderReadyForPickup.php +++ b/InventoryInStorePickup/Model/IsOrderReadyForPickup.php @@ -7,16 +7,18 @@ namespace Magento\InventoryInStorePickup\Model; -use Magento\InventoryInStorePickup\Model\Order\IsFulfilled; +use Magento\InventoryInStorePickup\Model\Order\CanBeFulfilled; use Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface; +use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order; class IsOrderReadyForPickup implements IsOrderReadyForPickupInterface { /** - * @var \Magento\InventoryInStorePickup\Model\Order\IsFulfilled + * @var \Magento\InventoryInStorePickup\Model\Order\CanBeFulfilled */ - private $isFulfilled; + private $canBeFulfilled; /** * @var \Magento\Sales\Api\OrderRepositoryInterface @@ -24,16 +26,14 @@ class IsOrderReadyForPickup implements IsOrderReadyForPickupInterface private $orderRepository; /** - * IsReadyForPickup constructor. - * - * @param \Magento\InventoryInStorePickup\Model\Order\IsFulfilled $isFulfilled + * @param \Magento\InventoryInStorePickup\Model\Order\CanBeFulfilled $canBeFulfilled * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository */ public function __construct( - IsFulfilled $isFulfilled, + CanBeFulfilled $canBeFulfilled, OrderRepositoryInterface $orderRepository ) { - $this->isFulfilled = $isFulfilled; + $this->canBeFulfilled = $canBeFulfilled; $this->orderRepository = $orderRepository; } @@ -44,18 +44,19 @@ public function __construct( */ public function execute(int $orderId): bool { - return $this->canShip($orderId) && $this->isFulfilled->execute($orderId); + $order = $this->orderRepository->get($orderId); + + return $this->canShip($order) && $this->canBeFulfilled->execute($order); } /** - * @param int $orderId + * @param \Magento\Sales\Api\Data\OrderInterface $order * * @return bool */ - private function canShip(int $orderId): bool + private function canShip(OrderInterface $order): bool { - $order = $this->orderRepository->get($orderId); - if ($order instanceof \Magento\Sales\Model\Order) { + if ($order instanceof Order) { return $order->canShip(); } diff --git a/InventoryInStorePickup/Model/NotifyOrderIsReadyAndShip.php b/InventoryInStorePickup/Model/NotifyOrderIsReadyForPickup.php similarity index 81% rename from InventoryInStorePickup/Model/NotifyOrderIsReadyAndShip.php rename to InventoryInStorePickup/Model/NotifyOrderIsReadyForPickup.php index 0ba020cbe807..25e91850c36a 100644 --- a/InventoryInStorePickup/Model/NotifyOrderIsReadyAndShip.php +++ b/InventoryInStorePickup/Model/NotifyOrderIsReadyForPickup.php @@ -8,10 +8,10 @@ namespace Magento\InventoryInStorePickup\Model; use Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface; -use Magento\InventoryInStorePickupApi\Api\NotifyOrderIsReadyAndShipInterface; +use Magento\InventoryInStorePickupApi\Api\NotifyOrderIsReadyForPickupInterface; use Magento\InventoryInStorePickupApi\Exception\OrderIsNotReadyForPickupException; -class NotifyOrderIsReadyAndShip implements NotifyOrderIsReadyAndShipInterface +class NotifyOrderIsReadyForPickup implements NotifyOrderIsReadyForPickupInterface { /** * @var \Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface @@ -62,20 +62,11 @@ public function execute(int $orderId): ?int throw new OrderIsNotReadyForPickupException(); } - $this->emailNotifier->notify($this->getOrder($orderId)); + /** @noinspection PhpParamsInspection */ + $this->emailNotifier->notify($this->orderRepository->get($orderId)); /* TODO: add order comment? */ return (int)$this->shipOrder->execute($orderId); } - - /** - * @param int $orderId - * - * @return \Magento\Sales\Api\Data\OrderInterface|\Magento\Sales\Model\Order - */ - private function getOrder(int $orderId) - { - return $this->orderRepository->get($orderId); - } } diff --git a/InventoryInStorePickup/Model/Order/IsFulfilled.php b/InventoryInStorePickup/Model/Order/CanBeFulfilled.php similarity index 69% rename from InventoryInStorePickup/Model/Order/IsFulfilled.php rename to InventoryInStorePickup/Model/Order/CanBeFulfilled.php index a4d91a62d5a5..96aa289626c2 100644 --- a/InventoryInStorePickup/Model/Order/IsFulfilled.php +++ b/InventoryInStorePickup/Model/Order/CanBeFulfilled.php @@ -8,14 +8,10 @@ namespace Magento\InventoryInStorePickup\Model\Order; use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\Sales\Api\Data\OrderInterface; -class IsFulfilled +class CanBeFulfilled { - /** - * @var \Magento\Sales\Api\OrderRepositoryInterface - */ - private $orderRepository; - /** * @var \Magento\InventoryApi\Api\SourceItemRepositoryInterface */ @@ -27,34 +23,29 @@ class IsFulfilled private $searchCriteriaBuilderFactory; /** - * IsReadyForPickup constructor. - * - * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository * @param \Magento\InventoryApi\Api\SourceItemRepositoryInterface $sourceItemRepository * @param \Magento\Framework\Api\SearchCriteriaBuilderFactory $searchCriteriaBuilder */ public function __construct( - \Magento\Sales\Api\OrderRepositoryInterface $orderRepository, \Magento\InventoryApi\Api\SourceItemRepositoryInterface $sourceItemRepository, \Magento\Framework\Api\SearchCriteriaBuilderFactory $searchCriteriaBuilder ) { - $this->orderRepository = $orderRepository; $this->sourceItemRepository = $sourceItemRepository; $this->searchCriteriaBuilderFactory = $searchCriteriaBuilder; } /** - * @param int $orderId + * @param \Magento\Sales\Api\Data\OrderInterface $order * * @return bool */ - public function execute(int $orderId): bool + public function execute(OrderInterface $order): bool { - $order = $this->orderRepository->get($orderId); - - if ($sourceCode = $order->getExtensionAttributes()->getPickupLocationCode()) { + if ($order->getExtensionAttributes() + && $sourceCode = $order->getExtensionAttributes()->getPickupLocationCode() + ) { foreach ($order->getItems() as $item) { - if (!$this->isItemFulfilled($item->getSku(), $sourceCode, (float)$item->getQtyOrdered())) { + if (!$this->canItemBeFulfilled($item->getSku(), $sourceCode, (float)$item->getQtyOrdered())) { return false; } } @@ -72,7 +63,7 @@ public function execute(int $orderId): bool * * @return bool */ - private function isItemFulfilled(string $sku, string $sourceCode, float $qtyOrdered): bool + private function canItemBeFulfilled(string $sku, string $sourceCode, float $qtyOrdered): bool { $searchCriteria = $this->searchCriteriaBuilderFactory ->create() @@ -85,7 +76,7 @@ private function isItemFulfilled(string $sku, string $sourceCode, float $qtyOrde /** @var SourceItemInterface $sourceItem */ $sourceItem = current($sourceItems->getItems()); - return $sourceItem->getQuantity() >= $qtyOrdered; + return bccomp((string)$sourceItem->getQuantity(), (string)$qtyOrdered) >= 0; } return false; diff --git a/InventoryInStorePickup/Model/Order/Email/Container/ReadyForPickupIdentity.php b/InventoryInStorePickup/Model/Order/Email/Container/ReadyForPickupIdentity.php index 88719b363600..18fdbc86e9ee 100644 --- a/InventoryInStorePickup/Model/Order/Email/Container/ReadyForPickupIdentity.php +++ b/InventoryInStorePickup/Model/Order/Email/Container/ReadyForPickupIdentity.php @@ -15,12 +15,12 @@ class ReadyForPickupIdentity extends Container implements IdentityInterface /** * Configuration paths */ - const XML_PATH_EMAIL_COPY_METHOD = 'storepickup_email/order_ready_for_pickup/copy_method'; - const XML_PATH_EMAIL_COPY_TO = 'storepickup_email/order_ready_for_pickup/copy_to'; - const XML_PATH_EMAIL_IDENTITY = 'storepickup_email/order_ready_for_pickup/identity'; - const XML_PATH_EMAIL_GUEST_TEMPLATE = 'storepickup_email/order_ready_for_pickup/guest_template'; - const XML_PATH_EMAIL_TEMPLATE = 'storepickup_email/order_ready_for_pickup/template'; - const XML_PATH_EMAIL_ENABLED = 'storepickup_email/order_ready_for_pickup/enabled'; + private const XML_PATH_EMAIL_COPY_METHOD = 'storepickup_email/order_ready_for_pickup/copy_method'; + private const XML_PATH_EMAIL_COPY_TO = 'storepickup_email/order_ready_for_pickup/copy_to'; + private const XML_PATH_EMAIL_IDENTITY = 'storepickup_email/order_ready_for_pickup/identity'; + private const XML_PATH_EMAIL_GUEST_TEMPLATE = 'storepickup_email/order_ready_for_pickup/guest_template'; + private const XML_PATH_EMAIL_TEMPLATE = 'storepickup_email/order_ready_for_pickup/template'; + private const XML_PATH_EMAIL_ENABLED = 'storepickup_email/order_ready_for_pickup/enabled'; /** * @return bool diff --git a/InventoryInStorePickup/Model/Order/Email/ReadyForPickupSender.php b/InventoryInStorePickup/Model/Order/Email/ReadyForPickupSender.php index acd962afa916..8e94b0bc58fe 100644 --- a/InventoryInStorePickup/Model/Order/Email/ReadyForPickupSender.php +++ b/InventoryInStorePickup/Model/Order/Email/ReadyForPickupSender.php @@ -8,6 +8,7 @@ namespace Magento\InventoryInStorePickup\Model\Order\Email; use Magento\Framework\DataObject; +use Magento\Sales\Model\Order; /** * Class ReadyForPickupSender @@ -51,7 +52,7 @@ public function __construct( * * @return bool */ - public function send(\Magento\Sales\Model\Order $order): bool + public function send(Order $order): bool { return $this->checkAndSend($order); } @@ -59,15 +60,15 @@ public function send(\Magento\Sales\Model\Order $order): bool /** * Prepare email template with variables * - * @param \Magento\Sales\Model\ $order + * @param \Magento\Sales\Model\Order $order * * @return void */ - protected function prepareTemplate(\Magento\Sales\Model\Order $order) + protected function prepareTemplate(Order $order) { $transport = [ - 'order' => $order, - 'store' => $order->getStore(), + 'order' => $order, + 'store' => $order->getStore(), 'formattedShippingAddress' => $this->getFormattedShippingAddress($order), ]; $transportObject = new DataObject($transport); diff --git a/InventoryInStorePickup/etc/di.xml b/InventoryInStorePickup/etc/di.xml index 3c4229917676..141cc1c64937 100644 --- a/InventoryInStorePickup/etc/di.xml +++ b/InventoryInStorePickup/etc/di.xml @@ -26,8 +26,8 @@ </arguments> </type> - <preference for="\Magento\InventoryInStorePickupApi\Api\NotifyOrderIsReadyAndShipInterface" - type="\Magento\InventoryInStorePickup\Model\NotifyOrderIsReadyAndShip"/> + <preference for="\Magento\InventoryInStorePickupApi\Api\NotifyOrderIsReadyForPickupInterface" + type="\Magento\InventoryInStorePickup\Model\NotifyOrderIsReadyForPickup"/> <preference for="\Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface" type="\Magento\InventoryInStorePickup\Model\IsOrderReadyForPickup"/> <type name="\Magento\InventoryInStorePickup\Model\Order\Email\ReadyForPickupSender"> diff --git a/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php b/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php index 491da361fc64..667148334087 100644 --- a/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php +++ b/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php @@ -7,9 +7,13 @@ namespace Magento\InventoryInStorePickupAdminUi\Controller\Adminhtml\Order; +use Magento\Backend\App\Action; +use Magento\Framework\Controller\ResultInterface; use Magento\Framework\Exception\InputException; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; -use Magento\InventoryInStorePickupApi\Api\NotifyOrderIsReadyAndShipInterface; +use Magento\InventoryInStorePickupApi\Api\NotifyOrderIsReadyForPickupInterface; +use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\OrderRepositoryInterface; use Psr\Log\LoggerInterface; @@ -18,7 +22,7 @@ * * @package Magento\InventoryInStorePickupAdminUi\Controller\Adminhtml\Order */ -class NotifyPickup extends \Magento\Backend\App\Action +class NotifyPickup extends Action { /** * Authorization level of a basic admin session @@ -28,9 +32,9 @@ class NotifyPickup extends \Magento\Backend\App\Action const ADMIN_RESOURCE = 'Magento_Sales::emails'; /** - * @var \Magento\InventoryInStorePickupApi\Api\NotifyOrderIsReadyAndShipInterface + * @var \Magento\InventoryInStorePickupApi\Api\NotifyOrderIsReadyForPickupInterface */ - private $notifyOrderIsReadyAndShip; + private $notifyOrderIsReadyForPickup; /** * @var \Magento\Sales\Api\OrderRepositoryInterface @@ -46,17 +50,17 @@ class NotifyPickup extends \Magento\Backend\App\Action * NotifyPickup constructor. * * @param \Magento\Backend\App\Action\Context $context - * @param \Magento\InventoryInStorePickupApi\Api\NotifyOrderIsReadyAndShipInterface $notifyOrderIsReadyAndShip + * @param \Magento\InventoryInStorePickupApi\Api\NotifyOrderIsReadyForPickupInterface $notifyOrderIsReadyForPickup * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository * @param \Psr\Log\LoggerInterface $logger */ public function __construct( - \Magento\Backend\App\Action\Context $context, - NotifyOrderIsReadyAndShipInterface $notifyOrderIsReadyAndShip, + Action\Context $context, + NotifyOrderIsReadyForPickupInterface $notifyOrderIsReadyForPickup, OrderRepositoryInterface $orderRepository, LoggerInterface $logger ) { - $this->notifyOrderIsReadyAndShip = $notifyOrderIsReadyAndShip; + $this->notifyOrderIsReadyForPickup = $notifyOrderIsReadyForPickup; $this->orderRepository = $orderRepository; $this->logger = $logger; @@ -67,16 +71,19 @@ public function __construct( * Notify user * * @return \Magento\Framework\Controller\ResultInterface + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function execute(): \Magento\Framework\Controller\ResultInterface + public function execute(): ResultInterface { $order = $this->initOrder(); if ($order) { try { - $this->notifyOrderIsReadyAndShip->execute((int)$order->getEntityId()); + $this->notifyOrderIsReadyForPickup->execute((int)$order->getEntityId()); $this->messageManager->addSuccessMessage(__('The customer have been notified and shipment created.')); - } catch (\Magento\Framework\Exception\LocalizedException $e) { + } catch (LocalizedException $e) { $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { $this->messageManager->addErrorMessage(__('We can\'t notify the customer right now.')); @@ -97,25 +104,23 @@ public function execute(): \Magento\Framework\Controller\ResultInterface /** * Initialize order model instance * - * @return \Magento\Sales\Api\Data\OrderInterface|false + * @return \Magento\Sales\Api\Data\OrderInterface + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException * @see \Magento\Sales\Controller\Adminhtml\Order::_initOrder - * */ - private function initOrder() + private function initOrder(): OrderInterface { $id = $this->getRequest()->getParam('order_id'); try { $order = $this->orderRepository->get($id); - } catch (NoSuchEntityException $e) { - $this->messageManager->addErrorMessage(__('This order no longer exists.')); - $this->_actionFlag->set('', self::FLAG_NO_DISPATCH, true); - - return false; - } catch (InputException $e) { - $this->messageManager->addErrorMessage(__('This order no longer exists.')); - $this->_actionFlag->set('', self::FLAG_NO_DISPATCH, true); - - return false; + } catch (LocalizedException $e) { + if ($e instanceof NoSuchEntityException || $e instanceof InputException) { + $this->messageManager->addErrorMessage(__('This order no longer exists.')); + $this->_actionFlag->set('', self::FLAG_NO_DISPATCH, true); + } + throw $e; } return $order; diff --git a/InventoryInStorePickupAdminUi/composer.json b/InventoryInStorePickupAdminUi/composer.json index 7a34814f04f8..8a92226501db 100644 --- a/InventoryInStorePickupAdminUi/composer.json +++ b/InventoryInStorePickupAdminUi/composer.json @@ -7,7 +7,8 @@ "magento/module-ui": "*", "magento/module-inventory-in-store-pickup-api": "*", "magento/module-inventory-admin-ui": "*", - "magento/module-sales": "*" + "magento/module-sales": "*", + "magento/module-backend": "*" }, "type": "magento2-module", "license": [ diff --git a/InventoryInStorePickupAdminUi/i18n/en_US.csv b/InventoryInStorePickupAdminUi/i18n/en_US.csv index 7ab098d01742..6305e7962fa0 100644 --- a/InventoryInStorePickupAdminUi/i18n/en_US.csv +++ b/InventoryInStorePickupAdminUi/i18n/en_US.csv @@ -1 +1,4 @@ -"Delivery Methods","Delivery Methods" \ No newline at end of file +"Delivery Methods","Delivery Methods" +"The customer have been notified and shipment created.","The customer have been notified and shipment created." +"We can't notify the customer right now.","We can't notify the customer right now." +"Notify Order is Ready for Pickup","Notify Order is Ready for Pickup" \ No newline at end of file diff --git a/InventoryInStorePickupApi/Api/NotifyOrderIsReadyAndShipInterface.php b/InventoryInStorePickupApi/Api/NotifyOrderIsReadyForPickupInterface.php similarity index 92% rename from InventoryInStorePickupApi/Api/NotifyOrderIsReadyAndShipInterface.php rename to InventoryInStorePickupApi/Api/NotifyOrderIsReadyForPickupInterface.php index d73f8ff238a7..87200204b113 100644 --- a/InventoryInStorePickupApi/Api/NotifyOrderIsReadyAndShipInterface.php +++ b/InventoryInStorePickupApi/Api/NotifyOrderIsReadyForPickupInterface.php @@ -7,7 +7,7 @@ namespace Magento\InventoryInStorePickupApi\Api; -interface NotifyOrderIsReadyAndShipInterface +interface NotifyOrderIsReadyForPickupInterface { /** * @param int $orderId From e21c8f009fd2c03da73c08963593422b7e083e02 Mon Sep 17 00:00:00 2001 From: Maksym Novik <m.novik@ism-ukraine.com> Date: Fri, 5 Apr 2019 16:02:53 +0300 Subject: [PATCH 066/231] Add ready for pickup button on the order page in admin panel which notifies customer that order could be picked up 2034. int -> void --- InventoryInStorePickup/Model/NotifyOrderIsReadyForPickup.php | 4 ++-- .../Api/NotifyOrderIsReadyForPickupInterface.php | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/InventoryInStorePickup/Model/NotifyOrderIsReadyForPickup.php b/InventoryInStorePickup/Model/NotifyOrderIsReadyForPickup.php index 25e91850c36a..a3adef1fcadf 100644 --- a/InventoryInStorePickup/Model/NotifyOrderIsReadyForPickup.php +++ b/InventoryInStorePickup/Model/NotifyOrderIsReadyForPickup.php @@ -56,7 +56,7 @@ public function __construct( /** * {@inheritdoc} */ - public function execute(int $orderId): ?int + public function execute(int $orderId): void { if (!$this->isOrderReadyForPickup->execute($orderId)) { throw new OrderIsNotReadyForPickupException(); @@ -67,6 +67,6 @@ public function execute(int $orderId): ?int /* TODO: add order comment? */ - return (int)$this->shipOrder->execute($orderId); + $this->shipOrder->execute($orderId); } } diff --git a/InventoryInStorePickupApi/Api/NotifyOrderIsReadyForPickupInterface.php b/InventoryInStorePickupApi/Api/NotifyOrderIsReadyForPickupInterface.php index 87200204b113..e0f0cd04d99d 100644 --- a/InventoryInStorePickupApi/Api/NotifyOrderIsReadyForPickupInterface.php +++ b/InventoryInStorePickupApi/Api/NotifyOrderIsReadyForPickupInterface.php @@ -12,11 +12,9 @@ interface NotifyOrderIsReadyForPickupInterface /** * @param int $orderId * - * @return int|null Id of created Shipment - * * @throws \Magento\Framework\Exception\NoSuchEntityException * @throws \Magento\InventoryInStorePickupApi\Exception\OrderIsNotReadyForPickupException * @throws \Magento\Framework\Exception\LocalizedException */ - public function execute(int $orderId): ?int; + public function execute(int $orderId): void; } From d7c2bfb44ac498ec71a322a39d6bf981b00817c8 Mon Sep 17 00:00:00 2001 From: Giacomo Giannotti <giacomo.giannotti@magespecialist.it> Date: Fri, 5 Apr 2019 15:17:02 +0200 Subject: [PATCH 067/231] Fixed issue #2135 --- .../Order/GetListShipmentRepositoryPlugin.php | 54 +++++++++++++++++++ InventoryShipping/etc/di.xml | 3 ++ 2 files changed, 57 insertions(+) create mode 100644 InventoryShipping/Plugin/Sales/Model/Order/GetListShipmentRepositoryPlugin.php diff --git a/InventoryShipping/Plugin/Sales/Model/Order/GetListShipmentRepositoryPlugin.php b/InventoryShipping/Plugin/Sales/Model/Order/GetListShipmentRepositoryPlugin.php new file mode 100644 index 000000000000..e56eea4ff31d --- /dev/null +++ b/InventoryShipping/Plugin/Sales/Model/Order/GetListShipmentRepositoryPlugin.php @@ -0,0 +1,54 @@ +<?php +declare(strict_types=1); + +namespace Magento\InventoryShipping\Plugin\Sales\Model\Order; + +use Magento\InventoryShipping\Model\ResourceModel\ShipmentSource\GetSourceCodeByShipmentId; +use Magento\Sales\Api\Data\ShipmentExtensionFactory; +use Magento\Sales\Model\Order\ShipmentRepository; + +class GetListShipmentRepositoryPlugin +{ + /** + * @var ShipmentExtensionFactory + */ + private $shipmentExtensionFactory; + /** + * @var GetSourceCodeByShipmentId + */ + private $getSourceCodeByShipmentId; + + /** + * GetListShipmentRepositoryPlugin constructor. + * @param ShipmentExtensionFactory $shipmentExtensionFactory + * @param GetSourceCodeByShipmentId $getSourceCodeByShipmentId + */ + public function __construct( + ShipmentExtensionFactory $shipmentExtensionFactory, + GetSourceCodeByShipmentId $getSourceCodeByShipmentId + ) { + $this->shipmentExtensionFactory = $shipmentExtensionFactory; + $this->getSourceCodeByShipmentId = $getSourceCodeByShipmentId; + } + + /** + * @param ShipmentRepository $subject + * @param \Magento\Sales\Api\Data\ShipmentInterface[] $searchResult + * @return \Magento\Sales\Api\Data\ShipmentInterface[] + */ + public function afterGetList(ShipmentRepository $subject, $searchResult) + { + /** @var \Magento\Sales\Api\Data\ShipmentInterface $shipment */ + foreach ($searchResult->getItems() as &$shipment) { + $shipmentExtension = $shipment->getExtensionAttributes(); + if (empty($shipmentExtension)) { + $shipmentExtension = $this->shipmentExtensionFactory->create(); + } + $sourceCode = $this->getSourceCodeByShipmentId->execute((int)$shipment->getId()); + $shipmentExtension->setSourceCode($sourceCode); + $shipment->setExtensionAttributes($shipmentExtension); + } + + return $searchResult; + } +} diff --git a/InventoryShipping/etc/di.xml b/InventoryShipping/etc/di.xml index bf575600c911..67fb8c85c374 100644 --- a/InventoryShipping/etc/di.xml +++ b/InventoryShipping/etc/di.xml @@ -14,6 +14,9 @@ <plugin name="LoadSourceForShipment" type="Magento\InventoryShipping\Plugin\Sales\ResourceModel\Order\Shipment\LoadSourceForShipmentPlugin"/> <plugin name="DeleteSourceForShipment" type="Magento\InventoryShipping\Plugin\Sales\ResourceModel\Order\Shipment\DeleteSourceForShipmentPlugin"/> </type> + <type name="Magento\Sales\Model\Order\ShipmentRepository"> + <plugin name="GetListShipmentRepository" type="Magento\InventoryShipping\Plugin\Sales\Model\Order\GetListShipmentRepositoryPlugin"/> + </type> <type name="Magento\InventoryApi\Model\SourceValidatorChain"> <arguments> <argument name="validators" xsi:type="array"> From 181677b77fda46c7902b0467cae0c32249787688 Mon Sep 17 00:00:00 2001 From: Giacomo Giannotti <giacomo.giannotti@magespecialist.it> Date: Fri, 5 Apr 2019 16:42:13 +0200 Subject: [PATCH 068/231] Fixed issue #2128 --- InventoryDistanceBasedSourceSelection/etc/di.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/InventoryDistanceBasedSourceSelection/etc/di.xml b/InventoryDistanceBasedSourceSelection/etc/di.xml index 810df04e5b7c..d2f74629791a 100644 --- a/InventoryDistanceBasedSourceSelection/etc/di.xml +++ b/InventoryDistanceBasedSourceSelection/etc/di.xml @@ -69,6 +69,7 @@ <type name="Magento\InventoryDistanceBasedSourceSelection\Model\ImportGeoNames"> <arguments> <argument name="geoNamesBaseUrl" xsi:type="string">http://download.geonames.org/export/zip/</argument> + <argument name="client" xsi:type="object">Magento\Framework\HTTP\Client\Curl</argument> </arguments> </type> From b2544026ccd7baaaa6b3f36c7cb077a70b175923 Mon Sep 17 00:00:00 2001 From: Francesco Tobia <francesco.tobia@magespecialist.it> Date: Fri, 5 Apr 2019 17:10:21 +0200 Subject: [PATCH 069/231] fix: remove Unassign column label and add Unassign tooltip to the Trash icons Issue: #2133 In Assigned Sources panel, located on the Product Edit page the following edits were made: 1. Removed Unassign column label 2. Added Unassign tooltip to the Trash icons 3. Resized column width --- InventoryCatalogAdminUi/i18n/en_US.csv | 1 + .../view/adminhtml/ui_component/product_form.xml | 4 ++-- .../web/template/stock/assign-sources/action-delete.html | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/InventoryCatalogAdminUi/i18n/en_US.csv b/InventoryCatalogAdminUi/i18n/en_US.csv index 2bf1b90a9777..3ebf5bd5f933 100644 --- a/InventoryCatalogAdminUi/i18n/en_US.csv +++ b/InventoryCatalogAdminUi/i18n/en_US.csv @@ -48,3 +48,4 @@ Done,Done "Are you sure you want to unassign one or more sources from the selected items?","Are you sure you want to unassign one or more sources from the selected items?" "Transfer Inventory To Source","Transfer Inventory To Source" "Are you sure you want to transfer the inventory of the selected items?","Are you sure you want to transfer the inventory of the selected items?" +"Unassign","Unassign" diff --git a/InventoryCatalogAdminUi/view/adminhtml/ui_component/product_form.xml b/InventoryCatalogAdminUi/view/adminhtml/ui_component/product_form.xml index de9fd596cf63..e1211e08f89f 100644 --- a/InventoryCatalogAdminUi/view/adminhtml/ui_component/product_form.xml +++ b/InventoryCatalogAdminUi/view/adminhtml/ui_component/product_form.xml @@ -161,9 +161,9 @@ </imports> </settings> </field> - <field name="actionDelete" formElement="actionDelete" sortOrder="90"> + <field name="actionDelete" formElement="actionDelete" sortOrder="90" template="Magento_InventoryCatalogAdminUi/stock/assign-sources/action-delete"> <settings> - <label translate="true">Unassign</label> + <label translate="true"></label> </settings> </field> </container> diff --git a/InventoryCatalogAdminUi/view/adminhtml/web/template/stock/assign-sources/action-delete.html b/InventoryCatalogAdminUi/view/adminhtml/web/template/stock/assign-sources/action-delete.html index 5d00bc09e536..507e60a7b527 100644 --- a/InventoryCatalogAdminUi/view/adminhtml/web/template/stock/assign-sources/action-delete.html +++ b/InventoryCatalogAdminUi/view/adminhtml/web/template/stock/assign-sources/action-delete.html @@ -5,7 +5,7 @@ */ --> <button class="action-delete" - attr="{'data-action': 'remove_row', title: $parent.deleteButtonLabel}" + attr="{'data-action': 'remove_row', title: $t('Unassign')}" click="$data.deleteRecord.bind($data, $record().index, $record().recordId)" disable="disabled"> <span translate="$parent.deleteButtonLabel"></span> From 6ebbf0cfa3ca3f9f6314e405d09a77594fa0a553 Mon Sep 17 00:00:00 2001 From: Riccardo Ugolini <riccardo.ugolini@magespecialist.it> Date: Fri, 5 Apr 2019 18:00:48 +0200 Subject: [PATCH 070/231] added reservation cli --- .../Command/ReservationInconsistency.php | 64 +++++++++++++++++++ .../Model/GetReservationsTotOrder.php | 48 ++++++++++++++ .../GetListReservationsTotOrder.php | 49 ++++++++++++++ InventoryReservations/etc/di.xml | 7 ++ 4 files changed, 168 insertions(+) create mode 100644 InventoryReservations/Command/ReservationInconsistency.php create mode 100644 InventoryReservations/Model/GetReservationsTotOrder.php create mode 100644 InventoryReservations/Model/ResourceModel/GetListReservationsTotOrder.php diff --git a/InventoryReservations/Command/ReservationInconsistency.php b/InventoryReservations/Command/ReservationInconsistency.php new file mode 100644 index 000000000000..be5655f9e3e6 --- /dev/null +++ b/InventoryReservations/Command/ReservationInconsistency.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservations\Command; + +use Magento\InventoryReservations\Model\GetReservationsTotOrder\Proxy as GetReservationsTotOrder; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Class ImportCustomersCommand + */ +class ReservationInconsistency extends Command +{ + /** + * @var \Magento\InventoryReservations\Model\GetReservationsTotOrder $getReservationsTotOrder + */ + private $getReservationsTotOrder; + + /** + * ReservationInconsistency constructor. + * @param GetReservationsTotOrder $getReservationsTotOrder + */ + public function __construct( + GetReservationsTotOrder $getReservationsTotOrder + ) { + parent::__construct(); + $this->getReservationsTotOrder = $getReservationsTotOrder; + } + + protected function configure() + { + $this + ->setName('inventory:reservation:show-inconsistency') + ->setDescription('Show all reservation inconsistencies for completed orders'); + + parent::configure(); + } + + /** + * @param InputInterface $input + * @param OutputInterface $output + * @return void + */ + public function execute(InputInterface $input, OutputInterface $output): void + { + /** @var array $orderListReservations */ + $orderListReservations = $this->getReservationsTotOrder->getListReservationsTotOrder(); + + foreach ($orderListReservations as $orderReservationTot){ + $output->writeln( + __('Order %1 got inconsistency on reservation by %2', + $orderReservationTot['IncrementId'], + $orderReservationTot['ReservationTot'] + ) + ); + } + } +} diff --git a/InventoryReservations/Model/GetReservationsTotOrder.php b/InventoryReservations/Model/GetReservationsTotOrder.php new file mode 100644 index 000000000000..ab2f06b4ea5f --- /dev/null +++ b/InventoryReservations/Model/GetReservationsTotOrder.php @@ -0,0 +1,48 @@ +<?php +/** + * MageSpecialist + * + * NOTICE OF LICENSE + * + * This source file is subject to the Open Software License (OSL 3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://opensource.org/licenses/osl-3.0.php + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to info@magespecialist.it so we can send you a copy immediately. + * + * @copyright Copyright (c) 2019 Skeeller srl (http://www.magespecialist.it) + * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) + */ +declare(strict_types=1); + +namespace Magento\InventoryReservations\Model; + +use Magento\InventoryReservations\Model\ResourceModel\GetListReservationsTotOrder; + +class GetReservationsTotOrder +{ + /** + * @var GetListReservationsTotOrder + */ + private $getListReservationsTotOrder; + + /** + * GetReservationsTotOrder constructor. + * @param GetListReservationsTotOrder $getListReservationsTotOrder + */ + public function __construct ( + GetListReservationsTotOrder $getListReservationsTotOrder + ) { + $this->getListReservationsTotOrder = $getListReservationsTotOrder; + } + + /** + * @return array + */ + public function getListReservationsTotOrder(): array + { + return $this->getListReservationsTotOrder->execute(); + } +} diff --git a/InventoryReservations/Model/ResourceModel/GetListReservationsTotOrder.php b/InventoryReservations/Model/ResourceModel/GetListReservationsTotOrder.php new file mode 100644 index 000000000000..2c1306ed6919 --- /dev/null +++ b/InventoryReservations/Model/ResourceModel/GetListReservationsTotOrder.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservations\Model\ResourceModel; + + +use Magento\Framework\App\ResourceConnection; +use Magento\Sales\Model\Order; + +class GetListReservationsTotOrder +{ + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * GetListReservationsTotOrder constructor. + * @param ResourceConnection $resourceConnection + */ + public function __construct ( + ResourceConnection $resourceConnection + ) { + $this->resourceConnection = $resourceConnection; + } + + public function execute(): array + { + $tableName = $this->resourceConnection->getTableName('inventory_reservation'); + $tableSalesOrderName = $this->resourceConnection->getTableName('sales_order'); + $connection = $this->resourceConnection->getConnection(); + + //todo: modularity rework + $qry = $connection + ->select() + ->from($tableName, ['ReservationTot' => 'sum(quantity)']) + ->joinInner($tableSalesOrderName, + 'entity_id = ' . new \Zend_Db_Expr("CAST(JSON_EXTRACT(metadata, '$.object_id') as UNSIGNED) + AND state IN ('complete', 'closed', 'canceled')"), ['IncrementId' => 'increment_id'] + ) + ->group('IncrementId') + ->having('ReservationTot != ?', 0); + return $connection->fetchAll($qry); + } +} diff --git a/InventoryReservations/etc/di.xml b/InventoryReservations/etc/di.xml index ad75adea7ece..2f3479bb7c31 100644 --- a/InventoryReservations/etc/di.xml +++ b/InventoryReservations/etc/di.xml @@ -16,4 +16,11 @@ <argument name="groupConcatMaxLen" xsi:type="number">2000</argument> </arguments> </type> + <type name="Magento\Framework\Console\CommandList"> + <arguments> + <argument name="commands" xsi:type="array"> + <item name="inventory_reservation_inconsistency" xsi:type="object">Magento\InventoryReservations\Command\ReservationInconsistency</item> + </argument> + </arguments> + </type> </config> From a127d4d89f10b562691ac1f321d50abc71aaef5a Mon Sep 17 00:00:00 2001 From: Bettina Cerban <bettinacerban@gmail.com> Date: Fri, 5 Apr 2019 15:43:14 -0300 Subject: [PATCH 071/231] add missing extensionAttributes accessor methods --- .../Model/PartialInventoryTransfer.php | 17 +++++++++++++++++ .../Data/PartialInventoryTransferInterface.php | 10 ++++++++++ 2 files changed, 27 insertions(+) diff --git a/InventoryCatalog/Model/PartialInventoryTransfer.php b/InventoryCatalog/Model/PartialInventoryTransfer.php index 0918062410bd..67b39c00375d 100644 --- a/InventoryCatalog/Model/PartialInventoryTransfer.php +++ b/InventoryCatalog/Model/PartialInventoryTransfer.php @@ -8,6 +8,7 @@ namespace Magento\InventoryCatalog\Model; use Magento\Framework\Api\AbstractSimpleObject; +use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferExtensionInterface; use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface; class PartialInventoryTransfer extends AbstractSimpleObject implements PartialInventoryTransferInterface @@ -76,4 +77,20 @@ public function setDestinationSourceCode(string $code): void { $this->setData(self::DESTINATION_SOURCE_CODE, $code); } + + /** + * @return \Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferExtensionInterface + */ + public function getExtensionAttributes(): \Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferExtensionInterface + { + return $this->_get(self::EXTENSION_ATTRIBUTES_KEY); + } + + /** + * @param PartialInventoryTransferExtensionInterface $extensionAttributes + */ + public function setExtensionAttributes(\Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferExtensionInterface $extensionAttributes): void + { + $this->setData(self::EXTENSION_ATTRIBUTES_KEY, $extensionAttributes); + } } \ No newline at end of file diff --git a/InventoryCatalogApi/Api/Data/PartialInventoryTransferInterface.php b/InventoryCatalogApi/Api/Data/PartialInventoryTransferInterface.php index e78950e17203..e2a374c06029 100644 --- a/InventoryCatalogApi/Api/Data/PartialInventoryTransferInterface.php +++ b/InventoryCatalogApi/Api/Data/PartialInventoryTransferInterface.php @@ -60,4 +60,14 @@ public function getDestinationSourceCode(): string; * @param string $code */ public function setDestinationSourceCode(string $code): void; + + /** + * @return \Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferExtensionInterface + */ + public function getExtensionAttributes(): \Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferExtensionInterface; + + /** + * @param PartialInventoryTransferExtensionInterface $extensionAttributes + */ + public function setExtensionAttributes(\Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferExtensionInterface $extensionAttributes): void; } \ No newline at end of file From 1fbe9191249aacf22df02958b5180f6630e0f430 Mon Sep 17 00:00:00 2001 From: Bettina Cerban <bettinacerban@gmail.com> Date: Fri, 5 Apr 2019 15:44:24 -0300 Subject: [PATCH 072/231] add missing parameter for error message --- .../Model/Source/Validator/PartialTransferValidator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InventoryCatalog/Model/Source/Validator/PartialTransferValidator.php b/InventoryCatalog/Model/Source/Validator/PartialTransferValidator.php index 8a949802e906..9022a5fee399 100644 --- a/InventoryCatalog/Model/Source/Validator/PartialTransferValidator.php +++ b/InventoryCatalog/Model/Source/Validator/PartialTransferValidator.php @@ -76,7 +76,7 @@ public function validate(PartialInventoryTransferInterface $item): ValidationRes try { $originSourceItem = $this->getSourceItemBySkuAndSource($item->getSku(), $item->getOriginSourceCode()); if ($originSourceItem->getQuantity() < $item->getQty()) { - $errors[] = __('Requested transfer amount for sku %sku is not available'); + $errors[] = __('Requested transfer amount for sku %sku is not available', $item->getSku()); } $this->getSourceItemBySkuAndSource($item->getSku(), $item->getOriginSourceCode()); From 1637b241276097ce4ae1cdc47a7fb44110d78c58 Mon Sep 17 00:00:00 2001 From: Bettina Cerban <bettinacerban@gmail.com> Date: Fri, 5 Apr 2019 16:05:05 -0300 Subject: [PATCH 073/231] specify full class path for interface --- .../Api/BulkPartialInventoryTransferInterface.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/InventoryCatalogApi/Api/BulkPartialInventoryTransferInterface.php b/InventoryCatalogApi/Api/BulkPartialInventoryTransferInterface.php index 73046ee6b39c..d7d5e2ce9c17 100644 --- a/InventoryCatalogApi/Api/BulkPartialInventoryTransferInterface.php +++ b/InventoryCatalogApi/Api/BulkPartialInventoryTransferInterface.php @@ -7,16 +7,13 @@ namespace Magento\InventoryCatalogApi\Api; -use Magento\InventoryApi\Api\Data\SourceItemInterface; -use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface; - interface BulkPartialInventoryTransferInterface { /** * Run bulk partial inventory transfer for specified items. * - * @param PartialInventoryTransferInterface[] $items - * @return SourceItemInterface[] + * @param \Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface[] $items + * @return \Magento\InventoryApi\Api\Data\SourceItemInterface[] */ public function execute(array $items): array; } \ No newline at end of file From 20c1e01e407beeab3e11f3a2926742e3f3b7b1de Mon Sep 17 00:00:00 2001 From: Giacomo Giannotti <giacomo.giannotti@magespecialist.it> Date: Sat, 6 Apr 2019 10:39:37 +0200 Subject: [PATCH 074/231] Moved the ClientInterface preference to app/etc/di.xml --- InventoryDistanceBasedSourceSelection/etc/di.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/InventoryDistanceBasedSourceSelection/etc/di.xml b/InventoryDistanceBasedSourceSelection/etc/di.xml index d2f74629791a..810df04e5b7c 100644 --- a/InventoryDistanceBasedSourceSelection/etc/di.xml +++ b/InventoryDistanceBasedSourceSelection/etc/di.xml @@ -69,7 +69,6 @@ <type name="Magento\InventoryDistanceBasedSourceSelection\Model\ImportGeoNames"> <arguments> <argument name="geoNamesBaseUrl" xsi:type="string">http://download.geonames.org/export/zip/</argument> - <argument name="client" xsi:type="object">Magento\Framework\HTTP\Client\Curl</argument> </arguments> </type> From 4aebcc373116a5d7342aceef2372f03f00a310c9 Mon Sep 17 00:00:00 2001 From: Riccardo Ugolini <riccardo.ugolini@magespecialist.it> Date: Sat, 6 Apr 2019 11:41:29 +0200 Subject: [PATCH 075/231] added integration test --- .../Model/GetReservationsTotOrder.php | 16 +-- .../GetListReservationsTotOrder.php | 10 +- .../GetListReservationsTotOrdersTest.php | 111 ++++++++++++++++++ .../_fixtures/broken_reservation.php | 26 ++++ 4 files changed, 146 insertions(+), 17 deletions(-) create mode 100644 InventoryReservations/Test/Integration/Model/GetListReservationsTotOrdersTest.php create mode 100644 InventoryReservations/Test/Integration/_fixtures/broken_reservation.php diff --git a/InventoryReservations/Model/GetReservationsTotOrder.php b/InventoryReservations/Model/GetReservationsTotOrder.php index ab2f06b4ea5f..6af7d4ed184d 100644 --- a/InventoryReservations/Model/GetReservationsTotOrder.php +++ b/InventoryReservations/Model/GetReservationsTotOrder.php @@ -1,19 +1,7 @@ <?php /** - * MageSpecialist - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.txt. - * It is also available through the world-wide-web at this URL: - * http://opensource.org/licenses/osl-3.0.php - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to info@magespecialist.it so we can send you a copy immediately. - * - * @copyright Copyright (c) 2019 Skeeller srl (http://www.magespecialist.it) - * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. */ declare(strict_types=1); diff --git a/InventoryReservations/Model/ResourceModel/GetListReservationsTotOrder.php b/InventoryReservations/Model/ResourceModel/GetListReservationsTotOrder.php index 2c1306ed6919..3ca00352a886 100644 --- a/InventoryReservations/Model/ResourceModel/GetListReservationsTotOrder.php +++ b/InventoryReservations/Model/ResourceModel/GetListReservationsTotOrder.php @@ -7,7 +7,6 @@ namespace Magento\InventoryReservations\Model\ResourceModel; - use Magento\Framework\App\ResourceConnection; use Magento\Sales\Model\Order; @@ -30,9 +29,13 @@ public function __construct ( public function execute(): array { + $connection = $this->resourceConnection->getConnection(); $tableName = $this->resourceConnection->getTableName('inventory_reservation'); $tableSalesOrderName = $this->resourceConnection->getTableName('sales_order'); - $connection = $this->resourceConnection->getConnection(); + + $complete = Order::STATE_COMPLETE; + $closed = Order::STATE_CLOSED; + $canceled = Order::STATE_CANCELED; //todo: modularity rework $qry = $connection @@ -40,7 +43,8 @@ public function execute(): array ->from($tableName, ['ReservationTot' => 'sum(quantity)']) ->joinInner($tableSalesOrderName, 'entity_id = ' . new \Zend_Db_Expr("CAST(JSON_EXTRACT(metadata, '$.object_id') as UNSIGNED) - AND state IN ('complete', 'closed', 'canceled')"), ['IncrementId' => 'increment_id'] + AND " . $connection->quoteInto('state IN (?)', [$complete, $closed, $canceled])), + ['IncrementId' => 'increment_id'] ) ->group('IncrementId') ->having('ReservationTot != ?', 0); diff --git a/InventoryReservations/Test/Integration/Model/GetListReservationsTotOrdersTest.php b/InventoryReservations/Test/Integration/Model/GetListReservationsTotOrdersTest.php new file mode 100644 index 000000000000..cfb39429be7d --- /dev/null +++ b/InventoryReservations/Test/Integration/Model/GetListReservationsTotOrdersTest.php @@ -0,0 +1,111 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservations\Test\Integration\Model; + +use Magento\Catalog\Model\Product; +use Magento\InventoryReservations\Model\ResourceModel\GetListReservationsTotOrder; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\ResourceModel\Order\Collection as OrderCollection; +use Magento\Catalog\Model\ResourceModel\Product\Collection as ProductCollection; +use PHPUnit\Framework\TestCase; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\Registry; + +class GetListReservationsTotOrdersTest extends TestCase +{ + + /** + * @magentoDataFixture Magento/Sales/_files/order_new.php + */ + public function testShouldReturnEmptyArray(): void + { + $objectManager = Bootstrap::getObjectManager(); + + /** @var GetListReservationsTotOrder $subject */ + $subject = $objectManager->get(GetListReservationsTotOrder::class); + + /** @var OrderRepositoryInterface $orderRepository */ + $orderRepository = $objectManager->create(OrderRepositoryInterface::class); + + /** @var OrderCollection $orderCollection */ + $orderCollection = $objectManager->create(OrderCollection::class); + $orderCollection->addFieldToFilter('increment_id', '100000001'); + + /** @var Order $order */ + $order = $orderCollection->getFirstItem(); + + $order->setStatus(Order::STATE_COMPLETE); + $order->setState(Order::STATE_COMPLETE); + $orderRepository->save($order); + + /** @var array $result */ + $result = $subject->execute(); + + $this->assertSame([], $result); + } + + /** + * @magentoDataFixture Magento/Sales/_files/order_new.php + * @magentoDataFixture Magento/Sales/_files/order_shipping.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryReservations/Test/Integration/_fixtures/broken_reservation.php + * @magentoDbIsolation enabled + */ + public function testShouldReturnOneReservationInconsistency(): void + { + $objectManager = Bootstrap::getObjectManager(); + + /** @var GetListReservationsTotOrder $subject */ + $subject = $objectManager->get(GetListReservationsTotOrder::class); + + /** @var OrderRepositoryInterface $orderRepository */ + $orderRepository = $objectManager->create(OrderRepositoryInterface::class); + + /** @var OrderCollection $orderCollection */ + $orderCollection = $objectManager->create(OrderCollection::class); + $orderCollection->addFieldToFilter('increment_id', '100000001'); + + /** @var Order $order */ + $order = $orderCollection->getFirstItem(); + + $order->setStatus(Order::STATE_COMPLETE); + $order->setState(Order::STATE_COMPLETE); + $orderRepository->save($order); + + /** @var array $result */ + $result = $subject->execute(); + + self::assertCount(1, $result); + } + + + + /** + * @throws \Exception + */ + public function tearDown() + { + $objectManager = Bootstrap::getObjectManager(); + $objectManager->get(Registry::class)->register('isSecureArea', true); + + /** @var OrderCollection $orderCollection */ + $orderCollection = $objectManager->create(OrderCollection::class); + /** @var Order $order */ + foreach ($orderCollection->getItems() as $order) { + $order->getResource()->delete($order); + } + + /** @var ProductCollection $productCollection */ + $productCollection = $objectManager->create(ProductCollection::class); + + /** @var Product $product */ + foreach ($productCollection->getItems() as $product) { + $product->getResource()->delete($product); + } + } +} diff --git a/InventoryReservations/Test/Integration/_fixtures/broken_reservation.php b/InventoryReservations/Test/Integration/_fixtures/broken_reservation.php new file mode 100644 index 000000000000..87a165d2e8c5 --- /dev/null +++ b/InventoryReservations/Test/Integration/_fixtures/broken_reservation.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\TestFramework\Helper\Bootstrap; + +/** @var \Magento\Framework\ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +/** @var Magento\Framework\App\ResourceConnection $resourceConnection */ +$resourceConnection = $objectManager->create(Magento\Framework\App\ResourceConnection::class); + +$connection = $resourceConnection->getConnection(); +$tableName = $resourceConnection->getTableName('inventory_reservation'); + +$payload = [ + 'stock_id' => 1, + 'sku' => 'simple', + 'quantity' => -5, + 'metadata' => '{"event_type":"shipment_created","object_type":"order","object_id":"1"}' +]; + +$qry = $connection->insert($tableName, $payload); \ No newline at end of file From f150910d8e2c65da2cb5727f05e5b370fc9c5f4f Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Sat, 6 Apr 2019 11:59:23 +0200 Subject: [PATCH 076/231] FIX wrong dependency + FIX wring interface name --- .../Plugin/SetZeroQuantityToLegacyAtBulkUnassign.php | 6 +++--- InventoryLegacySynchronization/composer.json | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/InventoryLegacySynchronization/Plugin/SetZeroQuantityToLegacyAtBulkUnassign.php b/InventoryLegacySynchronization/Plugin/SetZeroQuantityToLegacyAtBulkUnassign.php index b744f0faec4c..06da8078ae21 100644 --- a/InventoryLegacySynchronization/Plugin/SetZeroQuantityToLegacyAtBulkUnassign.php +++ b/InventoryLegacySynchronization/Plugin/SetZeroQuantityToLegacyAtBulkUnassign.php @@ -9,7 +9,7 @@ namespace Magento\InventoryLegacySynchronization\Plugin; use Magento\InventoryApi\Api\Data\SourceItemInterface; -use Magento\InventoryCatalogApi\Api\BulkInventoryTransferInterface; +use Magento\InventoryCatalogApi\Api\BulkSourceUnassignInterface; use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; use Magento\InventoryLegacySynchronization\Model\Synchronize; @@ -42,7 +42,7 @@ public function __construct( } /** - * @param BulkInventoryTransferInterface $subject + * @param BulkSourceUnassignInterface $subject * @param int $result * @param array $skus * @param array $sourceCodes @@ -51,7 +51,7 @@ public function __construct( * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function afterExecute( - BulkInventoryTransferInterface $subject, + BulkSourceUnassignInterface $subject, int $result, array $skus, array $sourceCodes diff --git a/InventoryLegacySynchronization/composer.json b/InventoryLegacySynchronization/composer.json index 0606a9b4f6e0..6a30db2190b2 100644 --- a/InventoryLegacySynchronization/composer.json +++ b/InventoryLegacySynchronization/composer.json @@ -4,6 +4,7 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", + "magento/module-inventory-api": "*", "magento/module-catalog-inventory": "*", "magento/module-inventory-catalog": "*", "magento/module-inventory-catalog-api": "*", From 8328f417a362d62f548f447f5918202f832061e1 Mon Sep 17 00:00:00 2001 From: Francesco Tobia <francesco.tobia@magespecialist.it> Date: Sat, 6 Apr 2019 12:00:27 +0200 Subject: [PATCH 077/231] fix: change Trash icons tooltip Issue: #2133 Changed the tooltip description --- .../view/adminhtml/ui_component/product_form.xml | 1 + .../web/template/stock/assign-sources/action-delete.html | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/InventoryCatalogAdminUi/view/adminhtml/ui_component/product_form.xml b/InventoryCatalogAdminUi/view/adminhtml/ui_component/product_form.xml index e1211e08f89f..2298c75a9b94 100644 --- a/InventoryCatalogAdminUi/view/adminhtml/ui_component/product_form.xml +++ b/InventoryCatalogAdminUi/view/adminhtml/ui_component/product_form.xml @@ -65,6 +65,7 @@ <dynamicRows name="assigned_sources" component="Magento_Ui/js/dynamic-rows/dynamic-rows-grid" sortOrder="20"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> + <item name="deleteButtonLabel" xsi:type="string">Unassign</item> <item name="dataProvider" xsi:type="string">data.sources.assign_sources_grid</item> <item name="map" xsi:type="array"> <item name="source_code" xsi:type="string">source_code</item> diff --git a/InventoryCatalogAdminUi/view/adminhtml/web/template/stock/assign-sources/action-delete.html b/InventoryCatalogAdminUi/view/adminhtml/web/template/stock/assign-sources/action-delete.html index 507e60a7b527..aec24deb6a2d 100644 --- a/InventoryCatalogAdminUi/view/adminhtml/web/template/stock/assign-sources/action-delete.html +++ b/InventoryCatalogAdminUi/view/adminhtml/web/template/stock/assign-sources/action-delete.html @@ -5,7 +5,7 @@ */ --> <button class="action-delete" - attr="{'data-action': 'remove_row', title: $t('Unassign')}" + attr="{'data-action': 'remove_row', title: $t($parent.deleteButtonLabel)}" click="$data.deleteRecord.bind($data, $record().index, $record().recordId)" disable="disabled"> <span translate="$parent.deleteButtonLabel"></span> From f014685b71e6d4501d37373d21c6ba3acd725bc2 Mon Sep 17 00:00:00 2001 From: Maksym Novik <m.novik@ism-ukraine.com> Date: Sat, 6 Apr 2019 13:10:42 +0300 Subject: [PATCH 078/231] Add ready for pickup button on the order page in admin panel which notifies customer that order could be picked up 2034. Simplified Fully Qualified names --- .../Model/IsOrderReadyForPickup.php | 10 ++--- .../Model/NotifyOrderIsReadyForPickup.php | 23 ++++++----- .../Model/Order/CanBeFulfilled.php | 16 ++++---- .../Container/ReadyForPickupIdentity.php | 3 +- .../Order/Email/ReadyForPickupNotifier.php | 7 ++-- .../Order/Email/ReadyForPickupSender.php | 39 +++++++++++-------- .../GetPickupLocationCodeByOrderId.php | 4 +- .../SaveOrderPickupLocation.php | 4 +- .../LoadInStorePickupOnGetListPlugin.php | 4 +- .../SavePickupLocationForOrderPlugin.php | 8 ++-- .../InventorySourceExtensionTest.php | 3 +- .../Adminhtml/Order/View/ReadyForPickup.php | 22 ++++++----- .../Adminhtml/Order/NotifyPickup.php | 36 +++++++++-------- .../Model/IsDisplayReadyForPickupButton.php | 6 ++- .../GetNearbySourcesByPostcodeInterface.php | 7 +++- .../NotifyOrderIsReadyForPickupInterface.php | 10 +++-- .../OrderIsNotReadyForPickupException.php | 7 ++-- .../Model/GetNearbySourcesByPostcode.php | 3 +- 18 files changed, 122 insertions(+), 90 deletions(-) diff --git a/InventoryInStorePickup/Model/IsOrderReadyForPickup.php b/InventoryInStorePickup/Model/IsOrderReadyForPickup.php index 941c13561ca5..e6ddf7b2eab6 100644 --- a/InventoryInStorePickup/Model/IsOrderReadyForPickup.php +++ b/InventoryInStorePickup/Model/IsOrderReadyForPickup.php @@ -16,18 +16,18 @@ class IsOrderReadyForPickup implements IsOrderReadyForPickupInterface { /** - * @var \Magento\InventoryInStorePickup\Model\Order\CanBeFulfilled + * @var CanBeFulfilled */ private $canBeFulfilled; /** - * @var \Magento\Sales\Api\OrderRepositoryInterface + * @var OrderRepositoryInterface */ private $orderRepository; /** - * @param \Magento\InventoryInStorePickup\Model\Order\CanBeFulfilled $canBeFulfilled - * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository + * @param CanBeFulfilled $canBeFulfilled + * @param OrderRepositoryInterface $orderRepository */ public function __construct( CanBeFulfilled $canBeFulfilled, @@ -50,7 +50,7 @@ public function execute(int $orderId): bool } /** - * @param \Magento\Sales\Api\Data\OrderInterface $order + * @param OrderInterface $order * * @return bool */ diff --git a/InventoryInStorePickup/Model/NotifyOrderIsReadyForPickup.php b/InventoryInStorePickup/Model/NotifyOrderIsReadyForPickup.php index a3adef1fcadf..8b14a311110b 100644 --- a/InventoryInStorePickup/Model/NotifyOrderIsReadyForPickup.php +++ b/InventoryInStorePickup/Model/NotifyOrderIsReadyForPickup.php @@ -7,45 +7,48 @@ namespace Magento\InventoryInStorePickup\Model; +use Magento\InventoryInStorePickup\Model\Order\Email\ReadyForPickupNotifier; use Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface; use Magento\InventoryInStorePickupApi\Api\NotifyOrderIsReadyForPickupInterface; use Magento\InventoryInStorePickupApi\Exception\OrderIsNotReadyForPickupException; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Api\ShipOrderInterface; class NotifyOrderIsReadyForPickup implements NotifyOrderIsReadyForPickupInterface { /** - * @var \Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface + * @var IsOrderReadyForPickupInterface */ private $isOrderReadyForPickup; /** - * @var \Magento\Sales\Api\ShipOrderInterface + * @var ShipOrderInterface */ private $shipOrder; /** - * @var \Magento\InventoryInStorePickup\Model\Order\Email\ReadyForPickupNotifier + * @var ReadyForPickupNotifier */ private $emailNotifier; /** - * @var \Magento\Sales\Api\OrderRepositoryInterface + * @var OrderRepositoryInterface */ private $orderRepository; /** * NotifyOrderIsReadyAndShip constructor. * - * @param \Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface $isOrderReadyForPickup - * @param \Magento\Sales\Api\ShipOrderInterface $shipOrder - * @param \Magento\InventoryInStorePickup\Model\Order\Email\ReadyForPickupNotifier $emailNotifier - * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository + * @param IsOrderReadyForPickupInterface $isOrderReadyForPickup + * @param ShipOrderInterface $shipOrder + * @param ReadyForPickupNotifier $emailNotifier + * @param OrderRepositoryInterface $orderRepository */ public function __construct( IsOrderReadyForPickupInterface $isOrderReadyForPickup, - \Magento\Sales\Api\ShipOrderInterface $shipOrder, + ShipOrderInterface $shipOrder, Order\Email\ReadyForPickupNotifier $emailNotifier, - \Magento\Sales\Api\OrderRepositoryInterface $orderRepository + OrderRepositoryInterface $orderRepository ) { $this->isOrderReadyForPickup = $isOrderReadyForPickup; $this->shipOrder = $shipOrder; diff --git a/InventoryInStorePickup/Model/Order/CanBeFulfilled.php b/InventoryInStorePickup/Model/Order/CanBeFulfilled.php index 96aa289626c2..61b559cc6d4c 100644 --- a/InventoryInStorePickup/Model/Order/CanBeFulfilled.php +++ b/InventoryInStorePickup/Model/Order/CanBeFulfilled.php @@ -7,35 +7,37 @@ namespace Magento\InventoryInStorePickup\Model\Order; +use Magento\Framework\Api\SearchCriteriaBuilderFactory; use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryApi\Api\SourceItemRepositoryInterface; use Magento\Sales\Api\Data\OrderInterface; class CanBeFulfilled { /** - * @var \Magento\InventoryApi\Api\SourceItemRepositoryInterface + * @var SourceItemRepositoryInterface */ private $sourceItemRepository; /** - * @var \Magento\Framework\Api\SearchCriteriaBuilderFactory + * @var SearchCriteriaBuilderFactory */ private $searchCriteriaBuilderFactory; /** - * @param \Magento\InventoryApi\Api\SourceItemRepositoryInterface $sourceItemRepository - * @param \Magento\Framework\Api\SearchCriteriaBuilderFactory $searchCriteriaBuilder + * @param SourceItemRepositoryInterface $sourceItemRepository + * @param SearchCriteriaBuilderFactory $searchCriteriaBuilder */ public function __construct( - \Magento\InventoryApi\Api\SourceItemRepositoryInterface $sourceItemRepository, - \Magento\Framework\Api\SearchCriteriaBuilderFactory $searchCriteriaBuilder + SourceItemRepositoryInterface $sourceItemRepository, + SearchCriteriaBuilderFactory $searchCriteriaBuilder ) { $this->sourceItemRepository = $sourceItemRepository; $this->searchCriteriaBuilderFactory = $searchCriteriaBuilder; } /** - * @param \Magento\Sales\Api\Data\OrderInterface $order + * @param OrderInterface $order * * @return bool */ diff --git a/InventoryInStorePickup/Model/Order/Email/Container/ReadyForPickupIdentity.php b/InventoryInStorePickup/Model/Order/Email/Container/ReadyForPickupIdentity.php index 18fdbc86e9ee..5f3cba2aec0b 100644 --- a/InventoryInStorePickup/Model/Order/Email/Container/ReadyForPickupIdentity.php +++ b/InventoryInStorePickup/Model/Order/Email/Container/ReadyForPickupIdentity.php @@ -9,6 +9,7 @@ use Magento\Sales\Model\Order\Email\Container\Container; use Magento\Sales\Model\Order\Email\Container\IdentityInterface; +use Magento\Store\Model\ScopeInterface; class ReadyForPickupIdentity extends Container implements IdentityInterface { @@ -29,7 +30,7 @@ public function isEnabled() { return $this->scopeConfig->isSetFlag( self::XML_PATH_EMAIL_ENABLED, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + ScopeInterface::SCOPE_STORE, $this->getStore()->getStoreId() ); } diff --git a/InventoryInStorePickup/Model/Order/Email/ReadyForPickupNotifier.php b/InventoryInStorePickup/Model/Order/Email/ReadyForPickupNotifier.php index f3af8fdacb69..608c2770da1b 100644 --- a/InventoryInStorePickup/Model/Order/Email/ReadyForPickupNotifier.php +++ b/InventoryInStorePickup/Model/Order/Email/ReadyForPickupNotifier.php @@ -7,6 +7,7 @@ namespace Magento\InventoryInStorePickup\Model\Order\Email; +use Magento\Sales\Model\AbstractNotifier; use Magento\Sales\Model\Order\Email\Sender\OrderSender; use Magento\Sales\Model\ResourceModel\Order\Status\History\CollectionFactory; use Psr\Log\LoggerInterface as Logger; @@ -17,7 +18,7 @@ * @package Magento\InventoryInStorePickup\Model\Order\Email * TODO: probaly remove this class */ -class ReadyForPickupNotifier extends \Magento\Sales\Model\AbstractNotifier +class ReadyForPickupNotifier extends AbstractNotifier { /** * @var CollectionFactory @@ -25,7 +26,7 @@ class ReadyForPickupNotifier extends \Magento\Sales\Model\AbstractNotifier protected $historyCollectionFactory; /** - * @var \Psr\Log\LoggerInterface + * @var Logger */ protected $logger; @@ -37,7 +38,7 @@ class ReadyForPickupNotifier extends \Magento\Sales\Model\AbstractNotifier /** * @param CollectionFactory $historyCollectionFactory * @param Logger $logger - * @param \Magento\InventoryInStorePickup\Model\Order\Email\ReadyForPickupSender $sender + * @param ReadyForPickupSender $sender */ public function __construct( CollectionFactory $historyCollectionFactory, diff --git a/InventoryInStorePickup/Model/Order/Email/ReadyForPickupSender.php b/InventoryInStorePickup/Model/Order/Email/ReadyForPickupSender.php index 8e94b0bc58fe..a72e7924c17c 100644 --- a/InventoryInStorePickup/Model/Order/Email/ReadyForPickupSender.php +++ b/InventoryInStorePickup/Model/Order/Email/ReadyForPickupSender.php @@ -8,7 +8,14 @@ namespace Magento\InventoryInStorePickup\Model\Order\Email; use Magento\Framework\DataObject; +use Magento\Framework\Event\ManagerInterface; use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Address\Renderer; +use Magento\Sales\Model\Order\Email\Container\IdentityInterface; +use Magento\Sales\Model\Order\Email\Container\Template; +use Magento\Sales\Model\Order\Email\Sender; +use Magento\Sales\Model\Order\Email\SenderBuilderFactory; +use Psr\Log\LoggerInterface; /** * Class ReadyForPickupSender @@ -17,30 +24,30 @@ * TODO: refactor * TODO: Implement asynchronous email sending */ -class ReadyForPickupSender extends \Magento\Sales\Model\Order\Email\Sender +class ReadyForPickupSender extends Sender { /** - * @var \Magento\Framework\Event\ManagerInterface + * @var ManagerInterface */ private $eventManager; /** * ReadyForPickupSender constructor. * - * @param \Magento\Sales\Model\Order\Email\Container\Template $templateContainer - * @param \Magento\Sales\Model\Order\Email\Container\IdentityInterface $identityContainer - * @param \Magento\Sales\Model\Order\Email\SenderBuilderFactory $senderBuilderFactory - * @param \Psr\Log\LoggerInterface $logger - * @param \Magento\Sales\Model\Order\Address\Renderer $addressRenderer - * @param \Magento\Framework\Event\ManagerInterface $eventManager + * @param Template $templateContainer + * @param IdentityInterface $identityContainer + * @param SenderBuilderFactory $senderBuilderFactory + * @param LoggerInterface $logger + * @param Renderer $addressRenderer + * @param ManagerInterface $eventManager */ public function __construct( - \Magento\Sales\Model\Order\Email\Container\Template $templateContainer, - \Magento\Sales\Model\Order\Email\Container\IdentityInterface $identityContainer, - \Magento\Sales\Model\Order\Email\SenderBuilderFactory $senderBuilderFactory, - \Psr\Log\LoggerInterface $logger, - \Magento\Sales\Model\Order\Address\Renderer $addressRenderer, - \Magento\Framework\Event\ManagerInterface $eventManager + Template $templateContainer, + IdentityInterface $identityContainer, + SenderBuilderFactory $senderBuilderFactory, + LoggerInterface $logger, + Renderer $addressRenderer, + ManagerInterface $eventManager ) { parent::__construct($templateContainer, $identityContainer, $senderBuilderFactory, $logger, $addressRenderer); @@ -48,7 +55,7 @@ public function __construct( } /** - * @param \Magento\Sales\Model\Order $order + * @param Order $order * * @return bool */ @@ -60,7 +67,7 @@ public function send(Order $order): bool /** * Prepare email template with variables * - * @param \Magento\Sales\Model\Order $order + * @param Order $order * * @return void */ diff --git a/InventoryInStorePickup/Model/ResourceModel/OrderPickupLocation/GetPickupLocationCodeByOrderId.php b/InventoryInStorePickup/Model/ResourceModel/OrderPickupLocation/GetPickupLocationCodeByOrderId.php index eb286b3c833b..cfd193a6901d 100644 --- a/InventoryInStorePickup/Model/ResourceModel/OrderPickupLocation/GetPickupLocationCodeByOrderId.php +++ b/InventoryInStorePickup/Model/ResourceModel/OrderPickupLocation/GetPickupLocationCodeByOrderId.php @@ -19,12 +19,12 @@ class GetPickupLocationCodeByOrderId private const PICKUP_LOCATION_CODE = 'pickup_location_code'; /** - * @var \Magento\Framework\App\ResourceConnection + * @var ResourceConnection */ private $connection; /** - * @param \Magento\Framework\App\ResourceConnection $connection + * @param ResourceConnection $connection */ public function __construct( ResourceConnection $connection diff --git a/InventoryInStorePickup/Model/ResourceModel/OrderPickupLocation/SaveOrderPickupLocation.php b/InventoryInStorePickup/Model/ResourceModel/OrderPickupLocation/SaveOrderPickupLocation.php index 47ec145f2200..c881dd964df2 100644 --- a/InventoryInStorePickup/Model/ResourceModel/OrderPickupLocation/SaveOrderPickupLocation.php +++ b/InventoryInStorePickup/Model/ResourceModel/OrderPickupLocation/SaveOrderPickupLocation.php @@ -18,12 +18,12 @@ class SaveOrderPickupLocation private const PICKUP_LOCATION_CODE = 'pickup_location_code'; /** - * @var \Magento\Framework\App\ResourceConnection + * @var ResourceConnection */ private $connection; /** - * @param \Magento\Framework\App\ResourceConnection $connection + * @param ResourceConnection $connection */ public function __construct( ResourceConnection $connection diff --git a/InventoryInStorePickup/Plugin/InventoryApi/SourceRepository/LoadInStorePickupOnGetListPlugin.php b/InventoryInStorePickup/Plugin/InventoryApi/SourceRepository/LoadInStorePickupOnGetListPlugin.php index cf99f2d34320..fd85ccc98718 100644 --- a/InventoryInStorePickup/Plugin/InventoryApi/SourceRepository/LoadInStorePickupOnGetListPlugin.php +++ b/InventoryInStorePickup/Plugin/InventoryApi/SourceRepository/LoadInStorePickupOnGetListPlugin.php @@ -16,14 +16,14 @@ class LoadInStorePickupOnGetListPlugin { /** - * @var \Magento\Framework\Api\ExtensionAttributesFactory + * @var ExtensionAttributesFactory */ private $extensionAttributesFactory; /** * LoadInStorePickupOnGetListPlugin constructor. * - * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionAttributesFactory + * @param ExtensionAttributesFactory $extensionAttributesFactory */ public function __construct(ExtensionAttributesFactory $extensionAttributesFactory) { diff --git a/InventoryInStorePickup/Plugin/Sales/Order/SavePickupLocationForOrderPlugin.php b/InventoryInStorePickup/Plugin/Sales/Order/SavePickupLocationForOrderPlugin.php index a0c163d4631c..982df1c28e5b 100644 --- a/InventoryInStorePickup/Plugin/Sales/Order/SavePickupLocationForOrderPlugin.php +++ b/InventoryInStorePickup/Plugin/Sales/Order/SavePickupLocationForOrderPlugin.php @@ -32,11 +32,11 @@ public function __construct(SaveOrderPickupLocation $saveOrderPickupLocation) /** * Save Order to Pickup Location relation when saving the order. * - * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository - * @param \Magento\Sales\Api\Data\OrderInterface $result - * @param \Magento\Sales\Api\Data\OrderInterface $entity + * @param OrderRepositoryInterface $orderRepository + * @param OrderInterface $result + * @param OrderInterface $entity * - * @return \Magento\Sales\Api\Data\OrderInterface + * @return OrderInterface * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function afterSave( diff --git a/InventoryInStorePickup/Test/Integration/Extension/InventorySourceExtensionTest.php b/InventoryInStorePickup/Test/Integration/Extension/InventorySourceExtensionTest.php index 17d825058caf..af2ef3f42817 100644 --- a/InventoryInStorePickup/Test/Integration/Extension/InventorySourceExtensionTest.php +++ b/InventoryInStorePickup/Test/Integration/Extension/InventorySourceExtensionTest.php @@ -8,6 +8,7 @@ namespace Magento\InventoryInStorePickup\Test\Integration\Extension; use Magento\Framework\ObjectManagerInterface; +use Magento\InventoryApi\Api\Data\SourceInterface; use Magento\InventoryApi\Api\SourceRepositoryInterface; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; @@ -43,7 +44,7 @@ public function testGetListOfSourcesWithPickupLocationExtensionAfterSave() $searchResult = $this->sourceRepository->getList(); - /** @var \Magento\InventoryApi\Api\Data\SourceInterface $item */ + /** @var SourceInterface $item */ foreach ($searchResult->getItems() as $item) { $item->getExtensionAttributes()->setIsPickupLocationActive( $pickupLocationConfig[$item->getSourceCode()] diff --git a/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php b/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php index ec8761fb3aa8..940d1a547025 100644 --- a/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php +++ b/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php @@ -7,14 +7,18 @@ namespace Magento\InventoryInStorePickupAdminUi\Block\Adminhtml\Order\View; +use Magento\Backend\Block\Widget\Context; +use Magento\Backend\Block\Widget\Form\Container; use Magento\InventoryInStorePickupAdminUi\Controller\Adminhtml\Order\NotifyPickup; +use Magento\InventoryInStorePickupAdminUi\Model\IsDisplayReadyForPickupButton; +use Magento\Sales\Block\Adminhtml\Order\View; /** * TODO: is it possible to replace with UI Component? * * @package Magento\InventoryInStorePickupAdminUi\Block\Adminhtml\Order\View */ -class ReadyForPickup extends \Magento\Backend\Block\Widget\Form\Container +class ReadyForPickup extends Container { /** * Block group @@ -24,27 +28,27 @@ class ReadyForPickup extends \Magento\Backend\Block\Widget\Form\Container protected $_blockGroup = 'Magento_Sales'; /** - * @var \Magento\Sales\Block\Adminhtml\Order\View + * @var View */ private $viewBlock; /** - * @var \Magento\InventoryInStorePickupAdminUi\Model\IsDisplayReadyForPickupButton + * @var IsDisplayReadyForPickupButton */ private $isDisplayButton; /** * ReadyForPickup constructor. * - * @param \Magento\Backend\Block\Widget\Context $context - * @param \Magento\Sales\Block\Adminhtml\Order\View $viewBlock - * @param \Magento\InventoryInStorePickupAdminUi\Model\IsDisplayReadyForPickupButton $isDisplayButton + * @param Context $context + * @param View $viewBlock + * @param IsDisplayReadyForPickupButton $isDisplayButton * @param array $data */ public function __construct( - \Magento\Backend\Block\Widget\Context $context, - \Magento\Sales\Block\Adminhtml\Order\View $viewBlock, - \Magento\InventoryInStorePickupAdminUi\Model\IsDisplayReadyForPickupButton $isDisplayButton, + Context $context, + View $viewBlock, + IsDisplayReadyForPickupButton $isDisplayButton, array $data = [] ) { $this->viewBlock = $viewBlock; diff --git a/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php b/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php index 667148334087..8f7bc9390a5c 100644 --- a/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php +++ b/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php @@ -7,7 +7,9 @@ namespace Magento\InventoryInStorePickupAdminUi\Controller\Adminhtml\Order; +use Exception; use Magento\Backend\App\Action; +use Magento\Backend\App\Action\Context; use Magento\Framework\Controller\ResultInterface; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\LocalizedException; @@ -32,30 +34,30 @@ class NotifyPickup extends Action const ADMIN_RESOURCE = 'Magento_Sales::emails'; /** - * @var \Magento\InventoryInStorePickupApi\Api\NotifyOrderIsReadyForPickupInterface + * @var NotifyOrderIsReadyForPickupInterface */ private $notifyOrderIsReadyForPickup; /** - * @var \Magento\Sales\Api\OrderRepositoryInterface + * @var OrderRepositoryInterface */ private $orderRepository; /** - * @var \Psr\Log\LoggerInterface + * @var LoggerInterface */ private $logger; /** * NotifyPickup constructor. * - * @param \Magento\Backend\App\Action\Context $context - * @param \Magento\InventoryInStorePickupApi\Api\NotifyOrderIsReadyForPickupInterface $notifyOrderIsReadyForPickup - * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository - * @param \Psr\Log\LoggerInterface $logger + * @param Context $context + * @param NotifyOrderIsReadyForPickupInterface $notifyOrderIsReadyForPickup + * @param OrderRepositoryInterface $orderRepository + * @param LoggerInterface $logger */ public function __construct( - Action\Context $context, + Context $context, NotifyOrderIsReadyForPickupInterface $notifyOrderIsReadyForPickup, OrderRepositoryInterface $orderRepository, LoggerInterface $logger @@ -70,10 +72,10 @@ public function __construct( /** * Notify user * - * @return \Magento\Framework\Controller\ResultInterface - * @throws \Magento\Framework\Exception\InputException - * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @return ResultInterface + * @throws InputException + * @throws LocalizedException + * @throws NoSuchEntityException */ public function execute(): ResultInterface { @@ -85,7 +87,7 @@ public function execute(): ResultInterface $this->messageManager->addSuccessMessage(__('The customer have been notified and shipment created.')); } catch (LocalizedException $e) { $this->messageManager->addErrorMessage($e->getMessage()); - } catch (\Exception $e) { + } catch (Exception $e) { $this->messageManager->addErrorMessage(__('We can\'t notify the customer right now.')); $this->logger->critical($e); } @@ -104,10 +106,10 @@ public function execute(): ResultInterface /** * Initialize order model instance * - * @return \Magento\Sales\Api\Data\OrderInterface - * @throws \Magento\Framework\Exception\InputException - * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @return OrderInterface + * @throws InputException + * @throws LocalizedException + * @throws NoSuchEntityException * @see \Magento\Sales\Controller\Adminhtml\Order::_initOrder */ private function initOrder(): OrderInterface diff --git a/InventoryInStorePickupAdminUi/Model/IsDisplayReadyForPickupButton.php b/InventoryInStorePickupAdminUi/Model/IsDisplayReadyForPickupButton.php index 1ea28fa20390..c68942becd1f 100644 --- a/InventoryInStorePickupAdminUi/Model/IsDisplayReadyForPickupButton.php +++ b/InventoryInStorePickupAdminUi/Model/IsDisplayReadyForPickupButton.php @@ -7,14 +7,16 @@ namespace Magento\InventoryInStorePickupAdminUi\Model; +use Magento\Sales\Model\Order; + class IsDisplayReadyForPickupButton { /** - * @param \Magento\Sales\Model\Order $order + * @param Order $order * * @return bool */ - public function execute(\Magento\Sales\Model\Order $order): bool + public function execute(Order $order): bool { return $order->getExtensionAttributes()->getPickupLocationCode() && $order->canShip(); diff --git a/InventoryInStorePickupApi/Api/GetNearbySourcesByPostcodeInterface.php b/InventoryInStorePickupApi/Api/GetNearbySourcesByPostcodeInterface.php index 0390ea23ab83..bd1b3ca9136e 100644 --- a/InventoryInStorePickupApi/Api/GetNearbySourcesByPostcodeInterface.php +++ b/InventoryInStorePickupApi/Api/GetNearbySourcesByPostcodeInterface.php @@ -7,6 +7,9 @@ namespace Magento\InventoryInStorePickupApi\Api; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\InventoryApi\Api\Data\SourceInterface; + /** * Get nearby sources of a given zip code, based on the given radius in KM. * @@ -20,9 +23,9 @@ interface GetNearbySourcesByPostcodeInterface * @param string $country * @param string $postcode * @param int $radius - * @return \Magento\InventoryApi\Api\Data\SourceInterface[] + * @return SourceInterface[] * - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws NoSuchEntityException */ public function execute(string $country, string $postcode, int $radius): array; } diff --git a/InventoryInStorePickupApi/Api/NotifyOrderIsReadyForPickupInterface.php b/InventoryInStorePickupApi/Api/NotifyOrderIsReadyForPickupInterface.php index e0f0cd04d99d..1d54239ca1a8 100644 --- a/InventoryInStorePickupApi/Api/NotifyOrderIsReadyForPickupInterface.php +++ b/InventoryInStorePickupApi/Api/NotifyOrderIsReadyForPickupInterface.php @@ -7,14 +7,18 @@ namespace Magento\InventoryInStorePickupApi\Api; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\InventoryInStorePickupApi\Exception\OrderIsNotReadyForPickupException; + interface NotifyOrderIsReadyForPickupInterface { /** * @param int $orderId * - * @throws \Magento\Framework\Exception\NoSuchEntityException - * @throws \Magento\InventoryInStorePickupApi\Exception\OrderIsNotReadyForPickupException - * @throws \Magento\Framework\Exception\LocalizedException + * @throws NoSuchEntityException + * @throws OrderIsNotReadyForPickupException + * @throws LocalizedException */ public function execute(int $orderId): void; } diff --git a/InventoryInStorePickupApi/Exception/OrderIsNotReadyForPickupException.php b/InventoryInStorePickupApi/Exception/OrderIsNotReadyForPickupException.php index 800d0a4baa75..1670e4c8bcfd 100644 --- a/InventoryInStorePickupApi/Exception/OrderIsNotReadyForPickupException.php +++ b/InventoryInStorePickupApi/Exception/OrderIsNotReadyForPickupException.php @@ -7,6 +7,7 @@ namespace Magento\InventoryInStorePickupApi\Exception; +use Exception; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Phrase; @@ -19,11 +20,11 @@ class OrderIsNotReadyForPickupException extends LocalizedException { /** - * @param \Magento\Framework\Phrase $phrase - * @param \Exception $cause + * @param Phrase $phrase + * @param Exception $cause * @param int $code */ - public function __construct(Phrase $phrase = null, \Exception $cause = null, $code = 0) + public function __construct(Phrase $phrase = null, Exception $cause = null, $code = 0) { if ($phrase === null) { $phrase = new Phrase('The order is not ready for pickup'); diff --git a/InventoryInStorePickupApi/Model/GetNearbySourcesByPostcode.php b/InventoryInStorePickupApi/Model/GetNearbySourcesByPostcode.php index 9da390c98e80..000344622769 100644 --- a/InventoryInStorePickupApi/Model/GetNearbySourcesByPostcode.php +++ b/InventoryInStorePickupApi/Model/GetNearbySourcesByPostcode.php @@ -7,6 +7,7 @@ namespace Magento\InventoryInStorePickupApi\Model; +use InvalidArgumentException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\InventoryDistanceBasedSourceSelectionApi\Api\GetDistanceProviderCodeInterface; use Magento\InventoryInStorePickupApi\Api\GetNearbySourcesByPostcodeInterface; @@ -38,7 +39,7 @@ public function __construct( ) { foreach ($providers as $providerCode => $provider) { if (!($provider instanceof GetNearbySourcesByPostcodeInterface)) { - throw new \InvalidArgumentException( + throw new InvalidArgumentException( sprintf( "Nearby Sources provider %s must implement %s", $providerCode, From 04176e5a25324fedaf49905315821202ae18f048 Mon Sep 17 00:00:00 2001 From: Maksym Novik <m.novik@ism-ukraine.com> Date: Sat, 6 Apr 2019 13:16:00 +0300 Subject: [PATCH 079/231] Add ready for pickup button on the order page in admin panel which notifies customer that order could be picked up 2034. CanBeFulfilled -> IsFulfillable --- .../Model/IsOrderReadyForPickup.php | 14 +++++++------- .../{CanBeFulfilled.php => IsFulfillable.php} | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) rename InventoryInStorePickup/Model/Order/{CanBeFulfilled.php => IsFulfillable.php} (90%) diff --git a/InventoryInStorePickup/Model/IsOrderReadyForPickup.php b/InventoryInStorePickup/Model/IsOrderReadyForPickup.php index e6ddf7b2eab6..1ae2a947863c 100644 --- a/InventoryInStorePickup/Model/IsOrderReadyForPickup.php +++ b/InventoryInStorePickup/Model/IsOrderReadyForPickup.php @@ -7,7 +7,7 @@ namespace Magento\InventoryInStorePickup\Model; -use Magento\InventoryInStorePickup\Model\Order\CanBeFulfilled; +use Magento\InventoryInStorePickup\Model\Order\IsFulfillable; use Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface; use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\OrderRepositoryInterface; @@ -16,9 +16,9 @@ class IsOrderReadyForPickup implements IsOrderReadyForPickupInterface { /** - * @var CanBeFulfilled + * @var IsFulfillable */ - private $canBeFulfilled; + private $isFulfillable; /** * @var OrderRepositoryInterface @@ -26,14 +26,14 @@ class IsOrderReadyForPickup implements IsOrderReadyForPickupInterface private $orderRepository; /** - * @param CanBeFulfilled $canBeFulfilled + * @param IsFulfillable $isFulfillable * @param OrderRepositoryInterface $orderRepository */ public function __construct( - CanBeFulfilled $canBeFulfilled, + IsFulfillable $isFulfillable, OrderRepositoryInterface $orderRepository ) { - $this->canBeFulfilled = $canBeFulfilled; + $this->isFulfillable = $isFulfillable; $this->orderRepository = $orderRepository; } @@ -46,7 +46,7 @@ public function execute(int $orderId): bool { $order = $this->orderRepository->get($orderId); - return $this->canShip($order) && $this->canBeFulfilled->execute($order); + return $this->canShip($order) && $this->isFulfillable->execute($order); } /** diff --git a/InventoryInStorePickup/Model/Order/CanBeFulfilled.php b/InventoryInStorePickup/Model/Order/IsFulfillable.php similarity index 90% rename from InventoryInStorePickup/Model/Order/CanBeFulfilled.php rename to InventoryInStorePickup/Model/Order/IsFulfillable.php index 61b559cc6d4c..32a8a8753bfc 100644 --- a/InventoryInStorePickup/Model/Order/CanBeFulfilled.php +++ b/InventoryInStorePickup/Model/Order/IsFulfillable.php @@ -12,7 +12,7 @@ use Magento\InventoryApi\Api\SourceItemRepositoryInterface; use Magento\Sales\Api\Data\OrderInterface; -class CanBeFulfilled +class IsFulfillable { /** * @var SourceItemRepositoryInterface @@ -47,7 +47,7 @@ public function execute(OrderInterface $order): bool && $sourceCode = $order->getExtensionAttributes()->getPickupLocationCode() ) { foreach ($order->getItems() as $item) { - if (!$this->canItemBeFulfilled($item->getSku(), $sourceCode, (float)$item->getQtyOrdered())) { + if (!$this->isItemFulfillable($item->getSku(), $sourceCode, (float)$item->getQtyOrdered())) { return false; } } @@ -65,7 +65,7 @@ public function execute(OrderInterface $order): bool * * @return bool */ - private function canItemBeFulfilled(string $sku, string $sourceCode, float $qtyOrdered): bool + private function isItemFulfillable(string $sku, string $sourceCode, float $qtyOrdered): bool { $searchCriteria = $this->searchCriteriaBuilderFactory ->create() From acdd1fff10c1d9aace80cad462445722a03a716d Mon Sep 17 00:00:00 2001 From: Maksym Novik <m.novik@ism-ukraine.com> Date: Sat, 6 Apr 2019 13:24:54 +0300 Subject: [PATCH 080/231] Add ready for pickup button on the order page in admin panel which notifies customer that order could be picked up 2034. Killed OrderIsNotReadyForPickupException --- .../Model/NotifyOrderIsReadyForPickup.php | 4 +-- InventoryInStorePickup/i18n/en_US.csv | 1 + .../NotifyOrderIsReadyForPickupInterface.php | 2 -- .../OrderIsNotReadyForPickupException.php | 35 ------------------- 4 files changed, 3 insertions(+), 39 deletions(-) create mode 100644 InventoryInStorePickup/i18n/en_US.csv delete mode 100644 InventoryInStorePickupApi/Exception/OrderIsNotReadyForPickupException.php diff --git a/InventoryInStorePickup/Model/NotifyOrderIsReadyForPickup.php b/InventoryInStorePickup/Model/NotifyOrderIsReadyForPickup.php index 8b14a311110b..b088cda4a8ff 100644 --- a/InventoryInStorePickup/Model/NotifyOrderIsReadyForPickup.php +++ b/InventoryInStorePickup/Model/NotifyOrderIsReadyForPickup.php @@ -7,10 +7,10 @@ namespace Magento\InventoryInStorePickup\Model; +use Magento\Framework\Exception\LocalizedException; use Magento\InventoryInStorePickup\Model\Order\Email\ReadyForPickupNotifier; use Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface; use Magento\InventoryInStorePickupApi\Api\NotifyOrderIsReadyForPickupInterface; -use Magento\InventoryInStorePickupApi\Exception\OrderIsNotReadyForPickupException; use Magento\Sales\Api\OrderRepositoryInterface; use Magento\Sales\Api\ShipOrderInterface; @@ -62,7 +62,7 @@ public function __construct( public function execute(int $orderId): void { if (!$this->isOrderReadyForPickup->execute($orderId)) { - throw new OrderIsNotReadyForPickupException(); + throw new LocalizedException(__('The order is not ready for pickup')); } /** @noinspection PhpParamsInspection */ diff --git a/InventoryInStorePickup/i18n/en_US.csv b/InventoryInStorePickup/i18n/en_US.csv new file mode 100644 index 000000000000..984e2ad6a211 --- /dev/null +++ b/InventoryInStorePickup/i18n/en_US.csv @@ -0,0 +1 @@ +"The order is not ready for pickup","The order is not ready for pickup" diff --git a/InventoryInStorePickupApi/Api/NotifyOrderIsReadyForPickupInterface.php b/InventoryInStorePickupApi/Api/NotifyOrderIsReadyForPickupInterface.php index 1d54239ca1a8..7de9897179d2 100644 --- a/InventoryInStorePickupApi/Api/NotifyOrderIsReadyForPickupInterface.php +++ b/InventoryInStorePickupApi/Api/NotifyOrderIsReadyForPickupInterface.php @@ -9,7 +9,6 @@ use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; -use Magento\InventoryInStorePickupApi\Exception\OrderIsNotReadyForPickupException; interface NotifyOrderIsReadyForPickupInterface { @@ -17,7 +16,6 @@ interface NotifyOrderIsReadyForPickupInterface * @param int $orderId * * @throws NoSuchEntityException - * @throws OrderIsNotReadyForPickupException * @throws LocalizedException */ public function execute(int $orderId): void; diff --git a/InventoryInStorePickupApi/Exception/OrderIsNotReadyForPickupException.php b/InventoryInStorePickupApi/Exception/OrderIsNotReadyForPickupException.php deleted file mode 100644 index 1670e4c8bcfd..000000000000 --- a/InventoryInStorePickupApi/Exception/OrderIsNotReadyForPickupException.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryInStorePickupApi\Exception; - -use Exception; -use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Phrase; - -/** - * Class OrderIsNotReadyForPickupException - * - * @package Magento\InventoryInStorePickupApi\Exception - * @api - */ -class OrderIsNotReadyForPickupException extends LocalizedException -{ - /** - * @param Phrase $phrase - * @param Exception $cause - * @param int $code - */ - public function __construct(Phrase $phrase = null, Exception $cause = null, $code = 0) - { - if ($phrase === null) { - $phrase = new Phrase('The order is not ready for pickup'); - } - - parent::__construct($phrase, $cause, $code); - } -} From c4ee30a3c041b41952335d0ab6d634eb957de169 Mon Sep 17 00:00:00 2001 From: Maksym Novik <m.novik@ism-ukraine.com> Date: Sat, 6 Apr 2019 14:16:39 +0300 Subject: [PATCH 081/231] Add ready for pickup button on the order page in admin panel which notifies customer that order could be picked up 2034. Just another PHPDocs improvement --- InventoryInStorePickup/Model/IsOrderReadyForPickup.php | 3 +++ .../Model/NotifyOrderIsReadyForPickup.php | 7 ++++--- .../Model/Order/Email/ReadyForPickupNotifier.php | 6 ++---- .../Model/Order/Email/ReadyForPickupSender.php | 9 +++------ InventoryInStorePickup/Model/Order/IsFulfillable.php | 3 +++ .../LoadInStorePickupOnGetListPlugin.php | 2 -- .../SourceRepository/LoadInStorePickupOnGetPlugin.php | 2 +- .../Block/Adminhtml/Order/View/ReadyForPickup.php | 2 +- .../Controller/Adminhtml/Order/NotifyPickup.php | 2 +- .../Model/IsDisplayReadyForPickupButton.php | 3 +++ .../Api/IsOrderReadyForPickupInterface.php | 3 +++ .../Api/NotifyOrderIsReadyForPickupInterface.php | 3 +++ 12 files changed, 27 insertions(+), 18 deletions(-) diff --git a/InventoryInStorePickup/Model/IsOrderReadyForPickup.php b/InventoryInStorePickup/Model/IsOrderReadyForPickup.php index 1ae2a947863c..f9bb6f13edc4 100644 --- a/InventoryInStorePickup/Model/IsOrderReadyForPickup.php +++ b/InventoryInStorePickup/Model/IsOrderReadyForPickup.php @@ -13,6 +13,9 @@ use Magento\Sales\Api\OrderRepositoryInterface; use Magento\Sales\Model\Order; +/** + * Check if order can be shipped and the pickup location has enough QTY + */ class IsOrderReadyForPickup implements IsOrderReadyForPickupInterface { /** diff --git a/InventoryInStorePickup/Model/NotifyOrderIsReadyForPickup.php b/InventoryInStorePickup/Model/NotifyOrderIsReadyForPickup.php index b088cda4a8ff..03a94aa20982 100644 --- a/InventoryInStorePickup/Model/NotifyOrderIsReadyForPickup.php +++ b/InventoryInStorePickup/Model/NotifyOrderIsReadyForPickup.php @@ -14,6 +14,9 @@ use Magento\Sales\Api\OrderRepositoryInterface; use Magento\Sales\Api\ShipOrderInterface; +/** + * Send an email to the customer and ship the order to reserve pickup location`s QTY + */ class NotifyOrderIsReadyForPickup implements NotifyOrderIsReadyForPickupInterface { /** @@ -37,8 +40,6 @@ class NotifyOrderIsReadyForPickup implements NotifyOrderIsReadyForPickupInterfac private $orderRepository; /** - * NotifyOrderIsReadyAndShip constructor. - * * @param IsOrderReadyForPickupInterface $isOrderReadyForPickup * @param ShipOrderInterface $shipOrder * @param ReadyForPickupNotifier $emailNotifier @@ -57,7 +58,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function execute(int $orderId): void { diff --git a/InventoryInStorePickup/Model/Order/Email/ReadyForPickupNotifier.php b/InventoryInStorePickup/Model/Order/Email/ReadyForPickupNotifier.php index 608c2770da1b..4d0379735124 100644 --- a/InventoryInStorePickup/Model/Order/Email/ReadyForPickupNotifier.php +++ b/InventoryInStorePickup/Model/Order/Email/ReadyForPickupNotifier.php @@ -13,10 +13,8 @@ use Psr\Log\LoggerInterface as Logger; /** - * Class ReadyForPickupNotifier - * - * @package Magento\InventoryInStorePickup\Model\Order\Email - * TODO: probaly remove this class +* Sends email to customer + * TODO: remove this class with asynchronous mailing implementation */ class ReadyForPickupNotifier extends AbstractNotifier { diff --git a/InventoryInStorePickup/Model/Order/Email/ReadyForPickupSender.php b/InventoryInStorePickup/Model/Order/Email/ReadyForPickupSender.php index a72e7924c17c..c24b65fe6789 100644 --- a/InventoryInStorePickup/Model/Order/Email/ReadyForPickupSender.php +++ b/InventoryInStorePickup/Model/Order/Email/ReadyForPickupSender.php @@ -18,9 +18,6 @@ use Psr\Log\LoggerInterface; /** - * Class ReadyForPickupSender - * - * @package Magento\InventoryInStorePickup\Model\Order\Email * TODO: refactor * TODO: Implement asynchronous email sending */ @@ -32,8 +29,6 @@ class ReadyForPickupSender extends Sender private $eventManager; /** - * ReadyForPickupSender constructor. - * * @param Template $templateContainer * @param IdentityInterface $identityContainer * @param SenderBuilderFactory $senderBuilderFactory @@ -55,6 +50,9 @@ public function __construct( } /** + * Send order-specific email. + * This method is not declared anywhere in parent/interface, but Magento calls it + * * @param Order $order * * @return bool @@ -92,5 +90,4 @@ protected function prepareTemplate(Order $order) parent::prepareTemplate($order); } - } diff --git a/InventoryInStorePickup/Model/Order/IsFulfillable.php b/InventoryInStorePickup/Model/Order/IsFulfillable.php index 32a8a8753bfc..cb151099faed 100644 --- a/InventoryInStorePickup/Model/Order/IsFulfillable.php +++ b/InventoryInStorePickup/Model/Order/IsFulfillable.php @@ -12,6 +12,9 @@ use Magento\InventoryApi\Api\SourceItemRepositoryInterface; use Magento\Sales\Api\Data\OrderInterface; +/** + * Check if order can be fulfilled: if its pickup location has enough QTY + */ class IsFulfillable { /** diff --git a/InventoryInStorePickup/Plugin/InventoryApi/SourceRepository/LoadInStorePickupOnGetListPlugin.php b/InventoryInStorePickup/Plugin/InventoryApi/SourceRepository/LoadInStorePickupOnGetListPlugin.php index fd85ccc98718..637bc1765803 100644 --- a/InventoryInStorePickup/Plugin/InventoryApi/SourceRepository/LoadInStorePickupOnGetListPlugin.php +++ b/InventoryInStorePickup/Plugin/InventoryApi/SourceRepository/LoadInStorePickupOnGetListPlugin.php @@ -21,8 +21,6 @@ class LoadInStorePickupOnGetListPlugin private $extensionAttributesFactory; /** - * LoadInStorePickupOnGetListPlugin constructor. - * * @param ExtensionAttributesFactory $extensionAttributesFactory */ public function __construct(ExtensionAttributesFactory $extensionAttributesFactory) diff --git a/InventoryInStorePickup/Plugin/InventoryApi/SourceRepository/LoadInStorePickupOnGetPlugin.php b/InventoryInStorePickup/Plugin/InventoryApi/SourceRepository/LoadInStorePickupOnGetPlugin.php index 60ef170a55c2..1e324b0231ab 100644 --- a/InventoryInStorePickup/Plugin/InventoryApi/SourceRepository/LoadInStorePickupOnGetPlugin.php +++ b/InventoryInStorePickup/Plugin/InventoryApi/SourceRepository/LoadInStorePickupOnGetPlugin.php @@ -20,7 +20,7 @@ class LoadInStorePickupOnGetPlugin private $extensionAttributesFactory; /** - * LoadInStorePickupOnGetPlugin constructor. + * * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionAttributesFactory */ diff --git a/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php b/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php index 940d1a547025..adff53e5e70c 100644 --- a/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php +++ b/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php @@ -16,7 +16,7 @@ /** * TODO: is it possible to replace with UI Component? * - * @package Magento\InventoryInStorePickupAdminUi\Block\Adminhtml\Order\View + * Render 'Notify Order is Ready for Pickup' button on order view page */ class ReadyForPickup extends Container { diff --git a/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php b/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php index 8f7bc9390a5c..266edeefe3d6 100644 --- a/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php +++ b/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php @@ -70,7 +70,7 @@ public function __construct( } /** - * Notify user + * Notify customer by email * * @return ResultInterface * @throws InputException diff --git a/InventoryInStorePickupAdminUi/Model/IsDisplayReadyForPickupButton.php b/InventoryInStorePickupAdminUi/Model/IsDisplayReadyForPickupButton.php index c68942becd1f..9adf9d602183 100644 --- a/InventoryInStorePickupAdminUi/Model/IsDisplayReadyForPickupButton.php +++ b/InventoryInStorePickupAdminUi/Model/IsDisplayReadyForPickupButton.php @@ -9,6 +9,9 @@ use Magento\Sales\Model\Order; +/** + * Check if 'Notify Order is Ready for Pickup' button should be rendered + */ class IsDisplayReadyForPickupButton { /** diff --git a/InventoryInStorePickupApi/Api/IsOrderReadyForPickupInterface.php b/InventoryInStorePickupApi/Api/IsOrderReadyForPickupInterface.php index b62dd2ec8e51..658cc35b6842 100644 --- a/InventoryInStorePickupApi/Api/IsOrderReadyForPickupInterface.php +++ b/InventoryInStorePickupApi/Api/IsOrderReadyForPickupInterface.php @@ -7,6 +7,9 @@ namespace Magento\InventoryInStorePickupApi\Api; +/** + * Check if customer can pickup the order in the pickup location + */ interface IsOrderReadyForPickupInterface { /** diff --git a/InventoryInStorePickupApi/Api/NotifyOrderIsReadyForPickupInterface.php b/InventoryInStorePickupApi/Api/NotifyOrderIsReadyForPickupInterface.php index 7de9897179d2..539e8a5018b4 100644 --- a/InventoryInStorePickupApi/Api/NotifyOrderIsReadyForPickupInterface.php +++ b/InventoryInStorePickupApi/Api/NotifyOrderIsReadyForPickupInterface.php @@ -10,6 +10,9 @@ use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; +/** + * Send an email to the customer and ship the order to reserve pickup location`s QTY + */ interface NotifyOrderIsReadyForPickupInterface { /** From 54c23f03a17e96237881b02d31f4ef94c124b748 Mon Sep 17 00:00:00 2001 From: Maksym Novik <m.novik@ism-ukraine.com> Date: Sat, 6 Apr 2019 14:30:36 +0300 Subject: [PATCH 082/231] Add ready for pickup button on the order page in admin panel which notifies customer that order could be picked up 2034. Stopped declaring variables inside if condition --- InventoryInStorePickup/Model/Order/IsFulfillable.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/InventoryInStorePickup/Model/Order/IsFulfillable.php b/InventoryInStorePickup/Model/Order/IsFulfillable.php index cb151099faed..2c0034e6eb4a 100644 --- a/InventoryInStorePickup/Model/Order/IsFulfillable.php +++ b/InventoryInStorePickup/Model/Order/IsFulfillable.php @@ -46,9 +46,8 @@ public function __construct( */ public function execute(OrderInterface $order): bool { - if ($order->getExtensionAttributes() - && $sourceCode = $order->getExtensionAttributes()->getPickupLocationCode() - ) { + if ($order->getExtensionAttributes() && $order->getExtensionAttributes()->getPickupLocationCode()) { + $sourceCode = $order->getExtensionAttributes()->getPickupLocationCode(); foreach ($order->getItems() as $item) { if (!$this->isItemFulfillable($item->getSku(), $sourceCode, (float)$item->getQtyOrdered())) { return false; From d85ec25c7d62d33440bc4503169f95434bbd9c5d Mon Sep 17 00:00:00 2001 From: Francesco Tobia <francesco.tobia@magespecialist.it> Date: Sat, 6 Apr 2019 14:39:01 +0200 Subject: [PATCH 083/231] fix: remove Source Code column and add tooltip to the Name Column Issue: #2134 Removed Source Code column and added the source code to the Name Column. --- InventoryCatalogAdminUi/i18n/en_US.csv | 1 + .../view/adminhtml/ui_component/product_form.xml | 11 +---------- .../web/template/dynamic-rows/cells/text.html | 13 +++++++++++++ 3 files changed, 15 insertions(+), 10 deletions(-) create mode 100644 InventoryCatalogAdminUi/view/adminhtml/web/template/dynamic-rows/cells/text.html diff --git a/InventoryCatalogAdminUi/i18n/en_US.csv b/InventoryCatalogAdminUi/i18n/en_US.csv index 2bf1b90a9777..b4a59d5ff537 100644 --- a/InventoryCatalogAdminUi/i18n/en_US.csv +++ b/InventoryCatalogAdminUi/i18n/en_US.csv @@ -48,3 +48,4 @@ Done,Done "Are you sure you want to unassign one or more sources from the selected items?","Are you sure you want to unassign one or more sources from the selected items?" "Transfer Inventory To Source","Transfer Inventory To Source" "Are you sure you want to transfer the inventory of the selected items?","Are you sure you want to transfer the inventory of the selected items?" +"Source code: ", "Source code: " \ No newline at end of file diff --git a/InventoryCatalogAdminUi/view/adminhtml/ui_component/product_form.xml b/InventoryCatalogAdminUi/view/adminhtml/ui_component/product_form.xml index de9fd596cf63..77e5d9b25779 100644 --- a/InventoryCatalogAdminUi/view/adminhtml/ui_component/product_form.xml +++ b/InventoryCatalogAdminUi/view/adminhtml/ui_component/product_form.xml @@ -98,17 +98,8 @@ <item name="dataScope" xsi:type="string"/> </item> </argument> - <field name="source_code" formElement="input" sortOrder="10"> + <field name="name" formElement="input" sortOrder="20" template="Magento_InventoryCatalogAdminUi/dynamic-rows/cells/text"> <settings> - <elementTmpl>ui/dynamic-rows/cells/text</elementTmpl> - <dataType>text</dataType> - <dataScope>source_code</dataScope> - <label translate="true">Source Code</label> - </settings> - </field> - <field name="name" formElement="input" sortOrder="20"> - <settings> - <elementTmpl>ui/dynamic-rows/cells/text</elementTmpl> <dataType>text</dataType> <dataScope>name</dataScope> <label translate="true">Name</label> diff --git a/InventoryCatalogAdminUi/view/adminhtml/web/template/dynamic-rows/cells/text.html b/InventoryCatalogAdminUi/view/adminhtml/web/template/dynamic-rows/cells/text.html new file mode 100644 index 000000000000..fa290c8974e1 --- /dev/null +++ b/InventoryCatalogAdminUi/view/adminhtml/web/template/dynamic-rows/cells/text.html @@ -0,0 +1,13 @@ +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<div class="control-table-text"> + <span attr="'data-index': index, 'title': $t('Source code: ')+$record().data().source_code" data-bind=" + text: value, + css: {_disabled: disabled} + "> + </span> +</div> From 91a65e8ef3f4f0f699591872f6b0ef94ad33795e Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Sat, 6 Apr 2019 15:28:06 +0200 Subject: [PATCH 084/231] magento-engcom/msi#2120: Eliminate MySQL View usage on Default Stock --- .../AddIsInStockFieldToCollection.php | 40 +++++-------------- ...daptAddInStockFilterToCollectionPlugin.php | 19 ++++++++- 2 files changed, 27 insertions(+), 32 deletions(-) diff --git a/InventoryCatalog/Model/ResourceModel/AddIsInStockFieldToCollection.php b/InventoryCatalog/Model/ResourceModel/AddIsInStockFieldToCollection.php index 25b13e426902..5fcf9df771a6 100644 --- a/InventoryCatalog/Model/ResourceModel/AddIsInStockFieldToCollection.php +++ b/InventoryCatalog/Model/ResourceModel/AddIsInStockFieldToCollection.php @@ -8,8 +8,6 @@ namespace Magento\InventoryCatalog\Model\ResourceModel; use Magento\Catalog\Model\ResourceModel\Product\Collection; -use Magento\Framework\App\ObjectManager; -use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface; use Magento\InventoryIndexer\Indexer\IndexStructure; use Magento\InventoryIndexer\Model\StockIndexTableNameResolverInterface; @@ -23,48 +21,30 @@ class AddIsInStockFieldToCollection */ private $stockIndexTableProvider; - /** - * @var DefaultStockProviderInterface - */ - private $defaultStockProvider; - /** * @param StockIndexTableNameResolverInterface $stockIndexTableProvider - * @param DefaultStockProviderInterface|null $defaultStockProvider */ public function __construct( - StockIndexTableNameResolverInterface $stockIndexTableProvider, - ?DefaultStockProviderInterface $defaultStockProvider = null + StockIndexTableNameResolverInterface $stockIndexTableProvider ) { $this->stockIndexTableProvider = $stockIndexTableProvider; - $this->defaultStockProvider = $defaultStockProvider ?: ObjectManager::getInstance() - ->get(DefaultStockProviderInterface::class); } /** + * Modify "is in stock" collection filter to support non-default sources. + * * @param Collection $collection * @param int $stockId - * * @return void */ - public function execute($collection, int $stockId): void + public function execute($collection, int $stockId) { - if ($stockId === $this->defaultStockProvider->getId()) { - $isSalableColumnName = 'is_in_stock'; - $resource = $collection->getResource(); - $collection->getSelect()->join( - ['inventory_in_stock' => $resource->getTable('cataloginventory_stock_item')], - sprintf('%s.entity_id = inventory_in_stock.product_id', Collection::MAIN_TABLE_ALIAS), - [IndexStructure::IS_SALABLE => $isSalableColumnName] - ); - } else { - $tableName = $this->stockIndexTableProvider->execute($stockId); + $tableName = $this->stockIndexTableProvider->execute($stockId); - $collection->getSelect()->join( - ['inventory_in_stock' => $tableName], - 'e.sku = inventory_in_stock.sku', - [] - )->where('inventory_in_stock.' . IndexStructure::IS_SALABLE . ' = ?', 1); - } + $collection->getSelect()->join( + ['inventory_in_stock' => $tableName], + 'e.sku = inventory_in_stock.sku', + [] + )->where('inventory_in_stock.' . IndexStructure::IS_SALABLE . ' = ?', 1); } } diff --git a/InventoryCatalog/Plugin/CatalogInventory/Helper/Stock/AdaptAddInStockFilterToCollectionPlugin.php b/InventoryCatalog/Plugin/CatalogInventory/Helper/Stock/AdaptAddInStockFilterToCollectionPlugin.php index a625c704cfe2..bba3f6176db3 100644 --- a/InventoryCatalog/Plugin/CatalogInventory/Helper/Stock/AdaptAddInStockFilterToCollectionPlugin.php +++ b/InventoryCatalog/Plugin/CatalogInventory/Helper/Stock/AdaptAddInStockFilterToCollectionPlugin.php @@ -11,6 +11,7 @@ use Magento\CatalogInventory\Helper\Stock; use Magento\InventoryCatalog\Model\GetStockIdForCurrentWebsite; use Magento\InventoryCatalog\Model\ResourceModel\AddIsInStockFieldToCollection; +use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface; /** * Adapt addInStockFilterToCollection for multi stocks. @@ -27,19 +28,29 @@ class AdaptAddInStockFilterToCollectionPlugin */ private $addIsInStockFieldToCollection; + /** + * @var DefaultStockProviderInterface + */ + private $defaultStockProvider; + /** * @param GetStockIdForCurrentWebsite $getStockIdForCurrentWebsite * @param AddIsInStockFieldToCollection $addIsInStockFieldToCollection + * @param DefaultStockProviderInterface $defaultStockProvider */ public function __construct( GetStockIdForCurrentWebsite $getStockIdForCurrentWebsite, - AddIsInStockFieldToCollection $addIsInStockFieldToCollection + AddIsInStockFieldToCollection $addIsInStockFieldToCollection, + DefaultStockProviderInterface $defaultStockProvider ) { $this->getStockIdForCurrentWebsite = $getStockIdForCurrentWebsite; $this->addIsInStockFieldToCollection = $addIsInStockFieldToCollection; + $this->defaultStockProvider = $defaultStockProvider; } /** + * Add filtering by "is in stock" criteria to the stock filter collection when source is not default. + * * @param Stock $subject * @param callable $proceed * @param Collection $collection @@ -50,6 +61,10 @@ public function __construct( public function aroundAddInStockFilterToCollection(Stock $subject, callable $proceed, $collection) { $stockId = $this->getStockIdForCurrentWebsite->execute(); - $this->addIsInStockFieldToCollection->execute($collection, $stockId); + if ($stockId === $this->defaultStockProvider->getId()) { + $proceed($collection); + } else { + $this->addIsInStockFieldToCollection->execute($collection, $stockId); + } } } From efd2dd741cbc3093e4d05a33301ff91da489267c Mon Sep 17 00:00:00 2001 From: Riccardo Ugolini <riccardo.ugolini@magespecialist.it> Date: Sat, 6 Apr 2019 16:20:29 +0200 Subject: [PATCH 085/231] changed command classes, added integration test --- .../Command/ReservationInconsistency.php | 29 ++++--- .../Model/GetOrderInFinalState.php | 56 +++++++++++++ .../Model/GetOrderWithBrokenReservation.php | 51 ++++++++++++ InventoryReservations/Model/Reservation.php | 2 + .../GetListReservationsTotOrder.php | 19 ++--- .../GetListReservationsTotOrdersTest.php | 79 +++---------------- .../_fixtures/order_with_reservation.php | 55 +++++++++++++ 7 files changed, 193 insertions(+), 98 deletions(-) create mode 100644 InventoryReservations/Model/GetOrderInFinalState.php create mode 100644 InventoryReservations/Model/GetOrderWithBrokenReservation.php create mode 100644 InventoryReservations/Test/Integration/_fixtures/order_with_reservation.php diff --git a/InventoryReservations/Command/ReservationInconsistency.php b/InventoryReservations/Command/ReservationInconsistency.php index be5655f9e3e6..64a76521045c 100644 --- a/InventoryReservations/Command/ReservationInconsistency.php +++ b/InventoryReservations/Command/ReservationInconsistency.php @@ -7,30 +7,29 @@ namespace Magento\InventoryReservations\Command; -use Magento\InventoryReservations\Model\GetReservationsTotOrder\Proxy as GetReservationsTotOrder; +use Magento\InventoryReservations\Model\GetOrderWithBrokenReservation; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\Order; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -/** - * Class ImportCustomersCommand - */ class ReservationInconsistency extends Command { /** - * @var \Magento\InventoryReservations\Model\GetReservationsTotOrder $getReservationsTotOrder + * @var GetOrderWithBrokenReservation */ - private $getReservationsTotOrder; + private $getOrderWithBrokenReservation; /** * ReservationInconsistency constructor. - * @param GetReservationsTotOrder $getReservationsTotOrder + * @param GetOrderWithBrokenReservation $getOrderWithBrokenReservation */ public function __construct( - GetReservationsTotOrder $getReservationsTotOrder + GetOrderWithBrokenReservation $getOrderWithBrokenReservation ) { parent::__construct(); - $this->getReservationsTotOrder = $getReservationsTotOrder; + $this->getOrderWithBrokenReservation = $getOrderWithBrokenReservation; } protected function configure() @@ -49,14 +48,14 @@ protected function configure() */ public function execute(InputInterface $input, OutputInterface $output): void { - /** @var array $orderListReservations */ - $orderListReservations = $this->getReservationsTotOrder->getListReservationsTotOrder(); + /** @var OrderInterface[] $orders */ + $orders = $this->getOrderWithBrokenReservation->execute(); - foreach ($orderListReservations as $orderReservationTot){ + /** @var Order $order */ + foreach($orders as $order) { $output->writeln( - __('Order %1 got inconsistency on reservation by %2', - $orderReservationTot['IncrementId'], - $orderReservationTot['ReservationTot'] + __('Order %1 got inconsistency on inventory reservation', + $order->getIncrementId() ) ); } diff --git a/InventoryReservations/Model/GetOrderInFinalState.php b/InventoryReservations/Model/GetOrderInFinalState.php new file mode 100644 index 000000000000..df2787ca6d8e --- /dev/null +++ b/InventoryReservations/Model/GetOrderInFinalState.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservations\Model; + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Api\SearchCriteriaInterface; +use Magento\Sales\Model\Order; + +class GetOrderInFinalState +{ + /** + * @var \Magento\Sales\Api\OrderRepositoryInterface + */ + private $orderRepository; + /** + * @var SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + + /** + * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository + * @param SearchCriteriaBuilder $searchCriteriaBuilder + */ + public function __construct ( + \Magento\Sales\Api\OrderRepositoryInterface $orderRepository, + SearchCriteriaBuilder $searchCriteriaBuilder + ) { + $this->orderRepository = $orderRepository; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + } + + /** + * @param array $orderIds + * @return \Magento\Sales\Api\Data\OrderSearchResultInterface + */ + public function execute(array $orderIds): \Magento\Sales\Api\Data\OrderSearchResultInterface + { + /** @var SearchCriteriaInterface $filter */ + $filter = $this->searchCriteriaBuilder + ->addFilter('entity_id', $orderIds, 'in') + ->addFilter('state', [ + Order::STATE_COMPLETE, + Order::STATE_CLOSED, + Order::STATE_CANCELED + ], 'in') + ->create(); + + return $this->orderRepository->getList($filter); + } +} + diff --git a/InventoryReservations/Model/GetOrderWithBrokenReservation.php b/InventoryReservations/Model/GetOrderWithBrokenReservation.php new file mode 100644 index 000000000000..224d7eb179d4 --- /dev/null +++ b/InventoryReservations/Model/GetOrderWithBrokenReservation.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservations\Model; + +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\ResourceModel\Order\Collection; + +class GetOrderWithBrokenReservation +{ + /** + * @var GetReservationsTotOrder + */ + private $getReservationsTotOrder; + /** + * @var GetOrderInFinalState + */ + private $getOrderInFinalState; + + /** + * GetOrderWithBrokenReservation constructor. + * @param GetReservationsTotOrder $getReservationsTotOrder + * @param GetOrderInFinalState $getOrderInFinalState + */ + public function __construct( + GetReservationsTotOrder $getReservationsTotOrder, + GetOrderInFinalState $getOrderInFinalState + ) { + $this->getReservationsTotOrder = $getReservationsTotOrder; + $this->getOrderInFinalState = $getOrderInFinalState; + } + + /** + * @return OrderInterface[] + */ + public function execute(): array + { + /** @var array $orderListReservations */ + $orderListReservations = $this->getReservationsTotOrder->getListReservationsTotOrder(); + + $brokenReservation = array_column($orderListReservations, 'ReservationTot', 'OrderId'); + $orderIds = array_keys($brokenReservation); + /** @var Collection $orders */ + $orders = $this->getOrderInFinalState->execute($orderIds); + return $orders->getItems(); + } +} diff --git a/InventoryReservations/Model/Reservation.php b/InventoryReservations/Model/Reservation.php index 3d479530ed72..8b577feab847 100644 --- a/InventoryReservations/Model/Reservation.php +++ b/InventoryReservations/Model/Reservation.php @@ -16,6 +16,8 @@ */ class Reservation implements ReservationInterface { + public const TABLE_NAME = 'inventory_reservation'; + /** * @var int|null */ diff --git a/InventoryReservations/Model/ResourceModel/GetListReservationsTotOrder.php b/InventoryReservations/Model/ResourceModel/GetListReservationsTotOrder.php index 3ca00352a886..50edf5e229a3 100644 --- a/InventoryReservations/Model/ResourceModel/GetListReservationsTotOrder.php +++ b/InventoryReservations/Model/ResourceModel/GetListReservationsTotOrder.php @@ -8,7 +8,6 @@ namespace Magento\InventoryReservations\Model\ResourceModel; use Magento\Framework\App\ResourceConnection; -use Magento\Sales\Model\Order; class GetListReservationsTotOrder { @@ -31,22 +30,16 @@ public function execute(): array { $connection = $this->resourceConnection->getConnection(); $tableName = $this->resourceConnection->getTableName('inventory_reservation'); - $tableSalesOrderName = $this->resourceConnection->getTableName('sales_order'); - $complete = Order::STATE_COMPLETE; - $closed = Order::STATE_CLOSED; - $canceled = Order::STATE_CANCELED; - - //todo: modularity rework $qry = $connection ->select() - ->from($tableName, ['ReservationTot' => 'sum(quantity)']) - ->joinInner($tableSalesOrderName, - 'entity_id = ' . new \Zend_Db_Expr("CAST(JSON_EXTRACT(metadata, '$.object_id') as UNSIGNED) - AND " . $connection->quoteInto('state IN (?)', [$complete, $closed, $canceled])), - ['IncrementId' => 'increment_id'] + ->from($tableName, + [ + 'OrderId' => new \Zend_Db_Expr("CAST(JSON_EXTRACT(metadata, '$.object_id') as UNSIGNED)"), + 'ReservationTot' => 'sum(quantity)' + ] ) - ->group('IncrementId') + ->group('OrderId') ->having('ReservationTot != ?', 0); return $connection->fetchAll($qry); } diff --git a/InventoryReservations/Test/Integration/Model/GetListReservationsTotOrdersTest.php b/InventoryReservations/Test/Integration/Model/GetListReservationsTotOrdersTest.php index cfb39429be7d..638db72fc682 100644 --- a/InventoryReservations/Test/Integration/Model/GetListReservationsTotOrdersTest.php +++ b/InventoryReservations/Test/Integration/Model/GetListReservationsTotOrdersTest.php @@ -7,52 +7,31 @@ namespace Magento\InventoryReservations\Test\Integration\Model; -use Magento\Catalog\Model\Product; -use Magento\InventoryReservations\Model\ResourceModel\GetListReservationsTotOrder; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\ResourceModel\Order\Collection as OrderCollection; -use Magento\Catalog\Model\ResourceModel\Product\Collection as ProductCollection; +use Magento\InventoryReservations\Model\GetOrderWithBrokenReservation; use PHPUnit\Framework\TestCase; use Magento\TestFramework\Helper\Bootstrap; -use Magento\Framework\Registry; class GetListReservationsTotOrdersTest extends TestCase { /** - * @magentoDataFixture Magento/Sales/_files/order_new.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryReservations/Test/Integration/_fixtures/order_with_reservation.php */ - public function testShouldReturnEmptyArray(): void + public function testShouldNotFindAnyInconsistency(): void { $objectManager = Bootstrap::getObjectManager(); - /** @var GetListReservationsTotOrder $subject */ - $subject = $objectManager->get(GetListReservationsTotOrder::class); - - /** @var OrderRepositoryInterface $orderRepository */ - $orderRepository = $objectManager->create(OrderRepositoryInterface::class); - - /** @var OrderCollection $orderCollection */ - $orderCollection = $objectManager->create(OrderCollection::class); - $orderCollection->addFieldToFilter('increment_id', '100000001'); - - /** @var Order $order */ - $order = $orderCollection->getFirstItem(); - - $order->setStatus(Order::STATE_COMPLETE); - $order->setState(Order::STATE_COMPLETE); - $orderRepository->save($order); + /** @var GetOrderWithBrokenReservation $subject */ + $subject = $objectManager->get(GetOrderWithBrokenReservation::class); /** @var array $result */ $result = $subject->execute(); - $this->assertSame([], $result); + self::assertSame([], $result); } /** - * @magentoDataFixture Magento/Sales/_files/order_new.php - * @magentoDataFixture Magento/Sales/_files/order_shipping.php + * @magentoDataFixture Magento/Sales/_files/order_with_shipping_and_invoice.php * @magentoDataFixture ../../../../app/code/Magento/InventoryReservations/Test/Integration/_fixtures/broken_reservation.php * @magentoDbIsolation enabled */ @@ -60,52 +39,12 @@ public function testShouldReturnOneReservationInconsistency(): void { $objectManager = Bootstrap::getObjectManager(); - /** @var GetListReservationsTotOrder $subject */ - $subject = $objectManager->get(GetListReservationsTotOrder::class); - - /** @var OrderRepositoryInterface $orderRepository */ - $orderRepository = $objectManager->create(OrderRepositoryInterface::class); - - /** @var OrderCollection $orderCollection */ - $orderCollection = $objectManager->create(OrderCollection::class); - $orderCollection->addFieldToFilter('increment_id', '100000001'); - - /** @var Order $order */ - $order = $orderCollection->getFirstItem(); - - $order->setStatus(Order::STATE_COMPLETE); - $order->setState(Order::STATE_COMPLETE); - $orderRepository->save($order); + /** @var GetOrderWithBrokenReservation $subject */ + $subject = $objectManager->get(GetOrderWithBrokenReservation::class); /** @var array $result */ $result = $subject->execute(); self::assertCount(1, $result); } - - - - /** - * @throws \Exception - */ - public function tearDown() - { - $objectManager = Bootstrap::getObjectManager(); - $objectManager->get(Registry::class)->register('isSecureArea', true); - - /** @var OrderCollection $orderCollection */ - $orderCollection = $objectManager->create(OrderCollection::class); - /** @var Order $order */ - foreach ($orderCollection->getItems() as $order) { - $order->getResource()->delete($order); - } - - /** @var ProductCollection $productCollection */ - $productCollection = $objectManager->create(ProductCollection::class); - - /** @var Product $product */ - foreach ($productCollection->getItems() as $product) { - $product->getResource()->delete($product); - } - } } diff --git a/InventoryReservations/Test/Integration/_fixtures/order_with_reservation.php b/InventoryReservations/Test/Integration/_fixtures/order_with_reservation.php new file mode 100644 index 000000000000..20db3b6e83b8 --- /dev/null +++ b/InventoryReservations/Test/Integration/_fixtures/order_with_reservation.php @@ -0,0 +1,55 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Sales\Model\Order\ShipmentFactory; + +require __DIR__ . '/../../../../../../../dev/tests/integration/testsuite/Magento/Sales/_files/order.php'; + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var \Magento\Sales\Model\Order $order */ +$order = $objectManager->create(\Magento\Sales\Model\Order::class) + ->loadByIncrementId('100000001'); + +/** @var \Magento\Sales\Api\OrderManagementInterface $orderManagement */ +$orderManagement = $objectManager->create(\Magento\Sales\Api\OrderManagementInterface::class); + +$orderManagement->place($order); + +/** @var \Magento\Sales\Model\Service\InvoiceService $invoiceService */ +$invoiceService = $objectManager->create(\Magento\Sales\Api\InvoiceManagementInterface::class); + +/** @var \Magento\Framework\DB\Transaction $transaction */ +$transaction = $objectManager->create(\Magento\Framework\DB\Transaction::class); + +$order->setData( + 'base_to_global_rate', + 1 +)->setData( + 'base_to_order_rate', + 1 +)->setData( + 'shipping_amount', + 20 +)->setData( + 'base_shipping_amount', + 20 +); + +$invoice = $invoiceService->prepareInvoice($order); +$invoice->register(); + +$order->setIsInProcess(true); + +$items = []; +foreach ($order->getItems() as $orderItem) { + $items[$orderItem->getId()] = $orderItem->getQtyOrdered(); +} +$shipment = $objectManager->get(ShipmentFactory::class)->create($order, $items); +$shipment->register(); + +$transaction->addObject($invoice)->addObject($shipment)->addObject($order)->save(); From 89bad4e14c94433431a2599bf38654ca2af43d79 Mon Sep 17 00:00:00 2001 From: Francesco Tobia <francesco.tobia@magespecialist.it> Date: Sat, 6 Apr 2019 16:24:12 +0200 Subject: [PATCH 086/231] fix: elimate column Notify Quantity Use Default and move checkbox to Notify Qty column Issue: #2132 These changes have been made in the Assigned Source panel: - Eliminated column "Notify Quantity Use Default" - Moved checkbox which was in column "Notify Quantity Use Default" to "Notifiy Qty" column below the number input - Added "Use Default" description to the checkbox --- .../i18n/en_US.csv | 2 + .../adminhtml/ui_component/product_form.xml | 86 +++++++++++-------- 2 files changed, 50 insertions(+), 38 deletions(-) diff --git a/InventoryLowQuantityNotificationAdminUi/i18n/en_US.csv b/InventoryLowQuantityNotificationAdminUi/i18n/en_US.csv index 3c7093256d0f..9ebabb94e648 100644 --- a/InventoryLowQuantityNotificationAdminUi/i18n/en_US.csv +++ b/InventoryLowQuantityNotificationAdminUi/i18n/en_US.csv @@ -9,3 +9,5 @@ Quantity,Quantity "Source Code","Source Code" "Notify Quantity","Notify Quantity" "Notify Quantity Use Default","Notify Quantity Use Default" +"Notify Qty","Notify Qty" +"Use Default","Use Default" diff --git a/InventoryLowQuantityNotificationAdminUi/view/adminhtml/ui_component/product_form.xml b/InventoryLowQuantityNotificationAdminUi/view/adminhtml/ui_component/product_form.xml index f5aaa92a2bc8..1e8336c57f95 100644 --- a/InventoryLowQuantityNotificationAdminUi/view/adminhtml/ui_component/product_form.xml +++ b/InventoryLowQuantityNotificationAdminUi/view/adminhtml/ui_component/product_form.xml @@ -6,7 +6,7 @@ */ --> <form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd"> - <fieldset name="sources"> + <container name="sources"> <dynamicRows name="assigned_sources" component="Magento_Ui/js/dynamic-rows/dynamic-rows-grid"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> @@ -18,47 +18,57 @@ </item> </argument> <container name="record" component="Magento_Ui/js/dynamic-rows/record"> - <field name="notify_stock_qty" formElement="input" sortOrder="60" component="Magento_InventoryLowQuantityNotificationAdminUi/js/components/notify-stock-qty"> - <settings> - <dataType>text</dataType> - <dataScope>notify_stock_qty</dataScope> - <label translate="true">Notify Quantity</label> - <imports> - <link name="notifyStockQtyUseDefault">${$.parentName}.notify_stock_qty_use_default:checked</link> - <link name="manageStock">!${ $.provider }:data.product.stock_data.manage_stock</link> - </imports> - </settings> - </field> - <field name="notify_stock_qty_use_default" component="Magento_InventoryLowQuantityNotificationAdminUi/js/components/use-config-settings" formElement="checkbox" sortOrder="70"> + <container component="Magento_Ui/js/form/components/group" sortOrder="60"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> - <item name="valueFromConfig" xsi:type="object">Magento\CatalogInventory\Model\Source\StockConfiguration</item> - <item name="keyInConfiguration" xsi:type="string">notify_stock_qty</item> - <item name="default" xsi:type="number">1</item> + <item name="label" xsi:type="string" translate="true">Notify Qty</item> + <item name="showLabel" xsi:type="boolean">false</item> </item> </argument> - <settings> - <dataScope>notify_stock_qty_use_default</dataScope> - <label translate="true">Notify Quantity Use Default</label> - <links> - <link name="linkedValue">${$.provider}:${$.parentScope}.notify_stock_qty</link> - </links> - <imports> - <link name="disabled">!${ $.provider }:data.product.stock_data.manage_stock</link> - </imports> - </settings> - <formElements> - <checkbox class="Magento\InventoryLowQuantityNotificationAdminUi\Ui\Component\Product\Form\Element\UseConfigSettings"> - <settings> - <valueMap> - <map name="false" xsi:type="string">0</map> - <map name="true" xsi:type="string">1</map> - </valueMap> - </settings> - </checkbox> - </formElements> - </field> + <field name="notify_stock_qty" formElement="input" sortOrder="60" + component="Magento_InventoryLowQuantityNotificationAdminUi/js/components/notify-stock-qty"> + <settings> + <labelVisible>false</labelVisible> + <dataType>text</dataType> + <dataScope>notify_stock_qty</dataScope> + <imports> + <link name="notifyStockQtyUseDefault">${$.parentName}.notify_stock_qty_use_default:checked</link> + <link name="manageStock">!${ $.provider }:data.product.stock_data.manage_stock</link> + </imports> + </settings> + </field> + <field name="notify_stock_qty_use_default" component="Magento_InventoryLowQuantityNotificationAdminUi/js/components/use-config-settings" formElement="checkbox" sortOrder="70"> + <argument name="data" xsi:type="array"> + <item name="config" xsi:type="array"> + <item name="valueFromConfig" xsi:type="object">Magento\CatalogInventory\Model\Source\StockConfiguration</item> + <item name="keyInConfiguration" xsi:type="string">notify_stock_qty</item> + <item name="default" xsi:type="number">1</item> + </item> + </argument> + <settings> + <labelVisible>false</labelVisible> + <dataScope>notify_stock_qty_use_default</dataScope> + <links> + <link name="linkedValue">${$.provider}:${$.parentScope}.notify_stock_qty</link> + </links> + <imports> + <link name="disabled">!${ $.provider }:data.product.stock_data.manage_stock</link> + </imports> + </settings> + <formElements> + <checkbox class="Magento\InventoryLowQuantityNotificationAdminUi\Ui\Component\Product\Form\Element\UseConfigSettings"> + <settings> + <description translate="true">Use Default</description> + <valueMap> + <map name="false" xsi:type="string">0</map> + <map name="true" xsi:type="string">1</map> + </valueMap> + </settings> + </checkbox> + </formElements> + </field> + </container> </container> </dynamicRows> - </fieldset> + </container> </form> From b10e891a1f42c04a631cbe36e5a7ce96737746f5 Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Mon, 8 Apr 2019 10:41:47 +0200 Subject: [PATCH 087/231] Removed async tests due to missing AMQP configuration in the pipeline --- ...ToLegacyStockItemAtSourceItemsSaveTest.php | 85 ++++++++++--------- ...gacyStockStatusAtSourceItemsDeleteTest.php | 85 ++++++++++--------- ...ultSourceItemAtLegacyStockItemSaveTest.php | 39 ++++----- 3 files changed, 106 insertions(+), 103 deletions(-) diff --git a/InventoryLegacySynchronization/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php b/InventoryLegacySynchronization/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php index 80306d1d2701..986b360dfff4 100644 --- a/InventoryLegacySynchronization/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php +++ b/InventoryLegacySynchronization/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php @@ -135,47 +135,48 @@ public function testShouldSynchronizeLegacyStock(): void */ public function testShouldSynchronizeLegacyStockAsynchronously(): void { - $productSku = 'SKU-1'; - $product = $this->productRepository->get($productSku); - $productId = $product->getId(); - $websiteId = 0; - - /** @var StockItemCriteriaInterface $legacyStockItemCriteria */ - $legacyStockItemCriteria = $this->legacyStockItemCriteriaFactory->create(); - $legacyStockItemCriteria->setProductsFilter($productId); - $legacyStockItemCriteria->setScopeFilter($websiteId); - $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); - self::assertCount(1, $legacyStockItems); - - $legacyStockItem = reset($legacyStockItems); - self::assertTrue($legacyStockItem->getIsInStock()); - self::assertEquals(5.5, $legacyStockItem->getQty()); - - $searchCriteria = $this->searchCriteriaBuilder - ->addFilter(SourceItemInterface::SKU, $productSku) - ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) - ->create(); - $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); - self::assertCount(1, $sourceItems); - - $sourceItem = reset($sourceItems); - $sourceItem->setQuantity(20.0); - $sourceItem->setStatus(SourceItemInterface::STATUS_OUT_OF_STOCK); - $this->sourceItemsSave->execute($sourceItems); - - // Make sure we did not yet synchronized it - $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); - $legacyStockItem = current($legacyStockItems); - self::assertCount(1, $legacyStockItems); - self::assertTrue($legacyStockItem->getIsInStock()); - self::assertEquals(5.5, $legacyStockItem->getQty()); - - $this->consumer->process(1); - - // Check after asynchrnous consumer call - $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); - $legacyStockItem = current($legacyStockItems); - self::assertFalse($legacyStockItem->getIsInStock()); - self::assertEquals(20, $legacyStockItem->getQty()); +// $productSku = 'SKU-1'; +// $product = $this->productRepository->get($productSku); +// $productId = $product->getId(); +// $websiteId = 0; +// +// /** @var StockItemCriteriaInterface $legacyStockItemCriteria */ +// $legacyStockItemCriteria = $this->legacyStockItemCriteriaFactory->create(); +// $legacyStockItemCriteria->setProductsFilter($productId); +// $legacyStockItemCriteria->setScopeFilter($websiteId); +// $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); +// self::assertCount(1, $legacyStockItems); +// +// $legacyStockItem = reset($legacyStockItems); +// self::assertTrue($legacyStockItem->getIsInStock()); +// self::assertEquals(5.5, $legacyStockItem->getQty()); +// +// $searchCriteria = $this->searchCriteriaBuilder +// ->addFilter(SourceItemInterface::SKU, $productSku) +// ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) +// ->create(); +// $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); +// self::assertCount(1, $sourceItems); +// +// $sourceItem = reset($sourceItems); +// $sourceItem->setQuantity(20.0); +// $sourceItem->setStatus(SourceItemInterface::STATUS_OUT_OF_STOCK); +// $this->sourceItemsSave->execute($sourceItems); +// +// // Make sure we did not yet synchronized it +// $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); +// $legacyStockItem = current($legacyStockItems); +// self::assertCount(1, $legacyStockItems); +// self::assertTrue($legacyStockItem->getIsInStock()); +// self::assertEquals(5.5, $legacyStockItem->getQty()); +// +// $this->consumer->process(1); +// +// // Check after asynchrnous consumer call +// $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); +// $legacyStockItem = current($legacyStockItems); +// self::assertFalse($legacyStockItem->getIsInStock()); +// self::assertEquals(20, $legacyStockItem->getQty()); + $this->markTestIncomplete('Test is failing due to missing AMQP configuration'); } } diff --git a/InventoryLegacySynchronization/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php b/InventoryLegacySynchronization/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php index 55fd8392d5fb..5a5ac57f4d36 100644 --- a/InventoryLegacySynchronization/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php +++ b/InventoryLegacySynchronization/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php @@ -131,47 +131,48 @@ public function testShouldSetOutOfStockOnDelete(): void */ public function testShouldSetOutOfStockOnDeleteAsynchronously(): void { - $productSku = 'SKU-1'; - $product = $this->productRepository->get($productSku); - $productId = $product->getId(); - $websiteId = 0; - - /** @var StockStatusCriteriaInterface $legacyStockStatusCriteria */ - $legacyStockStatusCriteria = $this->legacyStockStatusCriteriaFactory->create(); - $legacyStockStatusCriteria->setProductsFilter($productId); - $legacyStockStatusCriteria->setScopeFilter($websiteId); - $legacyStockStatuses = $this->legacyStockStatusRepository->getList($legacyStockStatusCriteria)->getItems(); - self::assertCount(1, $legacyStockStatuses); - - $legacyStockStatus = reset($legacyStockStatuses); - self::assertSame(Status::STATUS_IN_STOCK, $legacyStockStatus->getStockStatus()); - self::assertSame(5.5, (float) $legacyStockStatus->getQty()); - - $searchCriteria = $this->searchCriteriaBuilder - ->addFilter(SourceItemInterface::SKU, $productSku) - ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) - ->create(); - $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); - self::assertCount(1, $sourceItems); - - $this->sourceItemsDelete->execute($sourceItems); - - // Before sync - $legacyStockStatuses = $this->legacyStockStatusRepository->getList($legacyStockStatusCriteria)->getItems(); - self::assertCount(1, $legacyStockStatuses); - - $legacyStockStatus = reset($legacyStockStatuses); - self::assertSame(Status::STATUS_IN_STOCK, $legacyStockStatus->getStockStatus()); - self::assertSame(5.5, (float) $legacyStockStatus->getQty()); - - $this->consumer->process(1); - - // After sync - $legacyStockStatuses = $this->legacyStockStatusRepository->getList($legacyStockStatusCriteria)->getItems(); - self::assertCount(1, $legacyStockStatuses); - - $legacyStockStatus = reset($legacyStockStatuses); - self::assertSame(Status::STATUS_OUT_OF_STOCK, $legacyStockStatus->getStockStatus()); - self::assertSame(0.0, (float) $legacyStockStatus->getQty()); +// $productSku = 'SKU-1'; +// $product = $this->productRepository->get($productSku); +// $productId = $product->getId(); +// $websiteId = 0; +// +// /** @var StockStatusCriteriaInterface $legacyStockStatusCriteria */ +// $legacyStockStatusCriteria = $this->legacyStockStatusCriteriaFactory->create(); +// $legacyStockStatusCriteria->setProductsFilter($productId); +// $legacyStockStatusCriteria->setScopeFilter($websiteId); +// $legacyStockStatuses = $this->legacyStockStatusRepository->getList($legacyStockStatusCriteria)->getItems(); +// self::assertCount(1, $legacyStockStatuses); +// +// $legacyStockStatus = reset($legacyStockStatuses); +// self::assertSame(Status::STATUS_IN_STOCK, $legacyStockStatus->getStockStatus()); +// self::assertSame(5.5, (float) $legacyStockStatus->getQty()); +// +// $searchCriteria = $this->searchCriteriaBuilder +// ->addFilter(SourceItemInterface::SKU, $productSku) +// ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) +// ->create(); +// $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); +// self::assertCount(1, $sourceItems); +// +// $this->sourceItemsDelete->execute($sourceItems); +// +// // Before sync +// $legacyStockStatuses = $this->legacyStockStatusRepository->getList($legacyStockStatusCriteria)->getItems(); +// self::assertCount(1, $legacyStockStatuses); +// +// $legacyStockStatus = reset($legacyStockStatuses); +// self::assertSame(Status::STATUS_IN_STOCK, $legacyStockStatus->getStockStatus()); +// self::assertSame(5.5, (float) $legacyStockStatus->getQty()); +// +// $this->consumer->process(1); +// +// // After sync +// $legacyStockStatuses = $this->legacyStockStatusRepository->getList($legacyStockStatusCriteria)->getItems(); +// self::assertCount(1, $legacyStockStatuses); +// +// $legacyStockStatus = reset($legacyStockStatuses); +// self::assertSame(Status::STATUS_OUT_OF_STOCK, $legacyStockStatus->getStockStatus()); +// self::assertSame(0.0, (float) $legacyStockStatus->getQty()); + $this->markTestIncomplete('Test is failing due to missing AMQP configuration'); } } diff --git a/InventoryLegacySynchronization/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php b/InventoryLegacySynchronization/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php index 1fbaa646ac62..551a577aa028 100644 --- a/InventoryLegacySynchronization/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php +++ b/InventoryLegacySynchronization/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php @@ -75,25 +75,26 @@ public function testSaveLegacyStockItemAssignedToDefaultSource(): void */ public function testSaveLegacyStockItemAssignedToDefaultSourceAsynchronously(): void { - $stockItem = $this->stockRegistry->getStockItemBySku('SKU-1'); - $stockItem->setQty(10); - $this->stockRegistry->updateStockItemBySku('SKU-1', $stockItem); - - $defaultSourceItem = $this->getDefaultSourceItemBySku->execute('SKU-1'); - self::assertEquals( - 5.5, - $defaultSourceItem->getQuantity(), - 'Source item was update synchronously even if asynchronous operation was requested' - ); - - $this->consumer->process(1); - - $defaultSourceItem = $this->getDefaultSourceItemBySku->execute('SKU-1'); - self::assertEquals( - 10, - $defaultSourceItem->getQuantity(), - 'Asynchronous source item update failed' - ); +// $stockItem = $this->stockRegistry->getStockItemBySku('SKU-1'); +// $stockItem->setQty(10); +// $this->stockRegistry->updateStockItemBySku('SKU-1', $stockItem); +// +// $defaultSourceItem = $this->getDefaultSourceItemBySku->execute('SKU-1'); +// self::assertEquals( +// 5.5, +// $defaultSourceItem->getQuantity(), +// 'Source item was update synchronously even if asynchronous operation was requested' +// ); +// +// $this->consumer->process(1); +// +// $defaultSourceItem = $this->getDefaultSourceItemBySku->execute('SKU-1'); +// self::assertEquals( +// 10, +// $defaultSourceItem->getQuantity(), +// 'Asynchronous source item update failed' +// ); + $this->markTestIncomplete('Test is failing due to missing AMQP configuration'); } /** From 351163782799871cc281dc0d624cffbdb8dd3064 Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Mon, 8 Apr 2019 10:51:41 +0200 Subject: [PATCH 088/231] FIX parameters deprecation --- .../Model/BulkInventoryTransfer.php | 36 +++++++------------ InventoryCatalog/Model/BulkSourceUnassign.php | 16 ++++----- InventoryCatalog/etc/di.xml | 24 +++++++++++++ 3 files changed, 44 insertions(+), 32 deletions(-) diff --git a/InventoryCatalog/Model/BulkInventoryTransfer.php b/InventoryCatalog/Model/BulkInventoryTransfer.php index f810d892d19a..79f1d7607089 100644 --- a/InventoryCatalog/Model/BulkInventoryTransfer.php +++ b/InventoryCatalog/Model/BulkInventoryTransfer.php @@ -10,10 +10,7 @@ use Magento\Framework\Validation\ValidationException; use Magento\InventoryCatalog\Model\ResourceModel\BulkInventoryTransfer as BulkInventoryTransferResource; use Magento\InventoryCatalogApi\Api\BulkInventoryTransferInterface; -use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; use Magento\InventoryCatalogApi\Model\BulkInventoryTransferValidatorInterface; -use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface; -use Magento\CatalogInventory\Model\Indexer\Stock as LegacyIndexer; use Magento\InventoryIndexer\Indexer\Source\SourceIndexer; /** @@ -32,40 +29,31 @@ class BulkInventoryTransfer implements BulkInventoryTransferInterface private $bulkInventoryTransfer; /** - * @var LegacyIndexer + * @var SourceIndexer */ - private $legacyIndexer; + private $sourceIndexer; /** - * MassProductSourceAssign constructor. * @param BulkInventoryTransferValidatorInterface $inventoryTransferValidator * @param BulkInventoryTransferResource $bulkInventoryTransfer - * @param SourceIndexer $sourceIndexer @deprecated - * @param DefaultSourceProviderInterface $defaultSourceProvider @deprecated - * @param GetProductIdsBySkusInterface $getProductIdsBySkus @deprecated - * @param LegacyIndexer $legacyIndexer + * @param SourceIndexer $sourceIndexer + * @param null $defaultSourceProvider @deprecated + * @param null $getProductIdsBySkus @deprecated + * @param null $legacyIndexer @deprecated * @SuppressWarnings(PHPMD.LongVariable) + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( BulkInventoryTransferValidatorInterface $inventoryTransferValidator, BulkInventoryTransferResource $bulkInventoryTransfer, SourceIndexer $sourceIndexer, - DefaultSourceProviderInterface $defaultSourceProvider, - GetProductIdsBySkusInterface $getProductIdsBySkus, - LegacyIndexer $legacyIndexer + $defaultSourceProvider, + $getProductIdsBySkus, + $legacyIndexer ) { $this->bulkInventoryTransferValidator = $inventoryTransferValidator; $this->bulkInventoryTransfer = $bulkInventoryTransfer; - $this->legacyIndexer = $legacyIndexer; - } - - /** - * Reindex legacy stock (for default source) - * @param array $productIds - */ - private function reindexLegacy(array $productIds): void - { - $this->legacyIndexer->executeList($productIds); + $this->sourceIndexer = $sourceIndexer; } /** @@ -95,6 +83,8 @@ public function execute( $unassignFromOrigin ); + $this->sourceIndexer->executeList([$originSource, $destinationSource]); + return true; } } diff --git a/InventoryCatalog/Model/BulkSourceUnassign.php b/InventoryCatalog/Model/BulkSourceUnassign.php index d9c67bec7360..e38c71b83950 100644 --- a/InventoryCatalog/Model/BulkSourceUnassign.php +++ b/InventoryCatalog/Model/BulkSourceUnassign.php @@ -9,11 +9,9 @@ use Magento\Framework\Validation\ValidationException; use Magento\InventoryCatalogApi\Api\BulkSourceUnassignInterface; -use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; use Magento\InventoryCatalogApi\Model\BulkSourceUnassignValidatorInterface; use Magento\InventoryCatalog\Model\ResourceModel\BulkSourceUnassign as BulkSourceUnassignResource; use Magento\InventoryIndexer\Indexer\Source\SourceIndexer; -use Magento\CatalogInventory\Model\Indexer\Stock as LegacyIndexer; /** * @inheritdoc @@ -36,22 +34,22 @@ class BulkSourceUnassign implements BulkSourceUnassignInterface private $sourceIndexer; /** - * MassProductSourceAssign constructor. * @param BulkSourceUnassignValidatorInterface $unassignValidator * @param BulkSourceUnassignResource $bulkSourceUnassign - * @param DefaultSourceProviderInterface $defaultSourceProvider @deprecated - * @param GetProductIdsBySkus $getProductIdsBySkus @deprecated + * @param null $defaultSourceProvider @deprecated + * @param null $getProductIdsBySkus @deprecated * @param SourceIndexer $sourceIndexer - * @param LegacyIndexer $legacyIndexer @deprecated + * @param null $legacyIndexer @deprecated * @SuppressWarnings(PHPMD.LongVariable) + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( BulkSourceUnassignValidatorInterface $unassignValidator, BulkSourceUnassignResource $bulkSourceUnassign, - DefaultSourceProviderInterface $defaultSourceProvider, - GetProductIdsBySkus $getProductIdsBySkus, + $defaultSourceProvider, + $getProductIdsBySkus, SourceIndexer $sourceIndexer, - LegacyIndexer $legacyIndexer + $legacyIndexer ) { $this->unassignValidator = $unassignValidator; $this->bulkSourceUnassign = $bulkSourceUnassign; diff --git a/InventoryCatalog/etc/di.xml b/InventoryCatalog/etc/di.xml index 1912e95fa519..1e2136a11b28 100644 --- a/InventoryCatalog/etc/di.xml +++ b/InventoryCatalog/etc/di.xml @@ -80,6 +80,7 @@ <plugin name="adapt_get_product_stock_status" type="Magento\InventoryCatalog\Plugin\CatalogInventory\Api\StockRegistry\AdaptGetProductStockStatusPlugin"/> <plugin name="adapt_get_product_stock_status_by_sku" type="Magento\InventoryCatalog\Plugin\CatalogInventory\Api\StockRegistry\AdaptGetProductStockStatusBySkuPlugin"/> </type> + <!-- Mass Source Assignment --> <preference for="Magento\InventoryCatalogApi\Api\BulkSourceAssignInterface" type="Magento\InventoryCatalog\Model\BulkSourceAssign"/> @@ -87,6 +88,29 @@ type="Magento\InventoryCatalog\Model\BulkSourceUnassign"/> <preference for="Magento\InventoryCatalogApi\Api\BulkInventoryTransferInterface" type="Magento\InventoryCatalog\Model\BulkInventoryTransfer"/> + + <type name="Magento\InventoryCatalog\Model\BulkInventoryTransfer"> + <arguments> + <!-- @deprecated parameter defaultSourceProvider has been deprecated and not in use now --> + <argument name="defaultSourceProvider" xsi:type="null" /> + <!-- @deprecated parameter defaultSourceProvider has been deprecated and not in use now --> + <argument name="getProductIdsBySkus" xsi:type="null" /> + <!-- @deprecated parameter legacyIndexer has been deprecated and not in use now --> + <argument name="legacyIndexer" xsi:type="null" /> + </arguments> + </type> + + <type name="Magento\InventoryCatalog\Model\BulkSourceUnassign"> + <arguments> + <!-- @deprecated parameter defaultSourceProvider has been deprecated and not in use now --> + <argument name="defaultSourceProvider" xsi:type="null" /> + <!-- @deprecated parameter defaultSourceProvider has been deprecated and not in use now --> + <argument name="getProductIdsBySkus" xsi:type="null" /> + <!-- @deprecated parameter legacyIndexer has been deprecated and not in use now --> + <argument name="legacyIndexer" xsi:type="null" /> + </arguments> + </type> + <type name="\Magento\InventoryCatalogApi\Model\BulkSourceAssignValidatorChain"> <arguments> <argument name="validators" xsi:type="array"> From 209e82d92336255b63a3b0e81f6b923b02b315bb Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Mon, 8 Apr 2019 22:44:15 +0300 Subject: [PATCH 089/231] magento-engcom/msi#2124: GraphQL support for the new Inventory - Added GraphQL schema - Updates based on static tests result --- .../Model/Resolver/OnlyXLeftInStockResolver.php | 12 ++---------- InventoryGraphQl/composer.json | 5 ++++- InventoryGraphQl/etc/di.xml | 11 ----------- InventoryGraphQl/etc/schema.graphqls | 12 ++++++++++++ 4 files changed, 18 insertions(+), 22 deletions(-) delete mode 100644 InventoryGraphQl/etc/di.xml create mode 100644 InventoryGraphQl/etc/schema.graphqls diff --git a/InventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php b/InventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php index 3bde5126bbb2..58dc850e65ac 100644 --- a/InventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php +++ b/InventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php @@ -8,7 +8,6 @@ namespace Magento\InventoryGraphQl\Model\Resolver; use Magento\Catalog\Api\Data\ProductInterface; -use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Config\Element\Field; @@ -24,11 +23,6 @@ */ class OnlyXLeftInStockResolver implements ResolverInterface { - /** - * @var ScopeConfigInterface - */ - private $scopeConfig; - /** * @var GetProductSalableQtyInterface */ @@ -45,18 +39,15 @@ class OnlyXLeftInStockResolver implements ResolverInterface private $getStockItemConfiguration; /** - * @param ScopeConfigInterface $scopeConfig * @param GetProductSalableQtyInterface $getProductSalableQty * @param GetStockIdForCurrentWebsite $getStockIdForCurrentWebsite * @param GetStockItemConfigurationInterface $getStockItemConfiguration */ public function __construct( - ScopeConfigInterface $scopeConfig, GetProductSalableQtyInterface $getProductSalableQty, GetStockIdForCurrentWebsite $getStockIdForCurrentWebsite, GetStockItemConfigurationInterface $getStockItemConfiguration ) { - $this->scopeConfig = $scopeConfig; $this->getProductSalableQty = $getProductSalableQty; $this->getStockIdForCurrentWebsite = $getStockIdForCurrentWebsite; $this->getStockItemConfiguration = $getStockItemConfiguration; @@ -75,8 +66,9 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value } /** - * @param string $sku + * Get quantity of a specified product when lower then configuration threshold. * + * @param string $sku * @return null|float * @throws SkuIsNotAssignedToStockException * @throws LocalizedException diff --git a/InventoryGraphQl/composer.json b/InventoryGraphQl/composer.json index b173994fbc9a..bc0bb2bb56d4 100644 --- a/InventoryGraphQl/composer.json +++ b/InventoryGraphQl/composer.json @@ -5,7 +5,10 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", - "magento/module-catalog-inventory-graph-ql": "*" + "magento/module-catalog": "*", + "magento/module-inventory-catalog": "*", + "magento/module-inventory-configuration-api": "*", + "magento/module-inventory-sales-api": "*" }, "license": [ "OSL-3.0", diff --git a/InventoryGraphQl/etc/di.xml b/InventoryGraphQl/etc/di.xml deleted file mode 100644 index 1dbe6bbdf2be..000000000000 --- a/InventoryGraphQl/etc/di.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?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"> - <preference for="Magento\CatalogInventoryGraphQl\Model\Resolver\OnlyXLeftInStockResolver" type="Magento\InventoryGraphQl\Model\Resolver\OnlyXLeftInStockResolver"/> - <preference for="Magento\CatalogInventoryGraphQl\Model\Resolver\StockStatusProvider" type="Magento\InventoryGraphQl\Model\Resolver\StockStatusProvider"/> -</config> diff --git a/InventoryGraphQl/etc/schema.graphqls b/InventoryGraphQl/etc/schema.graphqls new file mode 100644 index 000000000000..ce7e66457011 --- /dev/null +++ b/InventoryGraphQl/etc/schema.graphqls @@ -0,0 +1,12 @@ +# Copyright © Magento, Inc. All rights reserved. +# See COPYING.txt for license details. + +interface ProductInterface { + only_x_left_in_stock: Float @doc(description: "Product stock only x left count") @resolver(class: "Magento\\InventoryGraphQl\\Model\\Resolver\\OnlyXLeftInStockResolver") + stock_status: ProductStockStatus @doc(description: "Stock status of the product") @resolver(class: "Magento\\InventoryGraphQl\\Model\\Resolver\\StockStatusProvider") +} + +enum ProductStockStatus @doc(description: "This enumeration states whether a product stock status is in stock or out of stock") { + IN_STOCK + OUT_OF_STOCK +} From 8373319b6785a30f86bccbc760dc739ebcdebe51 Mon Sep 17 00:00:00 2001 From: Bettina Cerban <bettinacerban@gmail.com> Date: Mon, 8 Apr 2019 17:10:36 -0300 Subject: [PATCH 090/231] change transfer interface to specify origin and destination sources only once --- .../Model/BulkPartialInventoryTransfer.php | 59 +++++----- .../GetSourceItemsBySkuAndSourceCodes.php | 4 +- .../Model/PartialInventoryTransfer.php | 28 +---- .../Model/PartialInventoryTransferItem.php | 47 ++++++++ .../TransferInventoryPartially.php | 25 ++-- ....php => PartialTransferItemsValidator.php} | 48 +++----- .../PartialTransferSourceValidator.php | 70 ++++++++++++ .../Api/Bulk/PartialInventoryTransferTest.php | 108 +++++++++++++----- InventoryCatalog/etc/di.xml | 15 ++- .../BulkPartialInventoryTransferInterface.php | 5 +- .../PartialInventoryTransferInterface.php | 23 +--- .../PartialInventoryTransferItemInterface.php | 37 ++++++ ...PartialInventoryTransferValidatorChain.php | 69 +++++++++++ ...ialInventoryTransferValidatorInterface.php | 4 +- InventoryCatalogApi/etc/di.xml | 2 + 15 files changed, 394 insertions(+), 150 deletions(-) create mode 100644 InventoryCatalog/Model/PartialInventoryTransferItem.php rename InventoryCatalog/Model/Source/Validator/{PartialTransferValidator.php => PartialTransferItemsValidator.php} (58%) create mode 100644 InventoryCatalog/Model/Source/Validator/PartialTransferSourceValidator.php create mode 100644 InventoryCatalogApi/Api/Data/PartialInventoryTransferItemInterface.php create mode 100644 InventoryCatalogApi/Model/PartialInventoryTransferValidatorChain.php diff --git a/InventoryCatalog/Model/BulkPartialInventoryTransfer.php b/InventoryCatalog/Model/BulkPartialInventoryTransfer.php index d83a10adc312..5f4775efd535 100644 --- a/InventoryCatalog/Model/BulkPartialInventoryTransfer.php +++ b/InventoryCatalog/Model/BulkPartialInventoryTransfer.php @@ -7,9 +7,9 @@ namespace Magento\InventoryCatalog\Model; +use Magento\Framework\Validation\ValidationException; use Magento\InventoryApi\Api\Data\SourceItemInterface; use Magento\CatalogInventory\Model\Indexer\Stock as LegacyIndexer; -use Magento\Framework\Exception\NoSuchEntityException; use Magento\InventoryCatalog\Model\ResourceModel\TransferInventoryPartially; use Magento\InventoryCatalogApi\Api\BulkPartialInventoryTransferInterface; use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; @@ -42,7 +42,6 @@ class BulkPartialInventoryTransfer implements BulkPartialInventoryTransferInterf private $legacyIndexer; /** - * BulkPartialInventoryTransfer constructor. * @param PartialInventoryTransferValidatorInterface $partialInventoryTransferValidator * @param TransferInventoryPartially $transferInventoryPartiallyCommand * @param GetProductIdsBySkusInterface $getProductIdsBySkus @@ -59,42 +58,49 @@ public function __construct( DefaultSourceProviderInterface $defaultSourceProvider, SourceIndexer $sourceIndexer, LegacyIndexer $legacyIndexer - ) - { + ) { $this->transferValidator = $partialInventoryTransferValidator; - $this->transferCommand = $transferInventoryPartiallyCommand; - $this->productIdsBySkus = $getProductIdsBySkus; - $this->sourceItemsBySku = $getSourceItemsBySkuAndSourceCodes; + $this->transferCommand = $transferInventoryPartiallyCommand; + $this->productIdsBySkus = $getProductIdsBySkus; + $this->sourceItemsBySku = $getSourceItemsBySkuAndSourceCodes; $this->defaultSourceProvider = $defaultSourceProvider; - $this->sourceIndexer = $sourceIndexer; - $this->legacyIndexer = $legacyIndexer; + $this->sourceIndexer = $sourceIndexer; + $this->legacyIndexer = $legacyIndexer; } /** * Run bulk partial inventory transfer for specified items. * - * @param PartialInventoryTransferInterface[] $items + * @param PartialInventoryTransferInterface $transfer * @return SourceItemInterface[] + * @throws \Magento\Framework\Validation\ValidationException */ - public function execute(array $items): array + public function execute($transfer): array + { + $validationResult = $this->transferValidator->validate($transfer); + if ($validationResult->isValid()) { + return $this->processTransfer($transfer); + } + + throw new ValidationException(__("Transfer validation failed"), null, 0, $validationResult); + } + + /** + * @param PartialInventoryTransferInterface $transfer + * @return SourceItemInterface[] + */ + private function processTransfer($transfer): array { - $sources = []; $processedSkus = []; $sourceItems = []; - foreach ($items as $item) { - $validationResult = $this->transferValidator->validate($item); - if ($validationResult->isValid()) { - $this->transferCommand->execute($item); - - $processedSkus[] = $item->getSku(); - $sources[] = $item->getOriginSourceCode(); - $sources[] = $item->getDestinationSourceCode(); - } - $sourceItems += $this->sourceItemsBySku->execute($item->getSku(), [$item->getOriginSourceCode(), $item->getDestinationSourceCode()]); + foreach ($transfer->getItems() as $item) { + $this->transferCommand->execute($item, $transfer->getOriginSourceCode(), $transfer->getDestinationSourceCode()); + $processedSkus[] = $item->getSku(); + $sourceItems += $this->sourceItemsBySku->execute($item->getSku(), [$transfer->getOriginSourceCode(), $transfer->getDestinationSourceCode()]); } - $this->updateIndexes($sources, $processedSkus); + $this->updateIndexes([$transfer->getOriginSourceCode(), $transfer->getDestinationSourceCode()], $processedSkus); return $sourceItems; } @@ -113,13 +119,12 @@ private function updateIndexes(array $sources, array $skus) } /** + * * @param string[] $skus */ private function updateLegacyIndex(array $skus) { - try { - $productIds = $this->productIdsBySkus->execute($skus); - $this->legacyIndexer->executeList($productIds); - } catch (NoSuchEntityException $e) {} + $productIds = $this->productIdsBySkus->execute($skus); + $this->legacyIndexer->executeList($productIds); } } \ No newline at end of file diff --git a/InventoryCatalog/Model/GetSourceItemsBySkuAndSourceCodes.php b/InventoryCatalog/Model/GetSourceItemsBySkuAndSourceCodes.php index a8cc954d9af8..b58efa1c0caf 100644 --- a/InventoryCatalog/Model/GetSourceItemsBySkuAndSourceCodes.php +++ b/InventoryCatalog/Model/GetSourceItemsBySkuAndSourceCodes.php @@ -20,15 +20,13 @@ class GetSourceItemsBySkuAndSourceCodes private $sourceItemRepository; /** - * GetSourceItemsBySkuAndSourceCodes constructor. * @param SearchCriteriaBuilder $searchCriteriaBuilder * @param SourceItemRepositoryInterface $sourceItemRepository */ public function __construct( SearchCriteriaBuilder $searchCriteriaBuilder, SourceItemRepositoryInterface $sourceItemRepository - ) - { + ) { $this->searchCriteriaBuilder = $searchCriteriaBuilder; $this->sourceItemRepository = $sourceItemRepository; } diff --git a/InventoryCatalog/Model/PartialInventoryTransfer.php b/InventoryCatalog/Model/PartialInventoryTransfer.php index 67b39c00375d..453e96c8e55e 100644 --- a/InventoryCatalog/Model/PartialInventoryTransfer.php +++ b/InventoryCatalog/Model/PartialInventoryTransfer.php @@ -15,35 +15,19 @@ class PartialInventoryTransfer extends AbstractSimpleObject implements PartialIn { /** - * @return string - */ - public function getSku(): string - { - return $this->_get(self::SKU); - } - - /** - * @param string $sku - */ - public function setSku(string $sku): void - { - $this->setData(self::SKU, $sku); - } - - /** - * @return float + * @return \Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferItemInterface[] */ - public function getQty(): float + public function getItems(): array { - return $this->_get(self::QTY); + return $this->_get(self::ITEMS); } /** - * @param float $qty + * @param \Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferItemInterface[] $items */ - public function setQty(float $qty): void + public function setItems(array $items): void { - $this->setData(self::QTY, $qty); + $this->setData(self::ITEMS, $items); } /** diff --git a/InventoryCatalog/Model/PartialInventoryTransferItem.php b/InventoryCatalog/Model/PartialInventoryTransferItem.php new file mode 100644 index 000000000000..f4923a8298b4 --- /dev/null +++ b/InventoryCatalog/Model/PartialInventoryTransferItem.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalog\Model; + +use Magento\Framework\Api\AbstractSimpleObject; +use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferItemInterface; + +class PartialInventoryTransferItem extends AbstractSimpleObject implements PartialInventoryTransferItemInterface +{ + + /** + * @return string + */ + public function getSku(): string + { + return $this->_get(self::SKU); + } + + /** + * @param string $sku + */ + public function setSku(string $sku): void + { + $this->setData(self::SKU, $sku); + } + + /** + * @return float + */ + public function getQty(): float + { + return $this->_get(self::QTY); + } + + /** + * @param float $qty + */ + public function setQty(float $qty): void + { + $this->setData(self::QTY, $qty); + } +} \ No newline at end of file diff --git a/InventoryCatalog/Model/ResourceModel/TransferInventoryPartially.php b/InventoryCatalog/Model/ResourceModel/TransferInventoryPartially.php index 275b8294407e..05d565bd9c53 100644 --- a/InventoryCatalog/Model/ResourceModel/TransferInventoryPartially.php +++ b/InventoryCatalog/Model/ResourceModel/TransferInventoryPartially.php @@ -12,7 +12,7 @@ use Magento\Inventory\Model\ResourceModel\SourceItem; use Magento\InventoryApi\Api\Data\SourceItemInterface; use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; -use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface; +use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferItemInterface; class TransferInventoryPartially { @@ -26,7 +26,6 @@ class TransferInventoryPartially private $setDataToLegacyStockItemCommand; /** - * TransferInventoryPartially constructor. * @param ResourceConnection $resourceConnection * @param DefaultSourceProviderInterface $defaultSourceProvider * @param SetDataToLegacyStockItem $setDataToLegacyCatalogInventoryCommand @@ -35,21 +34,25 @@ public function __construct( ResourceConnection $resourceConnection, DefaultSourceProviderInterface $defaultSourceProvider, SetDataToLegacyStockItem $setDataToLegacyCatalogInventoryCommand - ) - { + ) { $this->resourceConnection = $resourceConnection; $this->defaultSourceProvider = $defaultSourceProvider; $this->setDataToLegacyStockItemCommand = $setDataToLegacyCatalogInventoryCommand; } - public function execute(PartialInventoryTransferInterface $transfer) + /** + * @param PartialInventoryTransferItemInterface $transfer + * @param string $originSourceCode + * @param string $destinationSourceCode + */ + public function execute(PartialInventoryTransferItemInterface $transfer, string $originSourceCode, string $destinationSourceCode): void { $tableName = $this->resourceConnection->getTableName(SourceItem::TABLE_NAME_SOURCE_ITEM); $connection = $this->resourceConnection->getConnection(); $connection->beginTransaction(); - $originSourceItemData = $this->getSourceItemData($transfer->getSku(), $transfer->getOriginSourceCode()); - $destSourceItemData = $this->getSourceItemData($transfer->getSku(), $transfer->getDestinationSourceCode()); + $originSourceItemData = $this->getSourceItemData($transfer->getSku(), $originSourceCode); + $destSourceItemData = $this->getSourceItemData($transfer->getSku(), $destinationSourceCode); $updatedQtyAtOrigin = $originSourceItemData === null ? 0.0 : (float) $originSourceItemData[SourceItemInterface::QUANTITY] - $transfer->getQty(); $updatedQtyAtDest = $destSourceItemData === null ? 0.0 : (float) $destSourceItemData[SourceItemInterface::QUANTITY] + $transfer->getQty(); @@ -58,17 +61,17 @@ public function execute(PartialInventoryTransferInterface $transfer) $destUpdate = [SourceItemInterface::QUANTITY => $updatedQtyAtDest, SourceItemInterface::STATUS => SourceItemInterface::STATUS_IN_STOCK]; $connection->update($tableName, $originUpdate, [ - SourceItemInterface::SOURCE_CODE . '=?' => $transfer->getOriginSourceCode(), + SourceItemInterface::SOURCE_CODE . '=?' => $originSourceCode, SourceItemInterface::SKU . '=?' => $transfer->getSku(), ]); $connection->update($tableName, $destUpdate, [ - SourceItemInterface::SOURCE_CODE . '=?' => $transfer->getDestinationSourceCode(), + SourceItemInterface::SOURCE_CODE . '=?' => $destinationSourceCode, SourceItemInterface::SKU . '=?' => $transfer->getSku(), ]); - if ($transfer->getOriginSourceCode() === $this->defaultSourceProvider->getCode()) { + if ($originSourceCode === $this->defaultSourceProvider->getCode()) { $this->setDataToLegacyStockItemCommand->execute($transfer->getSku(), $updatedQtyAtOrigin, $originSourceItemData[SourceItemInterface::STATUS]); - } elseif ($transfer->getDestinationSourceCode() === $this->defaultSourceProvider->getCode()) { + } elseif ($destinationSourceCode === $this->defaultSourceProvider->getCode()) { $this->setDataToLegacyStockItemCommand->execute($transfer->getSku(), $updatedQtyAtDest, SourceItemInterface::STATUS_IN_STOCK); } diff --git a/InventoryCatalog/Model/Source/Validator/PartialTransferValidator.php b/InventoryCatalog/Model/Source/Validator/PartialTransferItemsValidator.php similarity index 58% rename from InventoryCatalog/Model/Source/Validator/PartialTransferValidator.php rename to InventoryCatalog/Model/Source/Validator/PartialTransferItemsValidator.php index 9022a5fee399..3b6a6a66365c 100644 --- a/InventoryCatalog/Model/Source/Validator/PartialTransferValidator.php +++ b/InventoryCatalog/Model/Source/Validator/PartialTransferItemsValidator.php @@ -19,69 +19,47 @@ use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface; use Magento\InventoryCatalogApi\Model\PartialInventoryTransferValidatorInterface; -class PartialTransferValidator implements PartialInventoryTransferValidatorInterface +class PartialTransferItemsValidator implements PartialInventoryTransferValidatorInterface { /** @var ValidationResultFactory */ private $validationResultFactory; - /** @var SourceRepositoryInterface */ - private $sourceRepository; - /** @var GetSourceItemsBySkuAndSourceCodes */ private $getSourceItem; /** - * PartialTransferValidator constructor. * @param ValidationResultFactory $validationResultFactory - * @param SourceRepositoryInterface $sourceRepository * @param GetSourceItemsBySkuAndSourceCodes $getSourceItemsBySkuAndSourceCodes */ public function __construct( ValidationResultFactory $validationResultFactory, - SourceRepositoryInterface $sourceRepository, GetSourceItemsBySkuAndSourceCodes $getSourceItemsBySkuAndSourceCodes - ) - { + ) { $this->validationResultFactory = $validationResultFactory; $this->getSourceItem = $getSourceItemsBySkuAndSourceCodes; - $this->sourceRepository = $sourceRepository; } /** * Validates a partial transfer request. * - * @param PartialInventoryTransferInterface $item + * @param PartialInventoryTransferInterface $transfer * @return ValidationResult */ - public function validate(PartialInventoryTransferInterface $item): ValidationResult + public function validate(PartialInventoryTransferInterface $transfer): ValidationResult { $errors = []; - try { - $this->sourceRepository->get($item->getOriginSourceCode()); - } catch (NoSuchEntityException $e) { - $errors[] = __('Origin source %sourceCode does not exist', ['sourceCode' => $item->getOriginSourceCode()]); - } - - try { - $this->sourceRepository->get($item->getDestinationSourceCode()); - } catch (NoSuchEntityException $e) { - $errors[] = __('Destination source %sourceCode does not exist', ['sourceCode' => $item->getDestinationSourceCode()]); - } - - if ($item->getOriginSourceCode() === $item->getDestinationSourceCode()) { - $errors[] = __('Cannot transfer a source on itself'); - } + foreach ($transfer->getItems() as $item) { + try { + $originSourceItem = $this->getSourceItemBySkuAndSource($item->getSku(), $transfer->getOriginSourceCode()); + if ($originSourceItem->getQuantity() < $item->getQty()) { + $errors[] = __('Requested transfer amount for sku %sku is not available', ['sku' => $item->getSku()]); + } - try { - $originSourceItem = $this->getSourceItemBySkuAndSource($item->getSku(), $item->getOriginSourceCode()); - if ($originSourceItem->getQuantity() < $item->getQty()) { - $errors[] = __('Requested transfer amount for sku %sku is not available', $item->getSku()); + $this->getSourceItemBySkuAndSource($item->getSku(), $transfer->getOriginSourceCode()); + } catch (NoSuchEntityException $e) { + $errors[] = __('%message', ['message' => $e->getMessage()]); } - - $this->getSourceItemBySkuAndSource($item->getSku(), $item->getOriginSourceCode()); - } catch (NoSuchEntityException $e) { - $errors[] = $e->getMessage(); } return $this->validationResultFactory->create(['errors' => $errors]); diff --git a/InventoryCatalog/Model/Source/Validator/PartialTransferSourceValidator.php b/InventoryCatalog/Model/Source/Validator/PartialTransferSourceValidator.php new file mode 100644 index 000000000000..3cc2403d9524 --- /dev/null +++ b/InventoryCatalog/Model/Source/Validator/PartialTransferSourceValidator.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalog\Model\Source\Validator; + +use Magento\Framework\Validation\ValidationResult; +use Magento\Framework\Validation\ValidationResultFactory; +use Magento\Framework\Api\SearchCriteriaBuilder; + +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryApi\Api\SourceRepositoryInterface; +use Magento\InventoryApi\Api\SourceItemRepositoryInterface; +use Magento\InventoryCatalog\Model\GetSourceItemsBySkuAndSourceCodes; +use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface; +use Magento\InventoryCatalogApi\Model\PartialInventoryTransferValidatorInterface; + +class PartialTransferSourceValidator implements PartialInventoryTransferValidatorInterface +{ + /** @var ValidationResultFactory */ + private $validationResultFactory; + + /** @var SourceRepositoryInterface */ + private $sourceRepository; + + /** + * @param ValidationResultFactory $validationResultFactory + * @param SourceRepositoryInterface $sourceRepository + */ + public function __construct( + ValidationResultFactory $validationResultFactory, + SourceRepositoryInterface $sourceRepository + ) { + $this->validationResultFactory = $validationResultFactory; + $this->sourceRepository = $sourceRepository; + } + + /** + * Validates a partial transfer request. + * + * @param PartialInventoryTransferInterface $transfer + * @return ValidationResult + */ + public function validate(PartialInventoryTransferInterface $transfer): ValidationResult + { + $errors = []; + + try { + $this->sourceRepository->get($transfer->getOriginSourceCode()); + } catch (NoSuchEntityException $e) { + $errors[] = __('Origin source %sourceCode does not exist', ['sourceCode' => $transfer->getOriginSourceCode()]); + } + + try { + $this->sourceRepository->get($transfer->getDestinationSourceCode()); + } catch (NoSuchEntityException $e) { + $errors[] = __('Destination source %sourceCode does not exist', ['sourceCode' => $transfer->getDestinationSourceCode()]); + } + + if ($transfer->getOriginSourceCode() === $transfer->getDestinationSourceCode()) { + $errors[] = __('Cannot transfer a source on itself'); + } + + return $this->validationResultFactory->create(['errors' => $errors]); + } +} \ No newline at end of file diff --git a/InventoryCatalog/Test/Api/Bulk/PartialInventoryTransferTest.php b/InventoryCatalog/Test/Api/Bulk/PartialInventoryTransferTest.php index 66028a1839c2..3b6f05846c4f 100644 --- a/InventoryCatalog/Test/Api/Bulk/PartialInventoryTransferTest.php +++ b/InventoryCatalog/Test/Api/Bulk/PartialInventoryTransferTest.php @@ -8,16 +8,19 @@ namespace Magento\InventoryCatalog\Test\Api\Bulk; use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Webapi\Exception; use Magento\Framework\Webapi\Rest\Request; use Magento\InventoryApi\Api\Data\SourceItemInterface; use Magento\InventoryApi\Api\SourceItemRepositoryInterface; use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface; +use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferItemInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\WebapiAbstract; class PartialInventoryTransferTest extends WebapiAbstract { const RESOURCE_PATH = '/V1/inventory/bulk-partial-source-transfer'; + const VALIDATION_FAIL_MESSAGE = 'Transfer validation failed'; /** @var SourceItemRepositoryInterface */ private $sourceItemRepository; @@ -45,7 +48,7 @@ public function testValidTransfer() ] ]; - $this->_webApiCall($serviceInfo, ['items' => [$this->getTransferItem('SKU-1', 1, 'eu-3', 'eu-2')]]); + $this->_webApiCall($serviceInfo, ['transfer' => $this->getTransferItem('SKU-1', 1, 'eu-3', 'eu-2')]); $originSourceItem = $this->getSourceItem('SKU-1', 'eu-3'); $destinationSourceItem = $this->getSourceItem('SKU-1', 'eu-2'); @@ -72,11 +75,25 @@ public function testInvalidTransferOrigin() ] ]; - $result = $this->_webApiCall($serviceInfo, ['items' => [$this->getTransferItem('SKU-1', 1, 'eu-999', 'eu-2')]]); - $this->assertEquals(1, count($result)); - - $destinationItem = array_shift($result); - $this->assertEquals(3, $destinationItem[SourceItemInterface::QUANTITY]); + $body = ['transfer' => $this->getTransferItem('SKU-1', 1, 'eu-999', 'eu-2')]; + $expectedError = [ + 'message' => self::VALIDATION_FAIL_MESSAGE, + 'errors' => [ + [ + 'message' => 'Origin source %sourceCode does not exist', + 'parameters' => [ + 'sourceCode' => 'eu-999' + ] + ], + [ + 'message' => '%message', + 'parameters' => [ + 'message' => 'Source item for SKU-1 and eu-999 does not exist' + ] + ] + ] + ]; + $this->webApiCallWithException($serviceInfo, $body, $expectedError); } /** @@ -93,11 +110,19 @@ public function testInvalidTransferDestination() ] ]; - $result = $this->_webApiCall($serviceInfo, ['items' => [$this->getTransferItem('SKU-1', 1, 'eu-3', 'eu-999')]]); - $this->assertEquals(1, count($result)); - - $originItem = array_shift($result); - $this->assertEquals(10, $originItem[SourceItemInterface::QUANTITY]); + $body = ['transfer' => $this->getTransferItem('SKU-1', 1, 'eu-3', 'eu-999')]; + $expectedError = [ + 'message' => self::VALIDATION_FAIL_MESSAGE, + 'errors' => [ + [ + 'message' => 'Destination source %sourceCode does not exist', + 'parameters' => [ + 'sourceCode' => 'eu-999' + ] + ] + ] + ]; + $this->webApiCallWithException($serviceInfo, $body, $expectedError); } /** @@ -114,11 +139,17 @@ public function testInvalidTransferOriginAndDestinationAreTheSame() ] ]; - $result = $this->_webApiCall($serviceInfo, ['items' => [$this->getTransferItem('SKU-1', 1, 'eu-3', 'eu-3')]]); - $this->assertEquals(1, count($result)); - - $originItem = array_shift($result); - $this->assertEquals(10, $originItem[SourceItemInterface::QUANTITY]); + $body = ['transfer' => $this->getTransferItem('SKU-1', 1, 'eu-3', 'eu-3')]; + $expectedError = [ + 'message' => self::VALIDATION_FAIL_MESSAGE, + 'errors' => [ + [ + 'message' => 'Cannot transfer a source on itself', + 'parameters' => [] + ] + ] + ]; + $this->webApiCallWithException($serviceInfo, $body, $expectedError); } /** @@ -135,17 +166,19 @@ public function testInvalidTransferQuantityGreaterThanAvailable() ] ]; - $this->_webApiCall($serviceInfo, ['items' => [$this->getTransferItem('SKU-1', 100, 'eu-3', 'eu-2')]]); - - $originSourceItem = $this->getSourceItem('SKU-1', 'eu-3'); - $destinationSourceItem = $this->getSourceItem('SKU-1', 'eu-2'); - - if ($originSourceItem === null || $destinationSourceItem === null) { - $this->fail('Both source items should exist.'); - } - - $this->assertEquals(10, $originSourceItem->getQuantity()); - $this->assertEquals(3, $destinationSourceItem->getQuantity()); + $body = ['transfer' => $this->getTransferItem('SKU-1', 100, 'eu-3', 'eu-2')]; + $expectedError = [ + 'message' => self::VALIDATION_FAIL_MESSAGE, + 'errors' => [ + [ + 'message' => 'Requested transfer amount for sku %sku is not available', + 'parameters' => [ + 'sku' => 'SKU-1' + ] + ] + ] + ]; + $this->webApiCallWithException($serviceInfo, $body, $expectedError); } /** @@ -158,13 +191,30 @@ public function testInvalidTransferQuantityGreaterThanAvailable() private function getTransferItem(string $sku, float $qty, string $origin, string $destination): array { return [ - PartialInventoryTransferInterface::SKU => $sku, - PartialInventoryTransferInterface::QTY => $qty, + PartialInventoryTransferInterface::ITEMS => [ + [PartialInventoryTransferItemInterface::SKU => $sku, PartialInventoryTransferItemInterface::QTY => $qty] + ], PartialInventoryTransferInterface::ORIGIN_SOURCE_CODE => $origin, PartialInventoryTransferInterface::DESTINATION_SOURCE_CODE => $destination ]; } + /** + * @param array $serviceInfo + * @param array $data + * @param array $expectedError + */ + private function webApiCallWithException(array $serviceInfo, array $data, array $expectedError): void + { + try { + $this->_webApiCall($serviceInfo, $data); + $this->fail('An exception is expected but not thrown.'); + } catch (\Exception $e) { + self::assertEquals($expectedError, $this->processRestExceptionResult($e)); + self::assertEquals(Exception::HTTP_BAD_REQUEST, $e->getCode()); + } + } + /** * @param string $sku * @param string $sourceCode diff --git a/InventoryCatalog/etc/di.xml b/InventoryCatalog/etc/di.xml index 967574f9f040..3ad1d7f30aa6 100644 --- a/InventoryCatalog/etc/di.xml +++ b/InventoryCatalog/etc/di.xml @@ -107,8 +107,8 @@ type="Magento\InventoryCatalog\Model\BulkPartialInventoryTransfer"/> <preference for="Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface" type="Magento\InventoryCatalog\Model\PartialInventoryTransfer"/> - <preference for="Magento\InventoryCatalogApi\Model\PartialInventoryTransferValidatorInterface" - type="Magento\InventoryCatalog\Model\Source\Validator\PartialTransferValidator"/> + <preference for="Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferItemInterface" + type="Magento\InventoryCatalog\Model\PartialInventoryTransferItem"/> <type name="\Magento\InventoryCatalogApi\Model\BulkSourceAssignValidatorChain"> <arguments> @@ -134,6 +134,17 @@ </argument> </arguments> </type> + <type name="\Magento\InventoryCatalogApi\Model\PartialInventoryTransferValidatorChain"> + <arguments> + <argument name="validators" xsi:type="array"> + <item name="sources" + xsi:type="object">Magento\InventoryCatalog\Model\Source\Validator\PartialTransferSourceValidator</item> + <item name="items" + xsi:type="object">Magento\InventoryCatalog\Model\Source\Validator\PartialTransferItemsValidator</item> + </argument> + </arguments> + </type> + <!-- Negative Min Quantity Threshold for Backorders --> <type name="Magento\Catalog\Controller\Adminhtml\Product\Initialization\StockDataFilter"> <plugin name="allow_negative_min_qty" diff --git a/InventoryCatalogApi/Api/BulkPartialInventoryTransferInterface.php b/InventoryCatalogApi/Api/BulkPartialInventoryTransferInterface.php index d7d5e2ce9c17..7e4ef94497be 100644 --- a/InventoryCatalogApi/Api/BulkPartialInventoryTransferInterface.php +++ b/InventoryCatalogApi/Api/BulkPartialInventoryTransferInterface.php @@ -12,8 +12,9 @@ interface BulkPartialInventoryTransferInterface /** * Run bulk partial inventory transfer for specified items. * - * @param \Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface[] $items + * @param \Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface $transfer * @return \Magento\InventoryApi\Api\Data\SourceItemInterface[] + * @throws \Magento\Framework\Validation\ValidationException */ - public function execute(array $items): array; + public function execute($transfer): array; } \ No newline at end of file diff --git a/InventoryCatalogApi/Api/Data/PartialInventoryTransferInterface.php b/InventoryCatalogApi/Api/Data/PartialInventoryTransferInterface.php index e2a374c06029..d14832ef6888 100644 --- a/InventoryCatalogApi/Api/Data/PartialInventoryTransferInterface.php +++ b/InventoryCatalogApi/Api/Data/PartialInventoryTransferInterface.php @@ -16,30 +16,19 @@ */ interface PartialInventoryTransferInterface extends \Magento\Framework\Api\ExtensibleDataInterface { - const SKU = 'sku'; - const QTY = 'qty'; - const ORIGIN_SOURCE_CODE = 'origin_source_code'; + const ITEMS = 'items'; + const ORIGIN_SOURCE_CODE = 'origin_source_code'; const DESTINATION_SOURCE_CODE = 'destination_source_code'; /** - * @return string - */ - public function getSku(): string; - - /** - * @param string $sku - */ - public function setSku(string $sku): void; - - /** - * @return float + * @return \Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferItemInterface[] */ - public function getQty(): float; + public function getItems(): array; /** - * @param float $qty + * @param \Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferItemInterface[] $items */ - public function setQty(float $qty): void; + public function setItems(array $items): void; /** * @return string diff --git a/InventoryCatalogApi/Api/Data/PartialInventoryTransferItemInterface.php b/InventoryCatalogApi/Api/Data/PartialInventoryTransferItemInterface.php new file mode 100644 index 000000000000..8856765aba83 --- /dev/null +++ b/InventoryCatalogApi/Api/Data/PartialInventoryTransferItemInterface.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalogApi\Api\Data; + +/** + * @api + */ +interface PartialInventoryTransferItemInterface +{ + const SKU = 'sku'; + const QTY = 'qty'; + + /** + * @return string + */ + public function getSku(): string; + + /** + * @param string $sku + */ + public function setSku(string $sku): void; + + /** + * @return float + */ + public function getQty(): float; + + /** + * @param float $qty + */ + public function setQty(float $qty): void; +} \ No newline at end of file diff --git a/InventoryCatalogApi/Model/PartialInventoryTransferValidatorChain.php b/InventoryCatalogApi/Model/PartialInventoryTransferValidatorChain.php new file mode 100644 index 000000000000..77cd1b585717 --- /dev/null +++ b/InventoryCatalogApi/Model/PartialInventoryTransferValidatorChain.php @@ -0,0 +1,69 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalogApi\Model; + +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Validation\ValidationResult; +use Magento\Framework\Validation\ValidationResultFactory; +use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface; + +/** + * Chain of validators. Extension point for new validators via di configuration + * + * @api + */ +class PartialInventoryTransferValidatorChain implements PartialInventoryTransferValidatorInterface +{ + /** + * @var ValidationResultFactory + */ + private $validationResultFactory; + + /** + * @var PartialInventoryTransferValidatorInterface[] + */ + private $validators; + + /** + * @param ValidationResultFactory $validationResultFactory + * @param PartialInventoryTransferValidatorInterface[] $validators + * @throws LocalizedException + * @SuppressWarnings(PHPMD.LongVariable) + */ + public function __construct( + ValidationResultFactory $validationResultFactory, + array $validators = [] + ) { + $this->validationResultFactory = $validationResultFactory; + + foreach ($validators as $validator) { + if (!$validator instanceof PartialInventoryTransferValidatorInterface) { + throw new LocalizedException( + __('Source Validator must implement PartialInventoryTransferValidatorInterface.') + ); + } + } + $this->validators = $validators; + } + + /** + * @inheritdoc + */ + public function validate(PartialInventoryTransferInterface $transfer): ValidationResult + { + $errors = []; + foreach ($this->validators as $validator) { + $validationResult = $validator->validate($transfer); + + if (!$validationResult->isValid()) { + $errors = array_merge($errors, $validationResult->getErrors()); + } + } + return $this->validationResultFactory->create(['errors' => $errors]); + } +} diff --git a/InventoryCatalogApi/Model/PartialInventoryTransferValidatorInterface.php b/InventoryCatalogApi/Model/PartialInventoryTransferValidatorInterface.php index 356cbd3f18cb..924296263a5a 100644 --- a/InventoryCatalogApi/Model/PartialInventoryTransferValidatorInterface.php +++ b/InventoryCatalogApi/Model/PartialInventoryTransferValidatorInterface.php @@ -15,8 +15,8 @@ interface PartialInventoryTransferValidatorInterface /** * Validates a partial transfer request. * - * @param PartialInventoryTransferInterface $item + * @param PartialInventoryTransferInterface $transfer * @return ValidationResult */ - public function validate(PartialInventoryTransferInterface $item): ValidationResult; + public function validate(PartialInventoryTransferInterface $transfer): ValidationResult; } \ No newline at end of file diff --git a/InventoryCatalogApi/etc/di.xml b/InventoryCatalogApi/etc/di.xml index 9f0e66a2a4ab..60beb25a0d9d 100644 --- a/InventoryCatalogApi/etc/di.xml +++ b/InventoryCatalogApi/etc/di.xml @@ -13,4 +13,6 @@ type="Magento\InventoryCatalogApi\Model\BulkSourceUnassignValidatorChain" /> <preference for="Magento\InventoryCatalogApi\Model\BulkInventoryTransferValidatorInterface" type="Magento\InventoryCatalogApi\Model\BulkInventoryTransferValidatorChain" /> + <preference for="Magento\InventoryCatalogApi\Model\PartialInventoryTransferValidatorInterface" + type="Magento\InventoryCatalogApi\Model\PartialInventoryTransferValidatorChain"/> </config> \ No newline at end of file From 84e22e2ac27c70df1a532268a914eebdfde47baa Mon Sep 17 00:00:00 2001 From: Riccardo Ugolini <riccardo.ugolini@magespecialist.it> Date: Tue, 9 Apr 2019 10:59:56 +0200 Subject: [PATCH 091/231] changed logich for mysql 5.6 --- .../Model/GetOrderWithBrokenReservation.php | 45 ++++++++++++++----- ...nsTotOrder.php => GetReservationsList.php} | 16 +++---- ...nsTotOrder.php => GetListReservations.php} | 11 +---- 3 files changed, 43 insertions(+), 29 deletions(-) rename InventoryReservations/Model/{GetReservationsTotOrder.php => GetReservationsList.php} (54%) rename InventoryReservations/Model/ResourceModel/{GetListReservationsTotOrder.php => GetListReservations.php} (71%) diff --git a/InventoryReservations/Model/GetOrderWithBrokenReservation.php b/InventoryReservations/Model/GetOrderWithBrokenReservation.php index 224d7eb179d4..88987fd2abb0 100644 --- a/InventoryReservations/Model/GetOrderWithBrokenReservation.php +++ b/InventoryReservations/Model/GetOrderWithBrokenReservation.php @@ -7,31 +7,39 @@ namespace Magento\InventoryReservations\Model; +use Magento\Framework\Serialize\Serializer\Json; use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Model\ResourceModel\Order\Collection; class GetOrderWithBrokenReservation { - /** - * @var GetReservationsTotOrder - */ - private $getReservationsTotOrder; /** * @var GetOrderInFinalState */ private $getOrderInFinalState; + /** + * @var GetReservationsList + */ + private $getReservationsList; + /** + * @var Json + */ + private $json; /** * GetOrderWithBrokenReservation constructor. - * @param GetReservationsTotOrder $getReservationsTotOrder * @param GetOrderInFinalState $getOrderInFinalState + * @param GetReservationsList $getReservationsList + * @param Json $json */ public function __construct( - GetReservationsTotOrder $getReservationsTotOrder, - GetOrderInFinalState $getOrderInFinalState + GetOrderInFinalState $getOrderInFinalState, + GetReservationsList $getReservationsList, + Json $json ) { - $this->getReservationsTotOrder = $getReservationsTotOrder; $this->getOrderInFinalState = $getOrderInFinalState; + $this->getReservationsList = $getReservationsList; + $this->json = $json; } /** @@ -40,12 +48,25 @@ public function __construct( public function execute(): array { /** @var array $orderListReservations */ - $orderListReservations = $this->getReservationsTotOrder->getListReservationsTotOrder(); + $allReservations = $this->getReservationsList->getListReservationsTotOrder(); + + $result = []; + foreach ($allReservations as $reservation){ + /** @var array $metadata */ + $metadata = $this->json->unserialize($reservation['metadata']); + $objectId = $metadata['object_id']; + if(!array_key_exists($objectId, $result)) { + $result[$objectId] = .0; + } + $result[$objectId] += (float)$reservation['quantity']; + } + $result = array_filter($result); + if(count($result) === 0){ + return []; + } - $brokenReservation = array_column($orderListReservations, 'ReservationTot', 'OrderId'); - $orderIds = array_keys($brokenReservation); /** @var Collection $orders */ - $orders = $this->getOrderInFinalState->execute($orderIds); + $orders = $this->getOrderInFinalState->execute(array_keys($result)); return $orders->getItems(); } } diff --git a/InventoryReservations/Model/GetReservationsTotOrder.php b/InventoryReservations/Model/GetReservationsList.php similarity index 54% rename from InventoryReservations/Model/GetReservationsTotOrder.php rename to InventoryReservations/Model/GetReservationsList.php index 6af7d4ed184d..cc852127fe3c 100644 --- a/InventoryReservations/Model/GetReservationsTotOrder.php +++ b/InventoryReservations/Model/GetReservationsList.php @@ -7,23 +7,23 @@ namespace Magento\InventoryReservations\Model; -use Magento\InventoryReservations\Model\ResourceModel\GetListReservationsTotOrder; +use Magento\InventoryReservations\Model\ResourceModel\GetListReservations; -class GetReservationsTotOrder +class GetReservationsList { /** - * @var GetListReservationsTotOrder + * @var GetListReservations */ - private $getListReservationsTotOrder; + private $getListReservations; /** * GetReservationsTotOrder constructor. - * @param GetListReservationsTotOrder $getListReservationsTotOrder + * @param GetListReservations $getListReservations */ public function __construct ( - GetListReservationsTotOrder $getListReservationsTotOrder + GetListReservations $getListReservations ) { - $this->getListReservationsTotOrder = $getListReservationsTotOrder; + $this->getListReservations = $getListReservations; } /** @@ -31,6 +31,6 @@ public function __construct ( */ public function getListReservationsTotOrder(): array { - return $this->getListReservationsTotOrder->execute(); + return $this->getListReservations->execute(); } } diff --git a/InventoryReservations/Model/ResourceModel/GetListReservationsTotOrder.php b/InventoryReservations/Model/ResourceModel/GetListReservations.php similarity index 71% rename from InventoryReservations/Model/ResourceModel/GetListReservationsTotOrder.php rename to InventoryReservations/Model/ResourceModel/GetListReservations.php index 50edf5e229a3..643c78e54fb5 100644 --- a/InventoryReservations/Model/ResourceModel/GetListReservationsTotOrder.php +++ b/InventoryReservations/Model/ResourceModel/GetListReservations.php @@ -9,7 +9,7 @@ use Magento\Framework\App\ResourceConnection; -class GetListReservationsTotOrder +class GetListReservations { /** * @var ResourceConnection @@ -33,14 +33,7 @@ public function execute(): array $qry = $connection ->select() - ->from($tableName, - [ - 'OrderId' => new \Zend_Db_Expr("CAST(JSON_EXTRACT(metadata, '$.object_id') as UNSIGNED)"), - 'ReservationTot' => 'sum(quantity)' - ] - ) - ->group('OrderId') - ->having('ReservationTot != ?', 0); + ->from($tableName); return $connection->fetchAll($qry); } } From 8f463e5f6785a9e0e56121957531b9b75d317b5a Mon Sep 17 00:00:00 2001 From: Riccardo Ugolini <riccardo.ugolini@magespecialist.it> Date: Tue, 9 Apr 2019 11:55:27 +0200 Subject: [PATCH 092/231] phpmd, theRick fix --- .../Command/ReservationInconsistency.php | 1 - .../Model/GetOrderInFinalState.php | 17 +++++++----- .../Model/GetOrderWithBrokenReservation.php | 27 +++++++++---------- .../Model/GetReservationsList.php | 1 - InventoryReservations/Model/Reservation.php | 2 -- .../ResourceModel/GetListReservations.php | 1 - 6 files changed, 23 insertions(+), 26 deletions(-) diff --git a/InventoryReservations/Command/ReservationInconsistency.php b/InventoryReservations/Command/ReservationInconsistency.php index 64a76521045c..937d17357195 100644 --- a/InventoryReservations/Command/ReservationInconsistency.php +++ b/InventoryReservations/Command/ReservationInconsistency.php @@ -22,7 +22,6 @@ class ReservationInconsistency extends Command private $getOrderWithBrokenReservation; /** - * ReservationInconsistency constructor. * @param GetOrderWithBrokenReservation $getOrderWithBrokenReservation */ public function __construct( diff --git a/InventoryReservations/Model/GetOrderInFinalState.php b/InventoryReservations/Model/GetOrderInFinalState.php index df2787ca6d8e..d00f5576f55c 100644 --- a/InventoryReservations/Model/GetOrderInFinalState.php +++ b/InventoryReservations/Model/GetOrderInFinalState.php @@ -9,25 +9,28 @@ use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Api\SearchCriteriaInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\OrderRepositoryInterface; use Magento\Sales\Model\Order; class GetOrderInFinalState { /** - * @var \Magento\Sales\Api\OrderRepositoryInterface + * @var OrderRepositoryInterface */ private $orderRepository; + /** * @var SearchCriteriaBuilder */ private $searchCriteriaBuilder; /** - * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository + * @param OrderRepositoryInterface $orderRepository * @param SearchCriteriaBuilder $searchCriteriaBuilder */ public function __construct ( - \Magento\Sales\Api\OrderRepositoryInterface $orderRepository, + OrderRepositoryInterface $orderRepository, SearchCriteriaBuilder $searchCriteriaBuilder ) { $this->orderRepository = $orderRepository; @@ -36,9 +39,9 @@ public function __construct ( /** * @param array $orderIds - * @return \Magento\Sales\Api\Data\OrderSearchResultInterface + * @return OrderInterface[] */ - public function execute(array $orderIds): \Magento\Sales\Api\Data\OrderSearchResultInterface + public function execute(array $orderIds): array { /** @var SearchCriteriaInterface $filter */ $filter = $this->searchCriteriaBuilder @@ -50,7 +53,7 @@ public function execute(array $orderIds): \Magento\Sales\Api\Data\OrderSearchRes ], 'in') ->create(); - return $this->orderRepository->getList($filter); + $orderSearchResult = $this->orderRepository->getList($filter); + return $orderSearchResult->getItems(); } } - diff --git a/InventoryReservations/Model/GetOrderWithBrokenReservation.php b/InventoryReservations/Model/GetOrderWithBrokenReservation.php index 88987fd2abb0..e7db7ad81c55 100644 --- a/InventoryReservations/Model/GetOrderWithBrokenReservation.php +++ b/InventoryReservations/Model/GetOrderWithBrokenReservation.php @@ -7,9 +7,8 @@ namespace Magento\InventoryReservations\Model; -use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\Serialize\SerializerInterface; use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Model\ResourceModel\Order\Collection; class GetOrderWithBrokenReservation { @@ -17,29 +16,30 @@ class GetOrderWithBrokenReservation * @var GetOrderInFinalState */ private $getOrderInFinalState; + /** * @var GetReservationsList */ private $getReservationsList; + /** - * @var Json + * @var SerializerInterface */ - private $json; + private $serialize; /** - * GetOrderWithBrokenReservation constructor. * @param GetOrderInFinalState $getOrderInFinalState * @param GetReservationsList $getReservationsList - * @param Json $json + * @param SerializerInterface $serialize */ public function __construct( GetOrderInFinalState $getOrderInFinalState, GetReservationsList $getReservationsList, - Json $json + SerializerInterface $serialize ) { $this->getOrderInFinalState = $getOrderInFinalState; $this->getReservationsList = $getReservationsList; - $this->json = $json; + $this->serialize = $serialize; } /** @@ -53,20 +53,19 @@ public function execute(): array $result = []; foreach ($allReservations as $reservation){ /** @var array $metadata */ - $metadata = $this->json->unserialize($reservation['metadata']); + $metadata = $this->serialize->unserialize($reservation['metadata']); $objectId = $metadata['object_id']; if(!array_key_exists($objectId, $result)) { - $result[$objectId] = .0; + $result[$objectId] = (float) 0; } $result[$objectId] += (float)$reservation['quantity']; } $result = array_filter($result); - if(count($result) === 0){ + if(empty($result)){ return []; } - /** @var Collection $orders */ - $orders = $this->getOrderInFinalState->execute(array_keys($result)); - return $orders->getItems(); + /** @var OrderInterface[] $orders */ + return $this->getOrderInFinalState->execute(array_keys($result)); } } diff --git a/InventoryReservations/Model/GetReservationsList.php b/InventoryReservations/Model/GetReservationsList.php index cc852127fe3c..6c02cf10ac61 100644 --- a/InventoryReservations/Model/GetReservationsList.php +++ b/InventoryReservations/Model/GetReservationsList.php @@ -17,7 +17,6 @@ class GetReservationsList private $getListReservations; /** - * GetReservationsTotOrder constructor. * @param GetListReservations $getListReservations */ public function __construct ( diff --git a/InventoryReservations/Model/Reservation.php b/InventoryReservations/Model/Reservation.php index 8b577feab847..3d479530ed72 100644 --- a/InventoryReservations/Model/Reservation.php +++ b/InventoryReservations/Model/Reservation.php @@ -16,8 +16,6 @@ */ class Reservation implements ReservationInterface { - public const TABLE_NAME = 'inventory_reservation'; - /** * @var int|null */ diff --git a/InventoryReservations/Model/ResourceModel/GetListReservations.php b/InventoryReservations/Model/ResourceModel/GetListReservations.php index 643c78e54fb5..ab9b64e0ade6 100644 --- a/InventoryReservations/Model/ResourceModel/GetListReservations.php +++ b/InventoryReservations/Model/ResourceModel/GetListReservations.php @@ -17,7 +17,6 @@ class GetListReservations private $resourceConnection; /** - * GetListReservationsTotOrder constructor. * @param ResourceConnection $resourceConnection */ public function __construct ( From be3f5a682df0c14c3a4d97fedd6c542dd324a15d Mon Sep 17 00:00:00 2001 From: Riccardo Ugolini <riccardo.ugolini@magespecialist.it> Date: Tue, 9 Apr 2019 16:45:25 +0200 Subject: [PATCH 093/231] code refactor, created InventoryReservationCli --- .../ShowInconsistencyOrderComplete.php | 23 +++++-- .../Model/GetOrderInFinalState.php | 2 +- .../Model/GetOrderWithBrokenReservation.php | 18 +----- .../Model/GetReservationsList.php | 4 +- .../ResourceModel/GetListReservations.php | 2 +- .../GetListReservationsTotOrdersTest.php | 64 +++++++++++++++++++ .../_fixtures/broken_reservation.php | 0 .../_fixtures/order_with_reservation.php | 0 InventoryReservationCli/composer.json | 33 ++++++++++ InventoryReservationCli/etc/di.xml | 16 +++++ InventoryReservationCli/etc/module.xml | 11 ++++ InventoryReservationCli/registration.php | 7 ++ .../GetListReservationsTotOrdersTest.php | 50 --------------- InventoryReservations/etc/di.xml | 7 -- 14 files changed, 155 insertions(+), 82 deletions(-) rename InventoryReservations/Command/ReservationInconsistency.php => InventoryReservationCli/Command/ShowInconsistencyOrderComplete.php (67%) rename {InventoryReservations => InventoryReservationCli}/Model/GetOrderInFinalState.php (96%) rename {InventoryReservations => InventoryReservationCli}/Model/GetOrderWithBrokenReservation.php (75%) rename {InventoryReservations => InventoryReservationCli}/Model/GetReservationsList.php (83%) rename {InventoryReservations => InventoryReservationCli}/Model/ResourceModel/GetListReservations.php (93%) create mode 100644 InventoryReservationCli/Test/Integration/Model/GetListReservationsTotOrdersTest.php rename {InventoryReservations => InventoryReservationCli}/Test/Integration/_fixtures/broken_reservation.php (100%) rename {InventoryReservations => InventoryReservationCli}/Test/Integration/_fixtures/order_with_reservation.php (100%) create mode 100644 InventoryReservationCli/composer.json create mode 100644 InventoryReservationCli/etc/di.xml create mode 100644 InventoryReservationCli/etc/module.xml create mode 100644 InventoryReservationCli/registration.php delete mode 100644 InventoryReservations/Test/Integration/Model/GetListReservationsTotOrdersTest.php diff --git a/InventoryReservations/Command/ReservationInconsistency.php b/InventoryReservationCli/Command/ShowInconsistencyOrderComplete.php similarity index 67% rename from InventoryReservations/Command/ReservationInconsistency.php rename to InventoryReservationCli/Command/ShowInconsistencyOrderComplete.php index 937d17357195..288ac8e9d4df 100644 --- a/InventoryReservations/Command/ReservationInconsistency.php +++ b/InventoryReservationCli/Command/ShowInconsistencyOrderComplete.php @@ -5,30 +5,38 @@ */ declare(strict_types=1); -namespace Magento\InventoryReservations\Command; +namespace Magento\InventoryReservationCli\Command; -use Magento\InventoryReservations\Model\GetOrderWithBrokenReservation; +use Magento\InventoryReservationCli\Model\GetOrderInFinalState; +use Magento\InventoryReservationCli\Model\GetOrderWithBrokenReservation; use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Model\Order; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -class ReservationInconsistency extends Command +class ShowInconsistencyOrderComplete extends Command { /** * @var GetOrderWithBrokenReservation */ private $getOrderWithBrokenReservation; + /** + * @var GetOrderInFinalState + */ + private $getOrderInFinalState; /** * @param GetOrderWithBrokenReservation $getOrderWithBrokenReservation + * @param GetOrderInFinalState $getOrderInFinalState */ public function __construct( - GetOrderWithBrokenReservation $getOrderWithBrokenReservation + GetOrderWithBrokenReservation $getOrderWithBrokenReservation, + GetOrderInFinalState $getOrderInFinalState ) { - parent::__construct(); $this->getOrderWithBrokenReservation = $getOrderWithBrokenReservation; + $this->getOrderInFinalState = $getOrderInFinalState; + parent::__construct(); } protected function configure() @@ -47,8 +55,11 @@ protected function configure() */ public function execute(InputInterface $input, OutputInterface $output): void { + /** @var array $orderBrokenReservation */ + $orderBrokenReservation = $this->getOrderWithBrokenReservation->execute(); + /** @var OrderInterface[] $orders */ - $orders = $this->getOrderWithBrokenReservation->execute(); + $orders = $this->getOrderInFinalState->execute(array_keys($orderBrokenReservation)); /** @var Order $order */ foreach($orders as $order) { diff --git a/InventoryReservations/Model/GetOrderInFinalState.php b/InventoryReservationCli/Model/GetOrderInFinalState.php similarity index 96% rename from InventoryReservations/Model/GetOrderInFinalState.php rename to InventoryReservationCli/Model/GetOrderInFinalState.php index d00f5576f55c..a486ae78e052 100644 --- a/InventoryReservations/Model/GetOrderInFinalState.php +++ b/InventoryReservationCli/Model/GetOrderInFinalState.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryReservations\Model; +namespace Magento\InventoryReservationCli\Model; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Api\SearchCriteriaInterface; diff --git a/InventoryReservations/Model/GetOrderWithBrokenReservation.php b/InventoryReservationCli/Model/GetOrderWithBrokenReservation.php similarity index 75% rename from InventoryReservations/Model/GetOrderWithBrokenReservation.php rename to InventoryReservationCli/Model/GetOrderWithBrokenReservation.php index e7db7ad81c55..29b9df40c282 100644 --- a/InventoryReservations/Model/GetOrderWithBrokenReservation.php +++ b/InventoryReservationCli/Model/GetOrderWithBrokenReservation.php @@ -5,18 +5,13 @@ */ declare(strict_types=1); -namespace Magento\InventoryReservations\Model; +namespace Magento\InventoryReservationCli\Model; use Magento\Framework\Serialize\SerializerInterface; use Magento\Sales\Api\Data\OrderInterface; class GetOrderWithBrokenReservation { - /** - * @var GetOrderInFinalState - */ - private $getOrderInFinalState; - /** * @var GetReservationsList */ @@ -28,16 +23,13 @@ class GetOrderWithBrokenReservation private $serialize; /** - * @param GetOrderInFinalState $getOrderInFinalState * @param GetReservationsList $getReservationsList * @param SerializerInterface $serialize */ public function __construct( - GetOrderInFinalState $getOrderInFinalState, GetReservationsList $getReservationsList, SerializerInterface $serialize ) { - $this->getOrderInFinalState = $getOrderInFinalState; $this->getReservationsList = $getReservationsList; $this->serialize = $serialize; } @@ -50,6 +42,7 @@ public function execute(): array /** @var array $orderListReservations */ $allReservations = $this->getReservationsList->getListReservationsTotOrder(); + /** @var array $result */ $result = []; foreach ($allReservations as $reservation){ /** @var array $metadata */ @@ -61,11 +54,6 @@ public function execute(): array $result[$objectId] += (float)$reservation['quantity']; } $result = array_filter($result); - if(empty($result)){ - return []; - } - - /** @var OrderInterface[] $orders */ - return $this->getOrderInFinalState->execute(array_keys($result)); + return $result; } } diff --git a/InventoryReservations/Model/GetReservationsList.php b/InventoryReservationCli/Model/GetReservationsList.php similarity index 83% rename from InventoryReservations/Model/GetReservationsList.php rename to InventoryReservationCli/Model/GetReservationsList.php index 6c02cf10ac61..ab1f8a11a60d 100644 --- a/InventoryReservations/Model/GetReservationsList.php +++ b/InventoryReservationCli/Model/GetReservationsList.php @@ -5,9 +5,9 @@ */ declare(strict_types=1); -namespace Magento\InventoryReservations\Model; +namespace Magento\InventoryReservationCli\Model; -use Magento\InventoryReservations\Model\ResourceModel\GetListReservations; +use Magento\InventoryReservationCli\Model\ResourceModel\GetListReservations; class GetReservationsList { diff --git a/InventoryReservations/Model/ResourceModel/GetListReservations.php b/InventoryReservationCli/Model/ResourceModel/GetListReservations.php similarity index 93% rename from InventoryReservations/Model/ResourceModel/GetListReservations.php rename to InventoryReservationCli/Model/ResourceModel/GetListReservations.php index ab9b64e0ade6..7847adf854d6 100644 --- a/InventoryReservations/Model/ResourceModel/GetListReservations.php +++ b/InventoryReservationCli/Model/ResourceModel/GetListReservations.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryReservations\Model\ResourceModel; +namespace Magento\InventoryReservationCli\Model\ResourceModel; use Magento\Framework\App\ResourceConnection; diff --git a/InventoryReservationCli/Test/Integration/Model/GetListReservationsTotOrdersTest.php b/InventoryReservationCli/Test/Integration/Model/GetListReservationsTotOrdersTest.php new file mode 100644 index 000000000000..4cfb66673e17 --- /dev/null +++ b/InventoryReservationCli/Test/Integration/Model/GetListReservationsTotOrdersTest.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservations\Test\Integration\Model; + +use Magento\InventoryReservationCli\Model\GetOrderInFinalState; +use Magento\InventoryReservationCli\Model\GetOrderWithBrokenReservation; +use Magento\Sales\Api\Data\OrderInterface; +use PHPUnit\Framework\TestCase; +use Magento\TestFramework\Helper\Bootstrap; + +class GetListReservationsTotOrdersTest extends TestCase +{ + + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryReservationCli/Test/Integration/_fixtures/order_with_reservation.php + */ + public function testShouldNotFindAnyInconsistency(): void + { + $objectManager = Bootstrap::getObjectManager(); + + /** @var GetOrderWithBrokenReservation $getOrderWithBrokenReservation */ + $getOrderWithBrokenReservation = $objectManager->get(GetOrderWithBrokenReservation::class); + + /** @var GetOrderInFinalState $getOrderInFinalState */ + $getOrderInFinalState = $objectManager->get(GetOrderInFinalState::class); + + /** @var array $result */ + $result = $getOrderWithBrokenReservation->execute(); + + /** @var OrderInterface[] $orders */ + $orders = $getOrderInFinalState->execute(array_keys($result)); + + self::assertSame([], $orders); + } + + /** + * @magentoDataFixture Magento/Sales/_files/order_with_shipping_and_invoice.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryReservationCli/Test/Integration/_fixtures/broken_reservation.php + * @magentoDbIsolation enabled + */ + public function testShouldReturnOneReservationInconsistency(): void + { + $objectManager = Bootstrap::getObjectManager(); + + /** @var GetOrderWithBrokenReservation $getOrderWithBrokenReservation */ + $getOrderWithBrokenReservation = $objectManager->get(GetOrderWithBrokenReservation::class); + + /** @var GetOrderInFinalState $getOrderInFinalState */ + $getOrderInFinalState = $objectManager->get(GetOrderInFinalState::class); + + /** @var array $result */ + $result = $getOrderWithBrokenReservation->execute(); + + /** @var OrderInterface[] $orders */ + $orders = $getOrderInFinalState->execute(array_keys($result)); + + self::assertCount(1, $orders); + } +} diff --git a/InventoryReservations/Test/Integration/_fixtures/broken_reservation.php b/InventoryReservationCli/Test/Integration/_fixtures/broken_reservation.php similarity index 100% rename from InventoryReservations/Test/Integration/_fixtures/broken_reservation.php rename to InventoryReservationCli/Test/Integration/_fixtures/broken_reservation.php diff --git a/InventoryReservations/Test/Integration/_fixtures/order_with_reservation.php b/InventoryReservationCli/Test/Integration/_fixtures/order_with_reservation.php similarity index 100% rename from InventoryReservations/Test/Integration/_fixtures/order_with_reservation.php rename to InventoryReservationCli/Test/Integration/_fixtures/order_with_reservation.php diff --git a/InventoryReservationCli/composer.json b/InventoryReservationCli/composer.json new file mode 100644 index 000000000000..15969d15e2b4 --- /dev/null +++ b/InventoryReservationCli/composer.json @@ -0,0 +1,33 @@ +{ + "name": "magento/inventoryreservationcli", + "description": "", + "require": { + "php": "~5.5.0|~5.6.0|~7.0.0", + + "magento/magento-composer-installer": "*" + }, + "suggest": { + + }, + "type": "magento2-module", + "version": "0.1.0", + "license": [ + + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\InventoryReservationCli\\": "" + } + }, + "extra": { + "map": [ + [ + "*", + "Magento/InventoryReservationCli" + ] + ] + } +} \ No newline at end of file diff --git a/InventoryReservationCli/etc/di.xml b/InventoryReservationCli/etc/di.xml new file mode 100644 index 000000000000..f76bcb5bd69a --- /dev/null +++ b/InventoryReservationCli/etc/di.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:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\Framework\Console\CommandList"> + <arguments> + <argument name="commands" xsi:type="array"> + <item name="inventory_complete_order_inconsistency" xsi:type="object">Magento\InventoryReservationCli\Command\ShowInconsistencyOrderComplete</item> + </argument> + </arguments> + </type> +</config> diff --git a/InventoryReservationCli/etc/module.xml b/InventoryReservationCli/etc/module.xml new file mode 100644 index 000000000000..6c18a8a5cd79 --- /dev/null +++ b/InventoryReservationCli/etc/module.xml @@ -0,0 +1,11 @@ +<?xml version="1.0"?> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + <module name="Magento_InventoryReservationCli" setup_version="0.1.0"> + <sequence> + <module name="Magento_InventoryReservations"/> + <module name="Magento_Sales"/> + + </sequence> + </module> +</config> \ No newline at end of file diff --git a/InventoryReservationCli/registration.php b/InventoryReservationCli/registration.php new file mode 100644 index 000000000000..7807ebab57b6 --- /dev/null +++ b/InventoryReservationCli/registration.php @@ -0,0 +1,7 @@ +<?php + +\Magento\Framework\Component\ComponentRegistrar::register( + \Magento\Framework\Component\ComponentRegistrar::MODULE, + 'Magento_InventoryReservationCli', + __DIR__ +); \ No newline at end of file diff --git a/InventoryReservations/Test/Integration/Model/GetListReservationsTotOrdersTest.php b/InventoryReservations/Test/Integration/Model/GetListReservationsTotOrdersTest.php deleted file mode 100644 index 638db72fc682..000000000000 --- a/InventoryReservations/Test/Integration/Model/GetListReservationsTotOrdersTest.php +++ /dev/null @@ -1,50 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryReservations\Test\Integration\Model; - -use Magento\InventoryReservations\Model\GetOrderWithBrokenReservation; -use PHPUnit\Framework\TestCase; -use Magento\TestFramework\Helper\Bootstrap; - -class GetListReservationsTotOrdersTest extends TestCase -{ - - /** - * @magentoDataFixture ../../../../app/code/Magento/InventoryReservations/Test/Integration/_fixtures/order_with_reservation.php - */ - public function testShouldNotFindAnyInconsistency(): void - { - $objectManager = Bootstrap::getObjectManager(); - - /** @var GetOrderWithBrokenReservation $subject */ - $subject = $objectManager->get(GetOrderWithBrokenReservation::class); - - /** @var array $result */ - $result = $subject->execute(); - - self::assertSame([], $result); - } - - /** - * @magentoDataFixture Magento/Sales/_files/order_with_shipping_and_invoice.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryReservations/Test/Integration/_fixtures/broken_reservation.php - * @magentoDbIsolation enabled - */ - public function testShouldReturnOneReservationInconsistency(): void - { - $objectManager = Bootstrap::getObjectManager(); - - /** @var GetOrderWithBrokenReservation $subject */ - $subject = $objectManager->get(GetOrderWithBrokenReservation::class); - - /** @var array $result */ - $result = $subject->execute(); - - self::assertCount(1, $result); - } -} diff --git a/InventoryReservations/etc/di.xml b/InventoryReservations/etc/di.xml index 2f3479bb7c31..ad75adea7ece 100644 --- a/InventoryReservations/etc/di.xml +++ b/InventoryReservations/etc/di.xml @@ -16,11 +16,4 @@ <argument name="groupConcatMaxLen" xsi:type="number">2000</argument> </arguments> </type> - <type name="Magento\Framework\Console\CommandList"> - <arguments> - <argument name="commands" xsi:type="array"> - <item name="inventory_reservation_inconsistency" xsi:type="object">Magento\InventoryReservations\Command\ReservationInconsistency</item> - </argument> - </arguments> - </type> </config> From 5c8ec2de8ec248554a88232d6b2e64e468813e7f Mon Sep 17 00:00:00 2001 From: Riccardo Ugolini <riccardo.ugolini@magespecialist.it> Date: Tue, 9 Apr 2019 17:00:39 +0200 Subject: [PATCH 094/231] changed command name --- .../Command/ShowInconsistencyOrderComplete.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InventoryReservationCli/Command/ShowInconsistencyOrderComplete.php b/InventoryReservationCli/Command/ShowInconsistencyOrderComplete.php index 288ac8e9d4df..6cb904905093 100644 --- a/InventoryReservationCli/Command/ShowInconsistencyOrderComplete.php +++ b/InventoryReservationCli/Command/ShowInconsistencyOrderComplete.php @@ -42,7 +42,7 @@ public function __construct( protected function configure() { $this - ->setName('inventory:reservation:show-inconsistency') + ->setName('inventory:reservation:complete-order-inconsistency') ->setDescription('Show all reservation inconsistencies for completed orders'); parent::configure(); From 451ebca1fd161a0ba25c29c98677162a42a1b815 Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Wed, 10 Apr 2019 10:28:42 +0200 Subject: [PATCH 095/231] Tests restored --- ...ToLegacyStockItemAtSourceItemsSaveTest.php | 85 +++++++++---------- ...gacyStockStatusAtSourceItemsDeleteTest.php | 85 +++++++++---------- ...ultSourceItemAtLegacyStockItemSaveTest.php | 39 +++++---- 3 files changed, 103 insertions(+), 106 deletions(-) diff --git a/InventoryLegacySynchronization/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php b/InventoryLegacySynchronization/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php index 986b360dfff4..80306d1d2701 100644 --- a/InventoryLegacySynchronization/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php +++ b/InventoryLegacySynchronization/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php @@ -135,48 +135,47 @@ public function testShouldSynchronizeLegacyStock(): void */ public function testShouldSynchronizeLegacyStockAsynchronously(): void { -// $productSku = 'SKU-1'; -// $product = $this->productRepository->get($productSku); -// $productId = $product->getId(); -// $websiteId = 0; -// -// /** @var StockItemCriteriaInterface $legacyStockItemCriteria */ -// $legacyStockItemCriteria = $this->legacyStockItemCriteriaFactory->create(); -// $legacyStockItemCriteria->setProductsFilter($productId); -// $legacyStockItemCriteria->setScopeFilter($websiteId); -// $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); -// self::assertCount(1, $legacyStockItems); -// -// $legacyStockItem = reset($legacyStockItems); -// self::assertTrue($legacyStockItem->getIsInStock()); -// self::assertEquals(5.5, $legacyStockItem->getQty()); -// -// $searchCriteria = $this->searchCriteriaBuilder -// ->addFilter(SourceItemInterface::SKU, $productSku) -// ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) -// ->create(); -// $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); -// self::assertCount(1, $sourceItems); -// -// $sourceItem = reset($sourceItems); -// $sourceItem->setQuantity(20.0); -// $sourceItem->setStatus(SourceItemInterface::STATUS_OUT_OF_STOCK); -// $this->sourceItemsSave->execute($sourceItems); -// -// // Make sure we did not yet synchronized it -// $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); -// $legacyStockItem = current($legacyStockItems); -// self::assertCount(1, $legacyStockItems); -// self::assertTrue($legacyStockItem->getIsInStock()); -// self::assertEquals(5.5, $legacyStockItem->getQty()); -// -// $this->consumer->process(1); -// -// // Check after asynchrnous consumer call -// $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); -// $legacyStockItem = current($legacyStockItems); -// self::assertFalse($legacyStockItem->getIsInStock()); -// self::assertEquals(20, $legacyStockItem->getQty()); - $this->markTestIncomplete('Test is failing due to missing AMQP configuration'); + $productSku = 'SKU-1'; + $product = $this->productRepository->get($productSku); + $productId = $product->getId(); + $websiteId = 0; + + /** @var StockItemCriteriaInterface $legacyStockItemCriteria */ + $legacyStockItemCriteria = $this->legacyStockItemCriteriaFactory->create(); + $legacyStockItemCriteria->setProductsFilter($productId); + $legacyStockItemCriteria->setScopeFilter($websiteId); + $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); + self::assertCount(1, $legacyStockItems); + + $legacyStockItem = reset($legacyStockItems); + self::assertTrue($legacyStockItem->getIsInStock()); + self::assertEquals(5.5, $legacyStockItem->getQty()); + + $searchCriteria = $this->searchCriteriaBuilder + ->addFilter(SourceItemInterface::SKU, $productSku) + ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) + ->create(); + $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); + self::assertCount(1, $sourceItems); + + $sourceItem = reset($sourceItems); + $sourceItem->setQuantity(20.0); + $sourceItem->setStatus(SourceItemInterface::STATUS_OUT_OF_STOCK); + $this->sourceItemsSave->execute($sourceItems); + + // Make sure we did not yet synchronized it + $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); + $legacyStockItem = current($legacyStockItems); + self::assertCount(1, $legacyStockItems); + self::assertTrue($legacyStockItem->getIsInStock()); + self::assertEquals(5.5, $legacyStockItem->getQty()); + + $this->consumer->process(1); + + // Check after asynchrnous consumer call + $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); + $legacyStockItem = current($legacyStockItems); + self::assertFalse($legacyStockItem->getIsInStock()); + self::assertEquals(20, $legacyStockItem->getQty()); } } diff --git a/InventoryLegacySynchronization/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php b/InventoryLegacySynchronization/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php index 5a5ac57f4d36..55fd8392d5fb 100644 --- a/InventoryLegacySynchronization/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php +++ b/InventoryLegacySynchronization/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php @@ -131,48 +131,47 @@ public function testShouldSetOutOfStockOnDelete(): void */ public function testShouldSetOutOfStockOnDeleteAsynchronously(): void { -// $productSku = 'SKU-1'; -// $product = $this->productRepository->get($productSku); -// $productId = $product->getId(); -// $websiteId = 0; -// -// /** @var StockStatusCriteriaInterface $legacyStockStatusCriteria */ -// $legacyStockStatusCriteria = $this->legacyStockStatusCriteriaFactory->create(); -// $legacyStockStatusCriteria->setProductsFilter($productId); -// $legacyStockStatusCriteria->setScopeFilter($websiteId); -// $legacyStockStatuses = $this->legacyStockStatusRepository->getList($legacyStockStatusCriteria)->getItems(); -// self::assertCount(1, $legacyStockStatuses); -// -// $legacyStockStatus = reset($legacyStockStatuses); -// self::assertSame(Status::STATUS_IN_STOCK, $legacyStockStatus->getStockStatus()); -// self::assertSame(5.5, (float) $legacyStockStatus->getQty()); -// -// $searchCriteria = $this->searchCriteriaBuilder -// ->addFilter(SourceItemInterface::SKU, $productSku) -// ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) -// ->create(); -// $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); -// self::assertCount(1, $sourceItems); -// -// $this->sourceItemsDelete->execute($sourceItems); -// -// // Before sync -// $legacyStockStatuses = $this->legacyStockStatusRepository->getList($legacyStockStatusCriteria)->getItems(); -// self::assertCount(1, $legacyStockStatuses); -// -// $legacyStockStatus = reset($legacyStockStatuses); -// self::assertSame(Status::STATUS_IN_STOCK, $legacyStockStatus->getStockStatus()); -// self::assertSame(5.5, (float) $legacyStockStatus->getQty()); -// -// $this->consumer->process(1); -// -// // After sync -// $legacyStockStatuses = $this->legacyStockStatusRepository->getList($legacyStockStatusCriteria)->getItems(); -// self::assertCount(1, $legacyStockStatuses); -// -// $legacyStockStatus = reset($legacyStockStatuses); -// self::assertSame(Status::STATUS_OUT_OF_STOCK, $legacyStockStatus->getStockStatus()); -// self::assertSame(0.0, (float) $legacyStockStatus->getQty()); - $this->markTestIncomplete('Test is failing due to missing AMQP configuration'); + $productSku = 'SKU-1'; + $product = $this->productRepository->get($productSku); + $productId = $product->getId(); + $websiteId = 0; + + /** @var StockStatusCriteriaInterface $legacyStockStatusCriteria */ + $legacyStockStatusCriteria = $this->legacyStockStatusCriteriaFactory->create(); + $legacyStockStatusCriteria->setProductsFilter($productId); + $legacyStockStatusCriteria->setScopeFilter($websiteId); + $legacyStockStatuses = $this->legacyStockStatusRepository->getList($legacyStockStatusCriteria)->getItems(); + self::assertCount(1, $legacyStockStatuses); + + $legacyStockStatus = reset($legacyStockStatuses); + self::assertSame(Status::STATUS_IN_STOCK, $legacyStockStatus->getStockStatus()); + self::assertSame(5.5, (float) $legacyStockStatus->getQty()); + + $searchCriteria = $this->searchCriteriaBuilder + ->addFilter(SourceItemInterface::SKU, $productSku) + ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) + ->create(); + $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); + self::assertCount(1, $sourceItems); + + $this->sourceItemsDelete->execute($sourceItems); + + // Before sync + $legacyStockStatuses = $this->legacyStockStatusRepository->getList($legacyStockStatusCriteria)->getItems(); + self::assertCount(1, $legacyStockStatuses); + + $legacyStockStatus = reset($legacyStockStatuses); + self::assertSame(Status::STATUS_IN_STOCK, $legacyStockStatus->getStockStatus()); + self::assertSame(5.5, (float) $legacyStockStatus->getQty()); + + $this->consumer->process(1); + + // After sync + $legacyStockStatuses = $this->legacyStockStatusRepository->getList($legacyStockStatusCriteria)->getItems(); + self::assertCount(1, $legacyStockStatuses); + + $legacyStockStatus = reset($legacyStockStatuses); + self::assertSame(Status::STATUS_OUT_OF_STOCK, $legacyStockStatus->getStockStatus()); + self::assertSame(0.0, (float) $legacyStockStatus->getQty()); } } diff --git a/InventoryLegacySynchronization/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php b/InventoryLegacySynchronization/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php index 551a577aa028..1fbaa646ac62 100644 --- a/InventoryLegacySynchronization/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php +++ b/InventoryLegacySynchronization/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php @@ -75,26 +75,25 @@ public function testSaveLegacyStockItemAssignedToDefaultSource(): void */ public function testSaveLegacyStockItemAssignedToDefaultSourceAsynchronously(): void { -// $stockItem = $this->stockRegistry->getStockItemBySku('SKU-1'); -// $stockItem->setQty(10); -// $this->stockRegistry->updateStockItemBySku('SKU-1', $stockItem); -// -// $defaultSourceItem = $this->getDefaultSourceItemBySku->execute('SKU-1'); -// self::assertEquals( -// 5.5, -// $defaultSourceItem->getQuantity(), -// 'Source item was update synchronously even if asynchronous operation was requested' -// ); -// -// $this->consumer->process(1); -// -// $defaultSourceItem = $this->getDefaultSourceItemBySku->execute('SKU-1'); -// self::assertEquals( -// 10, -// $defaultSourceItem->getQuantity(), -// 'Asynchronous source item update failed' -// ); - $this->markTestIncomplete('Test is failing due to missing AMQP configuration'); + $stockItem = $this->stockRegistry->getStockItemBySku('SKU-1'); + $stockItem->setQty(10); + $this->stockRegistry->updateStockItemBySku('SKU-1', $stockItem); + + $defaultSourceItem = $this->getDefaultSourceItemBySku->execute('SKU-1'); + self::assertEquals( + 5.5, + $defaultSourceItem->getQuantity(), + 'Source item was update synchronously even if asynchronous operation was requested' + ); + + $this->consumer->process(1); + + $defaultSourceItem = $this->getDefaultSourceItemBySku->execute('SKU-1'); + self::assertEquals( + 10, + $defaultSourceItem->getQuantity(), + 'Asynchronous source item update failed' + ); } /** From 34d313b71a3403703ad719f85ebb6ecfaf3dab68 Mon Sep 17 00:00:00 2001 From: Riccardo Ugolini <riccardo.ugolini@magespecialist.it> Date: Wed, 10 Apr 2019 10:36:23 +0200 Subject: [PATCH 096/231] composer.json and test namespace fix --- .../GetListReservationsTotOrdersTest.php | 2 +- InventoryReservationCli/composer.json | 26 ++++++------------- InventoryReservationCli/etc/module.xml | 1 - 3 files changed, 9 insertions(+), 20 deletions(-) diff --git a/InventoryReservationCli/Test/Integration/Model/GetListReservationsTotOrdersTest.php b/InventoryReservationCli/Test/Integration/Model/GetListReservationsTotOrdersTest.php index 4cfb66673e17..fb36cf39048c 100644 --- a/InventoryReservationCli/Test/Integration/Model/GetListReservationsTotOrdersTest.php +++ b/InventoryReservationCli/Test/Integration/Model/GetListReservationsTotOrdersTest.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryReservations\Test\Integration\Model; +namespace Magento\InventoryReservationCli\Test\Integration\Model; use Magento\InventoryReservationCli\Model\GetOrderInFinalState; use Magento\InventoryReservationCli\Model\GetOrderWithBrokenReservation; diff --git a/InventoryReservationCli/composer.json b/InventoryReservationCli/composer.json index 15969d15e2b4..5a7926a2d942 100644 --- a/InventoryReservationCli/composer.json +++ b/InventoryReservationCli/composer.json @@ -1,18 +1,16 @@ { "name": "magento/inventoryreservationcli", - "description": "", + "description": "N/A", "require": { - "php": "~5.5.0|~5.6.0|~7.0.0", - - "magento/magento-composer-installer": "*" - }, - "suggest": { - + "php": "~7.1.3||~7.2.0", + "magento/framework": "*", + "magento/module-sales": "*", + "magento/module-inventory-reservations": "*" }, "type": "magento2-module", - "version": "0.1.0", "license": [ - + "OSL-3.0", + "AFL-3.0" ], "autoload": { "files": [ @@ -21,13 +19,5 @@ "psr-4": { "Magento\\InventoryReservationCli\\": "" } - }, - "extra": { - "map": [ - [ - "*", - "Magento/InventoryReservationCli" - ] - ] } -} \ No newline at end of file +} diff --git a/InventoryReservationCli/etc/module.xml b/InventoryReservationCli/etc/module.xml index 6c18a8a5cd79..9cd2b929062a 100644 --- a/InventoryReservationCli/etc/module.xml +++ b/InventoryReservationCli/etc/module.xml @@ -5,7 +5,6 @@ <sequence> <module name="Magento_InventoryReservations"/> <module name="Magento_Sales"/> - </sequence> </module> </config> \ No newline at end of file From 1a5ec7fcdfe15dd8d248f2c23d23b20fda2194a0 Mon Sep 17 00:00:00 2001 From: Riccardo Ugolini <riccardo.ugolini@magespecialist.it> Date: Wed, 10 Apr 2019 12:11:17 +0200 Subject: [PATCH 097/231] added readme file --- InventoryReservationCli/README.md | 7 +++++++ InventoryReservationCli/composer.json | 5 ++--- 2 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 InventoryReservationCli/README.md diff --git a/InventoryReservationCli/README.md b/InventoryReservationCli/README.md new file mode 100644 index 000000000000..b61d4ef8470c --- /dev/null +++ b/InventoryReservationCli/README.md @@ -0,0 +1,7 @@ +# InventoryReservationCli module + +The `InventoryReservationCli` module provide a cli command which helps the developer to discover inconsistencies on reservation. + +This module is part of the new inventory infrastructure. The +[Inventory Management overview](https://devdocs.magento.com/guides/v2.3/inventory/index.html) +describes the MSI (Multi-Source Inventory) project in more detail. diff --git a/InventoryReservationCli/composer.json b/InventoryReservationCli/composer.json index 5a7926a2d942..9a93ee19e42b 100644 --- a/InventoryReservationCli/composer.json +++ b/InventoryReservationCli/composer.json @@ -1,11 +1,10 @@ { - "name": "magento/inventoryreservationcli", + "name": "magento/module-inventory-reservation-cli", "description": "N/A", "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", - "magento/module-sales": "*", - "magento/module-inventory-reservations": "*" + "magento/module-sales": "*" }, "type": "magento2-module", "license": [ From 72bcce6f9e29b08698254a6c00fe1aa87bb7264f Mon Sep 17 00:00:00 2001 From: vad1m777 <gam.vadik@gmail.com> Date: Wed, 10 Apr 2019 17:08:06 +0300 Subject: [PATCH 098/231] fix #2155 --- .../Observer/ProcessSourceItemsObserver.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/InventoryConfigurableProductAdminUi/Observer/ProcessSourceItemsObserver.php b/InventoryConfigurableProductAdminUi/Observer/ProcessSourceItemsObserver.php index bea7a7ca5bc1..4e38dfce50de 100644 --- a/InventoryConfigurableProductAdminUi/Observer/ProcessSourceItemsObserver.php +++ b/InventoryConfigurableProductAdminUi/Observer/ProcessSourceItemsObserver.php @@ -85,8 +85,9 @@ public function execute(EventObserver $observer) private function processSourceItems(array $sourceItems, string $productSku) { foreach ($sourceItems as $key => $sourceItem) { + $sourceItems[$key][SourceItemInterface::QUANTITY] = $sourceItems[$key]['quantity_per_source']; + if (!isset($sourceItem[SourceItemInterface::STATUS])) { - $sourceItems[$key][SourceItemInterface::QUANTITY] = $sourceItems[$key]['quantity_per_source']; $sourceItems[$key][SourceItemInterface::STATUS] = $sourceItems[$key][SourceItemInterface::QUANTITY] > 0 ? 1 : 0; } From 40d7db1eb527b418bc6e6a0f75889c229dff8d97 Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Wed, 10 Apr 2019 16:12:37 +0200 Subject: [PATCH 099/231] Undeclared dependencies --- InventoryLegacySynchronization/composer.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/InventoryLegacySynchronization/composer.json b/InventoryLegacySynchronization/composer.json index 6a30db2190b2..797425caaa03 100644 --- a/InventoryLegacySynchronization/composer.json +++ b/InventoryLegacySynchronization/composer.json @@ -4,10 +4,12 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", + "magento/module-catalog": "*", "magento/module-inventory-api": "*", "magento/module-catalog-inventory": "*", "magento/module-inventory-catalog": "*", "magento/module-inventory-catalog-api": "*", + "magento/module-inventory-configuration-api": "*", "magento/module-asynchronous-operations": "*" }, "type": "magento2-module", From 748270b5fee95678923d230380161906fd61ea6a Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Wed, 10 Apr 2019 17:00:09 +0200 Subject: [PATCH 100/231] Mising dependency --- InventoryLegacySynchronization/composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/InventoryLegacySynchronization/composer.json b/InventoryLegacySynchronization/composer.json index 797425caaa03..e7e73f6d605c 100644 --- a/InventoryLegacySynchronization/composer.json +++ b/InventoryLegacySynchronization/composer.json @@ -5,6 +5,7 @@ "php": "~7.1.3||~7.2.0", "magento/framework": "*", "magento/module-catalog": "*", + "magento/module-inventory-indexer": "*", "magento/module-inventory-api": "*", "magento/module-catalog-inventory": "*", "magento/module-inventory-catalog": "*", From 0524eb6a49e8a86ee2fb393fda350d5c1fe871a2 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Wed, 10 Apr 2019 18:10:27 +0300 Subject: [PATCH 101/231] Comment updated --- InventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php b/InventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php index 58dc850e65ac..b24393c1c98e 100644 --- a/InventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php +++ b/InventoryGraphQl/Model/Resolver/OnlyXLeftInStockResolver.php @@ -66,7 +66,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value } /** - * Get quantity of a specified product when lower then configuration threshold. + * Get quantity of a specified product when equals or lower then configured threshold. * * @param string $sku * @return null|float From a96bd3c09b6e8684aba47f15d266995dd4431ee7 Mon Sep 17 00:00:00 2001 From: Bettina Cerban <bettinacerban@gmail.com> Date: Wed, 10 Apr 2019 12:19:43 -0300 Subject: [PATCH 102/231] remove interface per request --- .../Model/BulkPartialInventoryTransfer.php | 42 ++++------ .../Model/PartialInventoryTransfer.php | 80 ------------------- .../PartialTransferItemsValidator.php | 18 ++--- .../PartialTransferSourceValidator.php | 27 +++---- .../Api/Bulk/PartialInventoryTransferTest.php | 26 +++--- InventoryCatalog/etc/di.xml | 2 - .../BulkPartialInventoryTransferInterface.php | 8 +- .../PartialInventoryTransferInterface.php | 62 -------------- ...PartialInventoryTransferValidatorChain.php | 5 +- ...ialInventoryTransferValidatorInterface.php | 8 +- 10 files changed, 57 insertions(+), 221 deletions(-) delete mode 100644 InventoryCatalog/Model/PartialInventoryTransfer.php delete mode 100644 InventoryCatalogApi/Api/Data/PartialInventoryTransferInterface.php diff --git a/InventoryCatalog/Model/BulkPartialInventoryTransfer.php b/InventoryCatalog/Model/BulkPartialInventoryTransfer.php index 5f4775efd535..65b2de6205d1 100644 --- a/InventoryCatalog/Model/BulkPartialInventoryTransfer.php +++ b/InventoryCatalog/Model/BulkPartialInventoryTransfer.php @@ -8,12 +8,11 @@ namespace Magento\InventoryCatalog\Model; use Magento\Framework\Validation\ValidationException; -use Magento\InventoryApi\Api\Data\SourceItemInterface; use Magento\CatalogInventory\Model\Indexer\Stock as LegacyIndexer; use Magento\InventoryCatalog\Model\ResourceModel\TransferInventoryPartially; use Magento\InventoryCatalogApi\Api\BulkPartialInventoryTransferInterface; use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; -use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface; +use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferItemInterface; use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface; use Magento\InventoryCatalogApi\Model\PartialInventoryTransferValidatorInterface; use Magento\InventoryIndexer\Indexer\Source\SourceIndexer; @@ -29,9 +28,6 @@ class BulkPartialInventoryTransfer implements BulkPartialInventoryTransferInterf /** @var GetProductIdsBySkusInterface */ private $productIdsBySkus; - /** @var GetSourceItemsBySkuAndSourceCodes */ - private $sourceItemsBySku; - /** @var DefaultSourceProviderInterface */ private $defaultSourceProvider; @@ -45,7 +41,6 @@ class BulkPartialInventoryTransfer implements BulkPartialInventoryTransferInterf * @param PartialInventoryTransferValidatorInterface $partialInventoryTransferValidator * @param TransferInventoryPartially $transferInventoryPartiallyCommand * @param GetProductIdsBySkusInterface $getProductIdsBySkus - * @param GetSourceItemsBySkuAndSourceCodes $getSourceItemsBySkuAndSourceCodes * @param DefaultSourceProviderInterface $defaultSourceProvider * @param SourceIndexer $sourceIndexer * @param LegacyIndexer $legacyIndexer @@ -54,7 +49,6 @@ public function __construct( PartialInventoryTransferValidatorInterface $partialInventoryTransferValidator, TransferInventoryPartially $transferInventoryPartiallyCommand, GetProductIdsBySkusInterface $getProductIdsBySkus, - GetSourceItemsBySkuAndSourceCodes $getSourceItemsBySkuAndSourceCodes, DefaultSourceProviderInterface $defaultSourceProvider, SourceIndexer $sourceIndexer, LegacyIndexer $legacyIndexer @@ -62,7 +56,6 @@ public function __construct( $this->transferValidator = $partialInventoryTransferValidator; $this->transferCommand = $transferInventoryPartiallyCommand; $this->productIdsBySkus = $getProductIdsBySkus; - $this->sourceItemsBySku = $getSourceItemsBySkuAndSourceCodes; $this->defaultSourceProvider = $defaultSourceProvider; $this->sourceIndexer = $sourceIndexer; $this->legacyIndexer = $legacyIndexer; @@ -71,37 +64,36 @@ public function __construct( /** * Run bulk partial inventory transfer for specified items. * - * @param PartialInventoryTransferInterface $transfer - * @return SourceItemInterface[] + * @param string $originSourceCode + * @param string $destinationSourceCode + * @param PartialInventoryTransferItemInterface[] $items + * @return void * @throws \Magento\Framework\Validation\ValidationException */ - public function execute($transfer): array + public function execute(string $originSourceCode, string $destinationSourceCode, array $items): void { - $validationResult = $this->transferValidator->validate($transfer); - if ($validationResult->isValid()) { - return $this->processTransfer($transfer); + $validationResult = $this->transferValidator->validate($originSourceCode, $destinationSourceCode, $items); + if (!$validationResult->isValid()) { + throw new ValidationException(__("Transfer validation failed"), null, 0, $validationResult); } - throw new ValidationException(__("Transfer validation failed"), null, 0, $validationResult); + $this->processTransfer($originSourceCode, $destinationSourceCode, $items); } /** - * @param PartialInventoryTransferInterface $transfer - * @return SourceItemInterface[] + * @param string $originSourceCode + * @param string $destinationSourceCode + * @param PartialInventoryTransferItemInterface[] $items */ - private function processTransfer($transfer): array + private function processTransfer(string $originSourceCode, string $destinationSourceCode, array $items): void { $processedSkus = []; - $sourceItems = []; - - foreach ($transfer->getItems() as $item) { - $this->transferCommand->execute($item, $transfer->getOriginSourceCode(), $transfer->getDestinationSourceCode()); + foreach ($items as $item) { + $this->transferCommand->execute($item, $originSourceCode, $destinationSourceCode); $processedSkus[] = $item->getSku(); - $sourceItems += $this->sourceItemsBySku->execute($item->getSku(), [$transfer->getOriginSourceCode(), $transfer->getDestinationSourceCode()]); } - $this->updateIndexes([$transfer->getOriginSourceCode(), $transfer->getDestinationSourceCode()], $processedSkus); - return $sourceItems; + $this->updateIndexes([$originSourceCode, $destinationSourceCode], $processedSkus); } /** diff --git a/InventoryCatalog/Model/PartialInventoryTransfer.php b/InventoryCatalog/Model/PartialInventoryTransfer.php deleted file mode 100644 index 453e96c8e55e..000000000000 --- a/InventoryCatalog/Model/PartialInventoryTransfer.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryCatalog\Model; - -use Magento\Framework\Api\AbstractSimpleObject; -use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferExtensionInterface; -use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface; - -class PartialInventoryTransfer extends AbstractSimpleObject implements PartialInventoryTransferInterface -{ - - /** - * @return \Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferItemInterface[] - */ - public function getItems(): array - { - return $this->_get(self::ITEMS); - } - - /** - * @param \Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferItemInterface[] $items - */ - public function setItems(array $items): void - { - $this->setData(self::ITEMS, $items); - } - - /** - * @return string - */ - public function getOriginSourceCode(): string - { - return $this->_get(self::ORIGIN_SOURCE_CODE); - } - - /** - * @param string $code - */ - public function setOriginSourceCode(string $code): void - { - $this->setData(self::ORIGIN_SOURCE_CODE, $code); - } - - /** - * @return string - */ - public function getDestinationSourceCode(): string - { - return $this->_get(self::DESTINATION_SOURCE_CODE); - } - - /** - * @param string $code - */ - public function setDestinationSourceCode(string $code): void - { - $this->setData(self::DESTINATION_SOURCE_CODE, $code); - } - - /** - * @return \Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferExtensionInterface - */ - public function getExtensionAttributes(): \Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferExtensionInterface - { - return $this->_get(self::EXTENSION_ATTRIBUTES_KEY); - } - - /** - * @param PartialInventoryTransferExtensionInterface $extensionAttributes - */ - public function setExtensionAttributes(\Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferExtensionInterface $extensionAttributes): void - { - $this->setData(self::EXTENSION_ATTRIBUTES_KEY, $extensionAttributes); - } -} \ No newline at end of file diff --git a/InventoryCatalog/Model/Source/Validator/PartialTransferItemsValidator.php b/InventoryCatalog/Model/Source/Validator/PartialTransferItemsValidator.php index 3b6a6a66365c..eda220ecfcf1 100644 --- a/InventoryCatalog/Model/Source/Validator/PartialTransferItemsValidator.php +++ b/InventoryCatalog/Model/Source/Validator/PartialTransferItemsValidator.php @@ -9,14 +9,9 @@ use Magento\Framework\Validation\ValidationResult; use Magento\Framework\Validation\ValidationResultFactory; -use Magento\Framework\Api\SearchCriteriaBuilder; - use Magento\Framework\Exception\NoSuchEntityException; use Magento\InventoryApi\Api\Data\SourceItemInterface; -use Magento\InventoryApi\Api\SourceRepositoryInterface; -use Magento\InventoryApi\Api\SourceItemRepositoryInterface; use Magento\InventoryCatalog\Model\GetSourceItemsBySkuAndSourceCodes; -use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface; use Magento\InventoryCatalogApi\Model\PartialInventoryTransferValidatorInterface; class PartialTransferItemsValidator implements PartialInventoryTransferValidatorInterface @@ -40,23 +35,20 @@ public function __construct( } /** - * Validates a partial transfer request. - * - * @param PartialInventoryTransferInterface $transfer - * @return ValidationResult + * @inheritdoc */ - public function validate(PartialInventoryTransferInterface $transfer): ValidationResult + public function validate(string $originSourceCode, string $destinationSourceCode, array $items): ValidationResult { $errors = []; - foreach ($transfer->getItems() as $item) { + foreach ($items as $item) { try { - $originSourceItem = $this->getSourceItemBySkuAndSource($item->getSku(), $transfer->getOriginSourceCode()); + $originSourceItem = $this->getSourceItemBySkuAndSource($item->getSku(), $originSourceCode); if ($originSourceItem->getQuantity() < $item->getQty()) { $errors[] = __('Requested transfer amount for sku %sku is not available', ['sku' => $item->getSku()]); } - $this->getSourceItemBySkuAndSource($item->getSku(), $transfer->getOriginSourceCode()); + $this->getSourceItemBySkuAndSource($item->getSku(), $destinationSourceCode); } catch (NoSuchEntityException $e) { $errors[] = __('%message', ['message' => $e->getMessage()]); } diff --git a/InventoryCatalog/Model/Source/Validator/PartialTransferSourceValidator.php b/InventoryCatalog/Model/Source/Validator/PartialTransferSourceValidator.php index 3cc2403d9524..22dd02c9064e 100644 --- a/InventoryCatalog/Model/Source/Validator/PartialTransferSourceValidator.php +++ b/InventoryCatalog/Model/Source/Validator/PartialTransferSourceValidator.php @@ -9,14 +9,8 @@ use Magento\Framework\Validation\ValidationResult; use Magento\Framework\Validation\ValidationResultFactory; -use Magento\Framework\Api\SearchCriteriaBuilder; - use Magento\Framework\Exception\NoSuchEntityException; -use Magento\InventoryApi\Api\Data\SourceItemInterface; use Magento\InventoryApi\Api\SourceRepositoryInterface; -use Magento\InventoryApi\Api\SourceItemRepositoryInterface; -use Magento\InventoryCatalog\Model\GetSourceItemsBySkuAndSourceCodes; -use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface; use Magento\InventoryCatalogApi\Model\PartialInventoryTransferValidatorInterface; class PartialTransferSourceValidator implements PartialInventoryTransferValidatorInterface @@ -35,33 +29,30 @@ public function __construct( ValidationResultFactory $validationResultFactory, SourceRepositoryInterface $sourceRepository ) { - $this->validationResultFactory = $validationResultFactory; - $this->sourceRepository = $sourceRepository; + $this->validationResultFactory = $validationResultFactory; + $this->sourceRepository = $sourceRepository; } /** - * Validates a partial transfer request. - * - * @param PartialInventoryTransferInterface $transfer - * @return ValidationResult + * @inheritdoc */ - public function validate(PartialInventoryTransferInterface $transfer): ValidationResult + public function validate(string $originSourceCode, string $destinationSourceCode, array $items): ValidationResult { $errors = []; try { - $this->sourceRepository->get($transfer->getOriginSourceCode()); + $this->sourceRepository->get($originSourceCode); } catch (NoSuchEntityException $e) { - $errors[] = __('Origin source %sourceCode does not exist', ['sourceCode' => $transfer->getOriginSourceCode()]); + $errors[] = __('Origin source %sourceCode does not exist', ['sourceCode' => $originSourceCode]); } try { - $this->sourceRepository->get($transfer->getDestinationSourceCode()); + $this->sourceRepository->get($destinationSourceCode); } catch (NoSuchEntityException $e) { - $errors[] = __('Destination source %sourceCode does not exist', ['sourceCode' => $transfer->getDestinationSourceCode()]); + $errors[] = __('Destination source %sourceCode does not exist', ['sourceCode' => $destinationSourceCode]); } - if ($transfer->getOriginSourceCode() === $transfer->getDestinationSourceCode()) { + if ($originSourceCode === $destinationSourceCode) { $errors[] = __('Cannot transfer a source on itself'); } diff --git a/InventoryCatalog/Test/Api/Bulk/PartialInventoryTransferTest.php b/InventoryCatalog/Test/Api/Bulk/PartialInventoryTransferTest.php index 3b6f05846c4f..feeec5ade83b 100644 --- a/InventoryCatalog/Test/Api/Bulk/PartialInventoryTransferTest.php +++ b/InventoryCatalog/Test/Api/Bulk/PartialInventoryTransferTest.php @@ -48,7 +48,7 @@ public function testValidTransfer() ] ]; - $this->_webApiCall($serviceInfo, ['transfer' => $this->getTransferItem('SKU-1', 1, 'eu-3', 'eu-2')]); + $this->_webApiCall($serviceInfo, $this->getTransferItem('SKU-1', 1, 'eu-3', 'eu-2')); $originSourceItem = $this->getSourceItem('SKU-1', 'eu-3'); $destinationSourceItem = $this->getSourceItem('SKU-1', 'eu-2'); @@ -75,7 +75,6 @@ public function testInvalidTransferOrigin() ] ]; - $body = ['transfer' => $this->getTransferItem('SKU-1', 1, 'eu-999', 'eu-2')]; $expectedError = [ 'message' => self::VALIDATION_FAIL_MESSAGE, 'errors' => [ @@ -93,7 +92,7 @@ public function testInvalidTransferOrigin() ] ] ]; - $this->webApiCallWithException($serviceInfo, $body, $expectedError); + $this->webApiCallWithException($serviceInfo, $this->getTransferItem('SKU-1', 1, 'eu-999', 'eu-2'), $expectedError); } /** @@ -110,7 +109,6 @@ public function testInvalidTransferDestination() ] ]; - $body = ['transfer' => $this->getTransferItem('SKU-1', 1, 'eu-3', 'eu-999')]; $expectedError = [ 'message' => self::VALIDATION_FAIL_MESSAGE, 'errors' => [ @@ -119,10 +117,16 @@ public function testInvalidTransferDestination() 'parameters' => [ 'sourceCode' => 'eu-999' ] + ], + [ + 'message' => '%message', + 'parameters' => [ + 'message' => 'Source item for SKU-1 and eu-999 does not exist' + ] ] ] ]; - $this->webApiCallWithException($serviceInfo, $body, $expectedError); + $this->webApiCallWithException($serviceInfo, $this->getTransferItem('SKU-1', 1, 'eu-3', 'eu-999'), $expectedError); } /** @@ -139,7 +143,6 @@ public function testInvalidTransferOriginAndDestinationAreTheSame() ] ]; - $body = ['transfer' => $this->getTransferItem('SKU-1', 1, 'eu-3', 'eu-3')]; $expectedError = [ 'message' => self::VALIDATION_FAIL_MESSAGE, 'errors' => [ @@ -149,7 +152,7 @@ public function testInvalidTransferOriginAndDestinationAreTheSame() ] ] ]; - $this->webApiCallWithException($serviceInfo, $body, $expectedError); + $this->webApiCallWithException($serviceInfo, $this->getTransferItem('SKU-1', 1, 'eu-3', 'eu-3'), $expectedError); } /** @@ -166,7 +169,6 @@ public function testInvalidTransferQuantityGreaterThanAvailable() ] ]; - $body = ['transfer' => $this->getTransferItem('SKU-1', 100, 'eu-3', 'eu-2')]; $expectedError = [ 'message' => self::VALIDATION_FAIL_MESSAGE, 'errors' => [ @@ -178,7 +180,7 @@ public function testInvalidTransferQuantityGreaterThanAvailable() ] ] ]; - $this->webApiCallWithException($serviceInfo, $body, $expectedError); + $this->webApiCallWithException($serviceInfo, $this->getTransferItem('SKU-1', 100, 'eu-3', 'eu-2'), $expectedError); } /** @@ -191,11 +193,11 @@ public function testInvalidTransferQuantityGreaterThanAvailable() private function getTransferItem(string $sku, float $qty, string $origin, string $destination): array { return [ - PartialInventoryTransferInterface::ITEMS => [ + 'items' => [ [PartialInventoryTransferItemInterface::SKU => $sku, PartialInventoryTransferItemInterface::QTY => $qty] ], - PartialInventoryTransferInterface::ORIGIN_SOURCE_CODE => $origin, - PartialInventoryTransferInterface::DESTINATION_SOURCE_CODE => $destination + 'origin_source_code' => $origin, + 'destination_source_code' => $destination ]; } diff --git a/InventoryCatalog/etc/di.xml b/InventoryCatalog/etc/di.xml index 3ad1d7f30aa6..a6f836c38351 100644 --- a/InventoryCatalog/etc/di.xml +++ b/InventoryCatalog/etc/di.xml @@ -105,8 +105,6 @@ type="Magento\InventoryCatalog\Model\BulkInventoryTransfer"/> <preference for="Magento\InventoryCatalogApi\Api\BulkPartialInventoryTransferInterface" type="Magento\InventoryCatalog\Model\BulkPartialInventoryTransfer"/> - <preference for="Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface" - type="Magento\InventoryCatalog\Model\PartialInventoryTransfer"/> <preference for="Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferItemInterface" type="Magento\InventoryCatalog\Model\PartialInventoryTransferItem"/> diff --git a/InventoryCatalogApi/Api/BulkPartialInventoryTransferInterface.php b/InventoryCatalogApi/Api/BulkPartialInventoryTransferInterface.php index 7e4ef94497be..2226e95c64d7 100644 --- a/InventoryCatalogApi/Api/BulkPartialInventoryTransferInterface.php +++ b/InventoryCatalogApi/Api/BulkPartialInventoryTransferInterface.php @@ -12,9 +12,11 @@ interface BulkPartialInventoryTransferInterface /** * Run bulk partial inventory transfer for specified items. * - * @param \Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface $transfer - * @return \Magento\InventoryApi\Api\Data\SourceItemInterface[] + * @param string $originSourceCode + * @param string $destinationSourceCode + * @param \Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferItemInterface[] $items + * @return void * @throws \Magento\Framework\Validation\ValidationException */ - public function execute($transfer): array; + public function execute(string $originSourceCode, string $destinationSourceCode, array $items): void; } \ No newline at end of file diff --git a/InventoryCatalogApi/Api/Data/PartialInventoryTransferInterface.php b/InventoryCatalogApi/Api/Data/PartialInventoryTransferInterface.php deleted file mode 100644 index d14832ef6888..000000000000 --- a/InventoryCatalogApi/Api/Data/PartialInventoryTransferInterface.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryCatalogApi\Api\Data; - -/** - * @api - * - * DTO to handle partial stock transfers from origin source to destination source, - * specifying the quantity to be transferred. - * @see \Magento\InventoryCatalogApi\Api\BulkPartialInventoryTransferInterface - */ -interface PartialInventoryTransferInterface extends \Magento\Framework\Api\ExtensibleDataInterface -{ - const ITEMS = 'items'; - const ORIGIN_SOURCE_CODE = 'origin_source_code'; - const DESTINATION_SOURCE_CODE = 'destination_source_code'; - - /** - * @return \Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferItemInterface[] - */ - public function getItems(): array; - - /** - * @param \Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferItemInterface[] $items - */ - public function setItems(array $items): void; - - /** - * @return string - */ - public function getOriginSourceCode(): string; - - /** - * @param string $code - */ - public function setOriginSourceCode(string $code): void; - - /** - * @return string - */ - public function getDestinationSourceCode(): string; - - /** - * @param string $code - */ - public function setDestinationSourceCode(string $code): void; - - /** - * @return \Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferExtensionInterface - */ - public function getExtensionAttributes(): \Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferExtensionInterface; - - /** - * @param PartialInventoryTransferExtensionInterface $extensionAttributes - */ - public function setExtensionAttributes(\Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferExtensionInterface $extensionAttributes): void; -} \ No newline at end of file diff --git a/InventoryCatalogApi/Model/PartialInventoryTransferValidatorChain.php b/InventoryCatalogApi/Model/PartialInventoryTransferValidatorChain.php index 77cd1b585717..a9ac74d32984 100644 --- a/InventoryCatalogApi/Model/PartialInventoryTransferValidatorChain.php +++ b/InventoryCatalogApi/Model/PartialInventoryTransferValidatorChain.php @@ -10,7 +10,6 @@ use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Validation\ValidationResult; use Magento\Framework\Validation\ValidationResultFactory; -use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface; /** * Chain of validators. Extension point for new validators via di configuration @@ -54,11 +53,11 @@ public function __construct( /** * @inheritdoc */ - public function validate(PartialInventoryTransferInterface $transfer): ValidationResult + public function validate(string $originSourceCode, string $destinationSourceCode, array $items): ValidationResult { $errors = []; foreach ($this->validators as $validator) { - $validationResult = $validator->validate($transfer); + $validationResult = $validator->validate($originSourceCode, $destinationSourceCode, $items); if (!$validationResult->isValid()) { $errors = array_merge($errors, $validationResult->getErrors()); diff --git a/InventoryCatalogApi/Model/PartialInventoryTransferValidatorInterface.php b/InventoryCatalogApi/Model/PartialInventoryTransferValidatorInterface.php index 924296263a5a..6b75d96885b0 100644 --- a/InventoryCatalogApi/Model/PartialInventoryTransferValidatorInterface.php +++ b/InventoryCatalogApi/Model/PartialInventoryTransferValidatorInterface.php @@ -8,15 +8,17 @@ namespace Magento\InventoryCatalogApi\Model; use Magento\Framework\Validation\ValidationResult; -use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferInterface; +use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferItemInterface; interface PartialInventoryTransferValidatorInterface { /** * Validates a partial transfer request. * - * @param PartialInventoryTransferInterface $transfer + * @param string $originSourceCode + * @param string $destinationSourceCode + * @param PartialInventoryTransferItemInterface[] $items * @return ValidationResult */ - public function validate(PartialInventoryTransferInterface $transfer): ValidationResult; + public function validate(string $originSourceCode, string $destinationSourceCode, array $items): ValidationResult; } \ No newline at end of file From 0ec61805a67e2eb300ac549219126853b820cebf Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Wed, 10 Apr 2019 19:09:03 +0300 Subject: [PATCH 103/231] magento-engcom/msi#2127: Order Ready for Pickup UI - Minor coding style improvements --- .../Model/IsOrderReadyForPickup.php | 3 +- .../Model/NotifyOrderIsReadyForPickup.php | 4 +-- .../Container/ReadyForPickupIdentity.php | 35 ++++++++----------- .../Order/Email/ReadyForPickupNotifier.php | 4 ++- .../Order/Email/ReadyForPickupSender.php | 6 ++-- .../Model/Order/IsFulfillable.php | 6 ++-- InventoryInStorePickup/etc/di.xml | 10 +++--- .../Adminhtml/Order/View/ReadyForPickup.php | 5 ++- .../Adminhtml/Order/NotifyPickup.php | 6 +--- .../Api/IsOrderReadyForPickupInterface.php | 5 +-- .../NotifyOrderIsReadyForPickupInterface.php | 3 +- 11 files changed, 42 insertions(+), 45 deletions(-) diff --git a/InventoryInStorePickup/Model/IsOrderReadyForPickup.php b/InventoryInStorePickup/Model/IsOrderReadyForPickup.php index f9bb6f13edc4..d8a3482a43f2 100644 --- a/InventoryInStorePickup/Model/IsOrderReadyForPickup.php +++ b/InventoryInStorePickup/Model/IsOrderReadyForPickup.php @@ -53,8 +53,9 @@ public function execute(int $orderId): bool } /** - * @param OrderInterface $order + * Retrieve order shipment availability. * + * @param OrderInterface $order * @return bool */ private function canShip(OrderInterface $order): bool diff --git a/InventoryInStorePickup/Model/NotifyOrderIsReadyForPickup.php b/InventoryInStorePickup/Model/NotifyOrderIsReadyForPickup.php index 03a94aa20982..b45e61c3a88f 100644 --- a/InventoryInStorePickup/Model/NotifyOrderIsReadyForPickup.php +++ b/InventoryInStorePickup/Model/NotifyOrderIsReadyForPickup.php @@ -15,7 +15,7 @@ use Magento\Sales\Api\ShipOrderInterface; /** - * Send an email to the customer and ship the order to reserve pickup location`s QTY + * Send an email to the customer and ship the order to reserve (deduct) pickup location`s QTY. */ class NotifyOrderIsReadyForPickup implements NotifyOrderIsReadyForPickupInterface { @@ -48,7 +48,7 @@ class NotifyOrderIsReadyForPickup implements NotifyOrderIsReadyForPickupInterfac public function __construct( IsOrderReadyForPickupInterface $isOrderReadyForPickup, ShipOrderInterface $shipOrder, - Order\Email\ReadyForPickupNotifier $emailNotifier, + ReadyForPickupNotifier $emailNotifier, OrderRepositoryInterface $orderRepository ) { $this->isOrderReadyForPickup = $isOrderReadyForPickup; diff --git a/InventoryInStorePickup/Model/Order/Email/Container/ReadyForPickupIdentity.php b/InventoryInStorePickup/Model/Order/Email/Container/ReadyForPickupIdentity.php index 5f3cba2aec0b..c546ae82b0f0 100644 --- a/InventoryInStorePickup/Model/Order/Email/Container/ReadyForPickupIdentity.php +++ b/InventoryInStorePickup/Model/Order/Email/Container/ReadyForPickupIdentity.php @@ -11,20 +11,23 @@ use Magento\Sales\Model\Order\Email\Container\IdentityInterface; use Magento\Store\Model\ScopeInterface; +/** + * @inheritdoc + */ class ReadyForPickupIdentity extends Container implements IdentityInterface { /** * Configuration paths */ - private const XML_PATH_EMAIL_COPY_METHOD = 'storepickup_email/order_ready_for_pickup/copy_method'; - private const XML_PATH_EMAIL_COPY_TO = 'storepickup_email/order_ready_for_pickup/copy_to'; - private const XML_PATH_EMAIL_IDENTITY = 'storepickup_email/order_ready_for_pickup/identity'; + private const XML_PATH_EMAIL_COPY_METHOD = 'storepickup_email/order_ready_for_pickup/copy_method'; + private const XML_PATH_EMAIL_COPY_TO = 'storepickup_email/order_ready_for_pickup/copy_to'; + private const XML_PATH_EMAIL_IDENTITY = 'storepickup_email/order_ready_for_pickup/identity'; private const XML_PATH_EMAIL_GUEST_TEMPLATE = 'storepickup_email/order_ready_for_pickup/guest_template'; - private const XML_PATH_EMAIL_TEMPLATE = 'storepickup_email/order_ready_for_pickup/template'; - private const XML_PATH_EMAIL_ENABLED = 'storepickup_email/order_ready_for_pickup/enabled'; + private const XML_PATH_EMAIL_TEMPLATE = 'storepickup_email/order_ready_for_pickup/template'; + private const XML_PATH_EMAIL_ENABLED = 'storepickup_email/order_ready_for_pickup/enabled'; /** - * @return bool + * @inheritdoc */ public function isEnabled() { @@ -36,9 +39,7 @@ public function isEnabled() } /** - * Return email copy_to list - * - * @return array|bool + * @inheritdoc */ public function getEmailCopyTo() { @@ -51,9 +52,7 @@ public function getEmailCopyTo() } /** - * Return copy method - * - * @return mixed + * @inheritdoc */ public function getCopyMethod() { @@ -61,9 +60,7 @@ public function getCopyMethod() } /** - * Return guest template id - * - * @return mixed + * @inheritdoc */ public function getGuestTemplateId() { @@ -71,9 +68,7 @@ public function getGuestTemplateId() } /** - * Return template id - * - * @return mixed + * @inheritdoc */ public function getTemplateId() { @@ -81,9 +76,7 @@ public function getTemplateId() } /** - * Return email identity - * - * @return mixed + * @inheritdoc */ public function getEmailIdentity() { diff --git a/InventoryInStorePickup/Model/Order/Email/ReadyForPickupNotifier.php b/InventoryInStorePickup/Model/Order/Email/ReadyForPickupNotifier.php index 4d0379735124..0d4c7c6d42fd 100644 --- a/InventoryInStorePickup/Model/Order/Email/ReadyForPickupNotifier.php +++ b/InventoryInStorePickup/Model/Order/Email/ReadyForPickupNotifier.php @@ -13,8 +13,10 @@ use Psr\Log\LoggerInterface as Logger; /** -* Sends email to customer + * {@inheritdoc} + * * TODO: remove this class with asynchronous mailing implementation + * @see https://github.com/magento-engcom/msi/issues/2160 */ class ReadyForPickupNotifier extends AbstractNotifier { diff --git a/InventoryInStorePickup/Model/Order/Email/ReadyForPickupSender.php b/InventoryInStorePickup/Model/Order/Email/ReadyForPickupSender.php index c24b65fe6789..8e86eacaaa34 100644 --- a/InventoryInStorePickup/Model/Order/Email/ReadyForPickupSender.php +++ b/InventoryInStorePickup/Model/Order/Email/ReadyForPickupSender.php @@ -18,8 +18,11 @@ use Psr\Log\LoggerInterface; /** + * {@inheritdoc} + * * TODO: refactor * TODO: Implement asynchronous email sending + * @see https://github.com/magento-engcom/msi/issues/2160 */ class ReadyForPickupSender extends Sender { @@ -51,10 +54,10 @@ public function __construct( /** * Send order-specific email. + * * This method is not declared anywhere in parent/interface, but Magento calls it * * @param Order $order - * * @return bool */ public function send(Order $order): bool @@ -66,7 +69,6 @@ public function send(Order $order): bool * Prepare email template with variables * * @param Order $order - * * @return void */ protected function prepareTemplate(Order $order) diff --git a/InventoryInStorePickup/Model/Order/IsFulfillable.php b/InventoryInStorePickup/Model/Order/IsFulfillable.php index 2c0034e6eb4a..0bf3fce86e84 100644 --- a/InventoryInStorePickup/Model/Order/IsFulfillable.php +++ b/InventoryInStorePickup/Model/Order/IsFulfillable.php @@ -40,8 +40,9 @@ public function __construct( } /** - * @param OrderInterface $order + * Check if items are ordered form the Pickup location and verify that each item has enough quantity. * + * @param OrderInterface $order * @return bool */ public function execute(OrderInterface $order): bool @@ -61,10 +62,11 @@ public function execute(OrderInterface $order): bool } /** + * Check if Pickup Location source has enough item qty. + * * @param string $sku * @param string $sourceCode * @param float $qtyOrdered - * * @return bool */ private function isItemFulfillable(string $sku, string $sourceCode, float $qtyOrdered): bool diff --git a/InventoryInStorePickup/etc/di.xml b/InventoryInStorePickup/etc/di.xml index 141cc1c64937..88fd5db7463a 100644 --- a/InventoryInStorePickup/etc/di.xml +++ b/InventoryInStorePickup/etc/di.xml @@ -26,11 +26,11 @@ </arguments> </type> - <preference for="\Magento\InventoryInStorePickupApi\Api\NotifyOrderIsReadyForPickupInterface" - type="\Magento\InventoryInStorePickup\Model\NotifyOrderIsReadyForPickup"/> - <preference for="\Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface" - type="\Magento\InventoryInStorePickup\Model\IsOrderReadyForPickup"/> - <type name="\Magento\InventoryInStorePickup\Model\Order\Email\ReadyForPickupSender"> + <preference for="Magento\InventoryInStorePickupApi\Api\NotifyOrderIsReadyForPickupInterface" + type="Magento\InventoryInStorePickup\Model\NotifyOrderIsReadyForPickup"/> + <preference for="Magento\InventoryInStorePickupApi\Api\IsOrderReadyForPickupInterface" + type="Magento\InventoryInStorePickup\Model\IsOrderReadyForPickup"/> + <type name="Magento\InventoryInStorePickup\Model\Order\Email\ReadyForPickupSender"> <arguments> <argument name="identityContainer" xsi:type="object">\Magento\InventoryInStorePickup\Model\Order\Email\Container\ReadyForPickupIdentity</argument> </arguments> diff --git a/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php b/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php index adff53e5e70c..6e680dfc83ad 100644 --- a/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php +++ b/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php @@ -15,15 +15,14 @@ /** * TODO: is it possible to replace with UI Component? + * @see https://github.com/magento-engcom/msi/issues/2161 * * Render 'Notify Order is Ready for Pickup' button on order view page */ class ReadyForPickup extends Container { /** - * Block group - * - * @var string + * @inheritdoc */ protected $_blockGroup = 'Magento_Sales'; diff --git a/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php b/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php index 266edeefe3d6..8274df1d1180 100644 --- a/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php +++ b/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php @@ -20,9 +20,7 @@ use Psr\Log\LoggerInterface; /** - * Class NotifyPickup - * - * @package Magento\InventoryInStorePickupAdminUi\Controller\Adminhtml\Order + * Notify Customer of order pickup availability. */ class NotifyPickup extends Action { @@ -49,8 +47,6 @@ class NotifyPickup extends Action private $logger; /** - * NotifyPickup constructor. - * * @param Context $context * @param NotifyOrderIsReadyForPickupInterface $notifyOrderIsReadyForPickup * @param OrderRepositoryInterface $orderRepository diff --git a/InventoryInStorePickupApi/Api/IsOrderReadyForPickupInterface.php b/InventoryInStorePickupApi/Api/IsOrderReadyForPickupInterface.php index 658cc35b6842..a80737f75aed 100644 --- a/InventoryInStorePickupApi/Api/IsOrderReadyForPickupInterface.php +++ b/InventoryInStorePickupApi/Api/IsOrderReadyForPickupInterface.php @@ -8,13 +8,14 @@ namespace Magento\InventoryInStorePickupApi\Api; /** - * Check if customer can pickup the order in the pickup location + * Check if order is ready to be picked up by customer at the pickup location. */ interface IsOrderReadyForPickupInterface { /** - * @param int $orderId + * Check if order is ready to be picked up by customer at the pickup location. * + * @param int $orderId * @return bool */ public function execute(int $orderId): bool; diff --git a/InventoryInStorePickupApi/Api/NotifyOrderIsReadyForPickupInterface.php b/InventoryInStorePickupApi/Api/NotifyOrderIsReadyForPickupInterface.php index 539e8a5018b4..deb26a9f9e84 100644 --- a/InventoryInStorePickupApi/Api/NotifyOrderIsReadyForPickupInterface.php +++ b/InventoryInStorePickupApi/Api/NotifyOrderIsReadyForPickupInterface.php @@ -16,8 +16,9 @@ interface NotifyOrderIsReadyForPickupInterface { /** - * @param int $orderId + * Send an email to the customer and ship the order to reserve pickup location`s QTY. * + * @param int $orderId * @throws NoSuchEntityException * @throws LocalizedException */ From b3b073584aa97510bba1a4d32840758a564a31f9 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Wed, 10 Apr 2019 19:11:10 +0300 Subject: [PATCH 104/231] magento-engcom/msi#2127: Order Ready for Pickup UI - Static testing results applied --- .../Block/Adminhtml/Order/View/ReadyForPickup.php | 1 + InventoryInStorePickupApi/composer.json | 1 + 2 files changed, 2 insertions(+) diff --git a/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php b/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php index 6e680dfc83ad..80d20a828148 100644 --- a/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php +++ b/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php @@ -15,6 +15,7 @@ /** * TODO: is it possible to replace with UI Component? + * @api * @see https://github.com/magento-engcom/msi/issues/2161 * * Render 'Notify Order is Ready for Pickup' button on order view page diff --git a/InventoryInStorePickupApi/composer.json b/InventoryInStorePickupApi/composer.json index 36ddfc89dd99..ae8815eda36b 100644 --- a/InventoryInStorePickupApi/composer.json +++ b/InventoryInStorePickupApi/composer.json @@ -4,6 +4,7 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", + "magento/module-inventory-api": "*", "magento/module-inventory-distance-based-source-selection-api": "*" }, "type": "magento2-module", From d1ca8514a19d582f94143e787491c5aa0ff5f338 Mon Sep 17 00:00:00 2001 From: Eugene Shakhsuvarov <shakhsuv@adobe.com> Date: Wed, 10 Apr 2019 19:26:16 +0300 Subject: [PATCH 105/231] Minor coding standards fixes --- .../Order/GetListShipmentRepositoryPlugin.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/InventoryShipping/Plugin/Sales/Model/Order/GetListShipmentRepositoryPlugin.php b/InventoryShipping/Plugin/Sales/Model/Order/GetListShipmentRepositoryPlugin.php index e56eea4ff31d..e4f36a8900f2 100644 --- a/InventoryShipping/Plugin/Sales/Model/Order/GetListShipmentRepositoryPlugin.php +++ b/InventoryShipping/Plugin/Sales/Model/Order/GetListShipmentRepositoryPlugin.php @@ -1,4 +1,8 @@ <?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ declare(strict_types=1); namespace Magento\InventoryShipping\Plugin\Sales\Model\Order; @@ -7,19 +11,22 @@ use Magento\Sales\Api\Data\ShipmentExtensionFactory; use Magento\Sales\Model\Order\ShipmentRepository; +/** + * Add Source Information to shipments loaded with Magento\Sales\Api\ShipmentRepositoryInterface::getList + */ class GetListShipmentRepositoryPlugin { /** * @var ShipmentExtensionFactory */ private $shipmentExtensionFactory; + /** * @var GetSourceCodeByShipmentId */ private $getSourceCodeByShipmentId; /** - * GetListShipmentRepositoryPlugin constructor. * @param ShipmentExtensionFactory $shipmentExtensionFactory * @param GetSourceCodeByShipmentId $getSourceCodeByShipmentId */ @@ -32,14 +39,17 @@ public function __construct( } /** + * Add Source Information to shipments. + * * @param ShipmentRepository $subject * @param \Magento\Sales\Api\Data\ShipmentInterface[] $searchResult * @return \Magento\Sales\Api\Data\ShipmentInterface[] + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function afterGetList(ShipmentRepository $subject, $searchResult) { /** @var \Magento\Sales\Api\Data\ShipmentInterface $shipment */ - foreach ($searchResult->getItems() as &$shipment) { + foreach ($searchResult->getItems() as $shipment) { $shipmentExtension = $shipment->getExtensionAttributes(); if (empty($shipmentExtension)) { $shipmentExtension = $this->shipmentExtensionFactory->create(); From 38006c6fac5b85a70e2204aa86e739d58d4c81af Mon Sep 17 00:00:00 2001 From: Eugene Shakhsuvarov <euhenio@me.com> Date: Wed, 10 Apr 2019 19:27:47 +0300 Subject: [PATCH 106/231] Move plugin to the interface --- InventoryShipping/etc/di.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InventoryShipping/etc/di.xml b/InventoryShipping/etc/di.xml index 67fb8c85c374..569a65c032b7 100644 --- a/InventoryShipping/etc/di.xml +++ b/InventoryShipping/etc/di.xml @@ -14,7 +14,7 @@ <plugin name="LoadSourceForShipment" type="Magento\InventoryShipping\Plugin\Sales\ResourceModel\Order\Shipment\LoadSourceForShipmentPlugin"/> <plugin name="DeleteSourceForShipment" type="Magento\InventoryShipping\Plugin\Sales\ResourceModel\Order\Shipment\DeleteSourceForShipmentPlugin"/> </type> - <type name="Magento\Sales\Model\Order\ShipmentRepository"> + <type name="Magento\Sales\Api\ShipmentRepositoryInterface"> <plugin name="GetListShipmentRepository" type="Magento\InventoryShipping\Plugin\Sales\Model\Order\GetListShipmentRepositoryPlugin"/> </type> <type name="Magento\InventoryApi\Model\SourceValidatorChain"> From 57214c4355d04ff0fd3cc688ac6c1e87470fdf9b Mon Sep 17 00:00:00 2001 From: Riccardo Ugolini <riccardo.ugolini@magespecialist.it> Date: Wed, 10 Apr 2019 18:35:01 +0200 Subject: [PATCH 107/231] order, sku couple rework --- .../ShowInconsistenciesInCompletedOrders.php | 125 ++++++++++++++++++ .../ShowInconsistencyOrderComplete.php | 73 ---------- ...nalState.php => GetOrdersInFinalState.php} | 2 +- ...tOrdersWithNotCompensatedReservations.php} | 37 ++++-- .../Model/GetReservationsList.php | 35 ----- .../GetListNotEqualizedReservations.php | 40 ++++++ .../Model/ResourceModel/GetReservations.php | 43 ++++++ ...servations.php => GetReservationsList.php} | 5 +- .../GetListReservationsTotOrdersTest.php | 32 ++--- InventoryReservationCli/etc/di.xml | 4 +- InventoryReservationCli/etc/module.xml | 2 +- 11 files changed, 259 insertions(+), 139 deletions(-) create mode 100644 InventoryReservationCli/Command/ShowInconsistenciesInCompletedOrders.php delete mode 100644 InventoryReservationCli/Command/ShowInconsistencyOrderComplete.php rename InventoryReservationCli/Model/{GetOrderInFinalState.php => GetOrdersInFinalState.php} (98%) rename InventoryReservationCli/Model/{GetOrderWithBrokenReservation.php => GetOrdersWithNotCompensatedReservations.php} (54%) delete mode 100644 InventoryReservationCli/Model/GetReservationsList.php create mode 100644 InventoryReservationCli/Model/ResourceModel/GetListNotEqualizedReservations.php create mode 100644 InventoryReservationCli/Model/ResourceModel/GetReservations.php rename InventoryReservationCli/Model/ResourceModel/{GetListReservations.php => GetReservationsList.php} (93%) diff --git a/InventoryReservationCli/Command/ShowInconsistenciesInCompletedOrders.php b/InventoryReservationCli/Command/ShowInconsistenciesInCompletedOrders.php new file mode 100644 index 000000000000..0875bed2b10c --- /dev/null +++ b/InventoryReservationCli/Command/ShowInconsistenciesInCompletedOrders.php @@ -0,0 +1,125 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservationCli\Command; + +use Magento\InventoryReservationCli\Model\GetOrdersInFinalState; +use Magento\InventoryReservationCli\Model\GetOrdersWithNotCompensatedReservations; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\Order; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class ShowInconsistenciesInCompletedOrders extends Command +{ + /** + * @var GetOrdersInFinalState + */ + private $getOrderInFinalState; + /** + * @var GetOrdersWithNotCompensatedReservations + */ + private $getOrdersWithNotCompensatedReservations; + + /** + * @param GetOrdersWithNotCompensatedReservations $getOrdersWithNotCompensatedReservations + * @param GetOrdersInFinalState $getOrderInFinalState + */ + public function __construct( + GetOrdersWithNotCompensatedReservations $getOrdersWithNotCompensatedReservations, + GetOrdersInFinalState $getOrderInFinalState + ) { + $this->getOrdersWithNotCompensatedReservations = $getOrdersWithNotCompensatedReservations; + $this->getOrderInFinalState = $getOrderInFinalState; + parent::__construct(); + } + + protected function configure() + { + $this + ->setName('inventory:reservation:list-not-compensated') + ->setDescription('Show all orders and products without reservation compensation') + ->addOption('raw', 'r', InputOption::VALUE_NONE, 'Raw output'); + + parent::configure(); + } + + /** + * @param OutputInterface $output + * @param array $itemsNotCompensated + */ + private function prettyOutput(OutputInterface $output, array $itemsNotCompensated): void + { + /** @var OrderInterface[] $orders */ + $orders = $this->getOrderInFinalState->execute(array_keys($itemsNotCompensated)); + + $output->writeln('<comment>Inconsistencies found on following entries:</comment>'); + + /** @var Order $order */ + foreach($orders as $order) { + $inconsistentSkus = $itemsNotCompensated[$order->getId()]; + + $output->writeln(sprintf('Order <comment>%s</comment>:', $order->getIncrementId())); + + foreach ($inconsistentSkus as $inconsistentSku => $qty) { + $output->writeln( + sprintf( + ' - Product <comment>%s</comment> should be compensated by <comment>%+f</comment>', + $inconsistentSku, + -$qty + ) + ); + } + } + } + + /** + * @param OutputInterface $output + * @param array $itemsNotCompensated + */ + private function rawOutput(OutputInterface $output, array $itemsNotCompensated): void + { + /** @var OrderInterface[] $orders */ + $orders = $this->getOrderInFinalState->execute(array_keys($itemsNotCompensated)); + + /** @var Order $order */ + foreach($orders as $order) { + $inconsistentSkus = $itemsNotCompensated[$order->getId()]; + + foreach ($inconsistentSkus as $inconsistentSku => $qty) { + $output->writeln( + sprintf('%s:%s:%f', $order->getIncrementId(), $inconsistentSku, -$qty) + ); + } + } + } + + /** + * @param InputInterface $input + * @param OutputInterface $output + * @return int + */ + public function execute(InputInterface $input, OutputInterface $output): int + { + /** @var array $itemsNotCompensated */ + $itemsNotCompensated = $this->getOrdersWithNotCompensatedReservations->execute(); + + if (empty($itemsNotCompensated)) { + $output->writeln('<info>No order inconsistencies were found</info>'); + return 0; + } + + if ($input->getOption('raw')) { + $this->rawOutput($output, $itemsNotCompensated); + } else { + $this->prettyOutput($output, $itemsNotCompensated); + } + return -1; + } +} diff --git a/InventoryReservationCli/Command/ShowInconsistencyOrderComplete.php b/InventoryReservationCli/Command/ShowInconsistencyOrderComplete.php deleted file mode 100644 index 6cb904905093..000000000000 --- a/InventoryReservationCli/Command/ShowInconsistencyOrderComplete.php +++ /dev/null @@ -1,73 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryReservationCli\Command; - -use Magento\InventoryReservationCli\Model\GetOrderInFinalState; -use Magento\InventoryReservationCli\Model\GetOrderWithBrokenReservation; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Model\Order; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; - -class ShowInconsistencyOrderComplete extends Command -{ - /** - * @var GetOrderWithBrokenReservation - */ - private $getOrderWithBrokenReservation; - /** - * @var GetOrderInFinalState - */ - private $getOrderInFinalState; - - /** - * @param GetOrderWithBrokenReservation $getOrderWithBrokenReservation - * @param GetOrderInFinalState $getOrderInFinalState - */ - public function __construct( - GetOrderWithBrokenReservation $getOrderWithBrokenReservation, - GetOrderInFinalState $getOrderInFinalState - ) { - $this->getOrderWithBrokenReservation = $getOrderWithBrokenReservation; - $this->getOrderInFinalState = $getOrderInFinalState; - parent::__construct(); - } - - protected function configure() - { - $this - ->setName('inventory:reservation:complete-order-inconsistency') - ->setDescription('Show all reservation inconsistencies for completed orders'); - - parent::configure(); - } - - /** - * @param InputInterface $input - * @param OutputInterface $output - * @return void - */ - public function execute(InputInterface $input, OutputInterface $output): void - { - /** @var array $orderBrokenReservation */ - $orderBrokenReservation = $this->getOrderWithBrokenReservation->execute(); - - /** @var OrderInterface[] $orders */ - $orders = $this->getOrderInFinalState->execute(array_keys($orderBrokenReservation)); - - /** @var Order $order */ - foreach($orders as $order) { - $output->writeln( - __('Order %1 got inconsistency on inventory reservation', - $order->getIncrementId() - ) - ); - } - } -} diff --git a/InventoryReservationCli/Model/GetOrderInFinalState.php b/InventoryReservationCli/Model/GetOrdersInFinalState.php similarity index 98% rename from InventoryReservationCli/Model/GetOrderInFinalState.php rename to InventoryReservationCli/Model/GetOrdersInFinalState.php index a486ae78e052..d823d0f72c93 100644 --- a/InventoryReservationCli/Model/GetOrderInFinalState.php +++ b/InventoryReservationCli/Model/GetOrdersInFinalState.php @@ -13,7 +13,7 @@ use Magento\Sales\Api\OrderRepositoryInterface; use Magento\Sales\Model\Order; -class GetOrderInFinalState +class GetOrdersInFinalState { /** * @var OrderRepositoryInterface diff --git a/InventoryReservationCli/Model/GetOrderWithBrokenReservation.php b/InventoryReservationCli/Model/GetOrdersWithNotCompensatedReservations.php similarity index 54% rename from InventoryReservationCli/Model/GetOrderWithBrokenReservation.php rename to InventoryReservationCli/Model/GetOrdersWithNotCompensatedReservations.php index 29b9df40c282..6d3c48172793 100644 --- a/InventoryReservationCli/Model/GetOrderWithBrokenReservation.php +++ b/InventoryReservationCli/Model/GetOrdersWithNotCompensatedReservations.php @@ -8,9 +8,9 @@ namespace Magento\InventoryReservationCli\Model; use Magento\Framework\Serialize\SerializerInterface; -use Magento\Sales\Api\Data\OrderInterface; +use Magento\InventoryReservationCli\Model\ResourceModel\GetReservationsList; -class GetOrderWithBrokenReservation +class GetOrdersWithNotCompensatedReservations { /** * @var GetReservationsList @@ -35,25 +35,40 @@ public function __construct( } /** - * @return OrderInterface[] + * @return array */ public function execute(): array { - /** @var array $orderListReservations */ - $allReservations = $this->getReservationsList->getListReservationsTotOrder(); + /** @var array $reservationList */ + $reservationList = $this->getReservationsList->execute(); /** @var array $result */ $result = []; - foreach ($allReservations as $reservation){ + foreach ($reservationList as $reservation) { /** @var array $metadata */ $metadata = $this->serialize->unserialize($reservation['metadata']); $objectId = $metadata['object_id']; - if(!array_key_exists($objectId, $result)) { - $result[$objectId] = (float) 0; + $sku = $reservation['sku']; + $orderType = $metadata['object_type']; + + if ($orderType !== 'order') { + continue; + } + + if (!isset($result[$objectId])) { + $result[$objectId] = []; + } + if (!isset($result[$objectId][$sku])) { + $result[$objectId][$sku] = 0.0; } - $result[$objectId] += (float)$reservation['quantity']; + + $result[$objectId][$sku] += (float) $reservation['quantity']; + } + + foreach ($result as &$entry) { + $entry = array_filter($entry); } - $result = array_filter($result); - return $result; + + return array_filter($result); } } diff --git a/InventoryReservationCli/Model/GetReservationsList.php b/InventoryReservationCli/Model/GetReservationsList.php deleted file mode 100644 index ab1f8a11a60d..000000000000 --- a/InventoryReservationCli/Model/GetReservationsList.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryReservationCli\Model; - -use Magento\InventoryReservationCli\Model\ResourceModel\GetListReservations; - -class GetReservationsList -{ - /** - * @var GetListReservations - */ - private $getListReservations; - - /** - * @param GetListReservations $getListReservations - */ - public function __construct ( - GetListReservations $getListReservations - ) { - $this->getListReservations = $getListReservations; - } - - /** - * @return array - */ - public function getListReservationsTotOrder(): array - { - return $this->getListReservations->execute(); - } -} diff --git a/InventoryReservationCli/Model/ResourceModel/GetListNotEqualizedReservations.php b/InventoryReservationCli/Model/ResourceModel/GetListNotEqualizedReservations.php new file mode 100644 index 000000000000..2b7834bb2761 --- /dev/null +++ b/InventoryReservationCli/Model/ResourceModel/GetListNotEqualizedReservations.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservationCli\Model\ResourceModel; + +use Magento\Framework\App\ResourceConnection; + +class GetListNotEqualizedReservations +{ + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @param ResourceConnection $resourceConnection + */ + public function __construct ( + ResourceConnection $resourceConnection + ) { + $this->resourceConnection = $resourceConnection; + } + + public function execute(): array + { + $connection = $this->resourceConnection->getConnection(); + $tableName = $this->resourceConnection->getTableName('inventory_reservation'); + + $qry = $connection + ->select() + ->from(['r' => $tableName], ['reservations' => 'GROUP_CONCAT(r.reservation_id)']) + ->group(['r.stock_id', 'r.sku']) + ->having('SUM(r.quantity) != 0'); + return $connection->fetchRow($qry); + } +} diff --git a/InventoryReservationCli/Model/ResourceModel/GetReservations.php b/InventoryReservationCli/Model/ResourceModel/GetReservations.php new file mode 100644 index 000000000000..800e3f991e3a --- /dev/null +++ b/InventoryReservationCli/Model/ResourceModel/GetReservations.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservationCli\Model\ResourceModel; + +use Magento\Framework\App\ResourceConnection; + +class GetReservations +{ + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @param ResourceConnection $resourceConnection + */ + public function __construct ( + ResourceConnection $resourceConnection + ) { + $this->resourceConnection = $resourceConnection; + } + + /** + * @param array $reservationIds + * @return array + */ + public function execute(array $reservationIds): array + { + $connection = $this->resourceConnection->getConnection(); + $tableName = $this->resourceConnection->getTableName('inventory_reservation'); + + $qry = $connection + ->select() + ->from($tableName) + ->where('reservation_id in (?)', $reservationIds); + return $connection->fetchAll($qry); + } +} diff --git a/InventoryReservationCli/Model/ResourceModel/GetListReservations.php b/InventoryReservationCli/Model/ResourceModel/GetReservationsList.php similarity index 93% rename from InventoryReservationCli/Model/ResourceModel/GetListReservations.php rename to InventoryReservationCli/Model/ResourceModel/GetReservationsList.php index 7847adf854d6..838e6d7c139f 100644 --- a/InventoryReservationCli/Model/ResourceModel/GetListReservations.php +++ b/InventoryReservationCli/Model/ResourceModel/GetReservationsList.php @@ -9,7 +9,7 @@ use Magento\Framework\App\ResourceConnection; -class GetListReservations +class GetReservationsList { /** * @var ResourceConnection @@ -25,6 +25,9 @@ public function __construct ( $this->resourceConnection = $resourceConnection; } + /** + * @return array + */ public function execute(): array { $connection = $this->resourceConnection->getConnection(); diff --git a/InventoryReservationCli/Test/Integration/Model/GetListReservationsTotOrdersTest.php b/InventoryReservationCli/Test/Integration/Model/GetListReservationsTotOrdersTest.php index fb36cf39048c..a0e5ed8debd5 100644 --- a/InventoryReservationCli/Test/Integration/Model/GetListReservationsTotOrdersTest.php +++ b/InventoryReservationCli/Test/Integration/Model/GetListReservationsTotOrdersTest.php @@ -7,8 +7,8 @@ namespace Magento\InventoryReservationCli\Test\Integration\Model; -use Magento\InventoryReservationCli\Model\GetOrderInFinalState; -use Magento\InventoryReservationCli\Model\GetOrderWithBrokenReservation; +use Magento\InventoryReservationCli\Model\GetOrdersInFinalState; +use Magento\InventoryReservationCli\Model\GetOrdersWithNotCompensatedReservations; use Magento\Sales\Api\Data\OrderInterface; use PHPUnit\Framework\TestCase; use Magento\TestFramework\Helper\Bootstrap; @@ -23,17 +23,17 @@ public function testShouldNotFindAnyInconsistency(): void { $objectManager = Bootstrap::getObjectManager(); - /** @var GetOrderWithBrokenReservation $getOrderWithBrokenReservation */ - $getOrderWithBrokenReservation = $objectManager->get(GetOrderWithBrokenReservation::class); + /** @var GetOrdersWithNotCompensatedReservations $getOrdersWithNotCompensatedReservations */ + $getOrdersWithNotCompensatedReservations = $objectManager->get(GetOrdersWithNotCompensatedReservations::class); - /** @var GetOrderInFinalState $getOrderInFinalState */ - $getOrderInFinalState = $objectManager->get(GetOrderInFinalState::class); + /** @var GetOrdersInFinalState $getOrderInFinalState */ + $getOrderInFinalState = $objectManager->get(GetOrdersInFinalState::class); - /** @var array $result */ - $result = $getOrderWithBrokenReservation->execute(); + /** @var array $itemsNotCompensated */ + $itemsNotCompensated = $getOrdersWithNotCompensatedReservations->execute(); /** @var OrderInterface[] $orders */ - $orders = $getOrderInFinalState->execute(array_keys($result)); + $orders = $getOrderInFinalState->execute(array_keys($itemsNotCompensated)); self::assertSame([], $orders); } @@ -47,17 +47,17 @@ public function testShouldReturnOneReservationInconsistency(): void { $objectManager = Bootstrap::getObjectManager(); - /** @var GetOrderWithBrokenReservation $getOrderWithBrokenReservation */ - $getOrderWithBrokenReservation = $objectManager->get(GetOrderWithBrokenReservation::class); + /** @var GetOrdersWithNotCompensatedReservations $getOrdersWithNotCompensatedReservations */ + $getOrdersWithNotCompensatedReservations = $objectManager->get(GetOrdersWithNotCompensatedReservations::class); - /** @var GetOrderInFinalState $getOrderInFinalState */ - $getOrderInFinalState = $objectManager->get(GetOrderInFinalState::class); + /** @var GetOrdersInFinalState $getOrderInFinalState */ + $getOrderInFinalState = $objectManager->get(GetOrdersInFinalState::class); - /** @var array $result */ - $result = $getOrderWithBrokenReservation->execute(); + /** @var array $itemsNotCompensated */ + $itemsNotCompensated = $getOrdersWithNotCompensatedReservations->execute(); /** @var OrderInterface[] $orders */ - $orders = $getOrderInFinalState->execute(array_keys($result)); + $orders = $getOrderInFinalState->execute(array_keys($itemsNotCompensated)); self::assertCount(1, $orders); } diff --git a/InventoryReservationCli/etc/di.xml b/InventoryReservationCli/etc/di.xml index f76bcb5bd69a..4c86ec9b3166 100644 --- a/InventoryReservationCli/etc/di.xml +++ b/InventoryReservationCli/etc/di.xml @@ -9,7 +9,9 @@ <type name="Magento\Framework\Console\CommandList"> <arguments> <argument name="commands" xsi:type="array"> - <item name="inventory_complete_order_inconsistency" xsi:type="object">Magento\InventoryReservationCli\Command\ShowInconsistencyOrderComplete</item> + <item name="inventory_complete_order_inconsistency" xsi:type="object"> + Magento\InventoryReservationCli\Command\ShowInconsistenciesInCompletedOrders + </item> </argument> </arguments> </type> diff --git a/InventoryReservationCli/etc/module.xml b/InventoryReservationCli/etc/module.xml index 9cd2b929062a..a2fc04db0463 100644 --- a/InventoryReservationCli/etc/module.xml +++ b/InventoryReservationCli/etc/module.xml @@ -1,7 +1,7 @@ <?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_InventoryReservationCli" setup_version="0.1.0"> + <module name="Magento_InventoryReservationCli" setup_version="1.0.0"> <sequence> <module name="Magento_InventoryReservations"/> <module name="Magento_Sales"/> From 29fc1cc6b735eff949c6c004b6780b9b80884586 Mon Sep 17 00:00:00 2001 From: Eugene Shakhsuvarov <euhenio@me.com> Date: Wed, 10 Apr 2019 23:03:44 +0300 Subject: [PATCH 108/231] Static test fixex --- .../Model/Order/GetListShipmentRepositoryPlugin.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/InventoryShipping/Plugin/Sales/Model/Order/GetListShipmentRepositoryPlugin.php b/InventoryShipping/Plugin/Sales/Model/Order/GetListShipmentRepositoryPlugin.php index e4f36a8900f2..629942babf5b 100644 --- a/InventoryShipping/Plugin/Sales/Model/Order/GetListShipmentRepositoryPlugin.php +++ b/InventoryShipping/Plugin/Sales/Model/Order/GetListShipmentRepositoryPlugin.php @@ -9,7 +9,8 @@ use Magento\InventoryShipping\Model\ResourceModel\ShipmentSource\GetSourceCodeByShipmentId; use Magento\Sales\Api\Data\ShipmentExtensionFactory; -use Magento\Sales\Model\Order\ShipmentRepository; +use Magento\Sales\Api\ShipmentRepositoryInterface; +use Magento\Sales\Api\Data\ShipmentInterface; /** * Add Source Information to shipments loaded with Magento\Sales\Api\ShipmentRepositoryInterface::getList @@ -41,14 +42,14 @@ public function __construct( /** * Add Source Information to shipments. * - * @param ShipmentRepository $subject - * @param \Magento\Sales\Api\Data\ShipmentInterface[] $searchResult - * @return \Magento\Sales\Api\Data\ShipmentInterface[] + * @param ShipmentRepositoryInterface $subject + * @param ShipmentInterface[] $searchResult + * @return ShipmentInterface[] * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function afterGetList(ShipmentRepository $subject, $searchResult) + public function afterGetList(ShipmentRepositoryInterface $subject, $searchResult) { - /** @var \Magento\Sales\Api\Data\ShipmentInterface $shipment */ + /** @var ShipmentInterface $shipment */ foreach ($searchResult->getItems() as $shipment) { $shipmentExtension = $shipment->getExtensionAttributes(); if (empty($shipmentExtension)) { From 2db8a10393b055ad5d8af884885acc804fc5d279 Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Wed, 10 Apr 2019 15:39:54 -0500 Subject: [PATCH 109/231] Removed see assertion on souce_code on product page. This column has been removed from that grid --- ...ockOnConfigurationPageTurnedOffForSimpleProductTest.xml | 1 - ...tAppliedToSimpleProductOnConfigurationAdminPageTest.xml | 1 - ...AppliedToVirtualProductOnConfigurationAdminPageTest.xml | 1 - .../Mftf/Test/AdminRemoveSourcesAssignedToProductTest.xml | 7 ------- .../Test/AdminSourceForEachQuantityCanBeSetByAdminTest.xml | 3 --- ...oritySelectionAlgorithmSimpleProductCustomStockTest.xml | 2 -- ...uctAssignedToNonDefaultSourceCreatedByAdminUserTest.xml | 1 - 7 files changed, 16 deletions(-) diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminManageStockOnConfigurationPageTurnedOffForSimpleProductTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminManageStockOnConfigurationPageTurnedOffForSimpleProductTest.xml index 513a5adf555f..a3284467114b 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminManageStockOnConfigurationPageTurnedOffForSimpleProductTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminManageStockOnConfigurationPageTurnedOffForSimpleProductTest.xml @@ -89,7 +89,6 @@ <click selector="{{AdminAssignSourcesSlideOutGridSection.checkboxByCode($$customSource.source[source_code]$$)}}" stepKey="selectCreatedCustomSource"/> <click selector="{{AdminAssignSourcesSlideOutSection.done}}" stepKey="doneSelectSource"/> - <see selector="{{AdminProductSourcesGrid.rowByIndex('0')}}" userInput="$$customSource.source[source_code]$$" stepKey="checkSelectedSourceCodeInProductAssignedSourcesList"/> <see selector="{{AdminProductSourcesGrid.rowByIndex('0')}}" userInput="$$customSource.source[name]$$" stepKey="checkSelectedSourceNameInProductAssignedSourcesList"/> <fillField selector="{{AdminProductSourcesGrid.rowQty('0')}}" userInput="{{SimpleMsiProduct.quantity}}" stepKey="fillSourceQtyField"/> diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminNotifyQuantityUseDefaultAppliedToSimpleProductOnConfigurationAdminPageTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminNotifyQuantityUseDefaultAppliedToSimpleProductOnConfigurationAdminPageTest.xml index c22fe6d81c38..94d4f7b843bf 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminNotifyQuantityUseDefaultAppliedToSimpleProductOnConfigurationAdminPageTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminNotifyQuantityUseDefaultAppliedToSimpleProductOnConfigurationAdminPageTest.xml @@ -100,7 +100,6 @@ <click selector="{{AdminAssignSourcesSlideOutGridSection.checkboxByCode($$customSource.source[source_code]$$)}}" stepKey="assignCustomSourceToProduct"/> <click selector="{{AdminAssignSourcesSlideOutSection.done}}" stepKey="doneWithCustomSourceAssignment"/> - <see selector="{{AdminProductSourcesGrid.rowByIndex('0')}}" userInput="$$customSource.source[source_code]$$" stepKey="checkCustomSourceCode"/> <see selector="{{AdminProductSourcesGrid.rowByIndex('0')}}" userInput="$$customSource.source[name]$$" stepKey="checkCustomSourceName"/> <fillField selector="{{AdminProductSourcesGrid.rowQty('0')}}" userInput="{{SimpleMsiProduct.quantity}}" stepKey="fillSourceQty"/> diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminNotifyQuantityUseDefaultAppliedToVirtualProductOnConfigurationAdminPageTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminNotifyQuantityUseDefaultAppliedToVirtualProductOnConfigurationAdminPageTest.xml index 0ef1afbca07f..6fcfe778c4cb 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminNotifyQuantityUseDefaultAppliedToVirtualProductOnConfigurationAdminPageTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminNotifyQuantityUseDefaultAppliedToVirtualProductOnConfigurationAdminPageTest.xml @@ -103,7 +103,6 @@ <click selector="{{AdminAssignSourcesSlideOutGridSection.checkboxByCode($$createSource1.source[source_code]$$)}}" stepKey="assignCustomSourceToProduct1"/> <click selector="{{AdminAssignSourcesSlideOutSection.done}}" stepKey="doneWithCustomSourceAssignment1"/> - <see selector="{{AdminProductSourcesGrid.rowByIndex('0')}}" userInput="$$createSource1.source[source_code]$$" stepKey="checkCustomSourceCode1"/> <see selector="{{AdminProductSourcesGrid.rowByIndex('0')}}" userInput="$$createSource1.source[name]$$" stepKey="checkCustomSourceName1"/> <fillField selector="{{AdminProductSourcesGrid.rowQty('0')}}" userInput="{{VirtualMsiProduct.quantity}}" stepKey="fillSourceQty1"/> diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminRemoveSourcesAssignedToProductTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminRemoveSourcesAssignedToProductTest.xml index 59a8be4ecba5..94e3b92636a4 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminRemoveSourcesAssignedToProductTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminRemoveSourcesAssignedToProductTest.xml @@ -58,29 +58,22 @@ <see selector="{{AdminProductSourcesGrid.rowByIndex('0')}}" userInput="default" stepKey="seeSourceIdInGrid1"/> <see selector="{{AdminProductSourcesGrid.rowByIndex('0')}}" userInput="Default Source" stepKey="seeSourceNameInGrid1"/> - <see selector="{{AdminProductSourcesGrid.rowByIndex('1')}}" userInput="$$createSource1.source[source_code]$$" stepKey="seeSourceIdInGrid2"/> <see selector="{{AdminProductSourcesGrid.rowByIndex('1')}}" userInput="$$createSource1.source[name]$$" stepKey="seeSourceNameInGrid2"/> - <see selector="{{AdminProductSourcesGrid.rowByIndex('2')}}" userInput="$$createSource2.source[source_code]$$" stepKey="seeSourceIdInGrid3"/> <see selector="{{AdminProductSourcesGrid.rowByIndex('2')}}" userInput="$$createSource2.source[name]$$" stepKey="seeSourceNameInGrid3"/> <click selector="{{AdminGridMainControls.saveAndContinue}}" stepKey="clickOnSaveAndContinue1"/> <click selector="{{AdminProductSourcesGrid.rowDelete('1')}}" stepKey="clickOnDelete1"/> - <dontSee selector="{{AdminProductSourcesGrid.rowByIndex('1')}}" userInput="$$createSource1.source[source_code]$$" stepKey="dontSeeSourceIdInGrid1"/> <dontSee selector="{{AdminProductSourcesGrid.rowByIndex('1')}}" userInput="$$createSource1.source[name]$$" stepKey="dontSeeSourceNameInGrid1"/> - <see selector="{{AdminProductSourcesGrid.rowByIndex('1')}}" userInput="$$createSource2.source[source_code]$$" stepKey="seeSourceIdInGrid5"/> <see selector="{{AdminProductSourcesGrid.rowByIndex('1')}}" userInput="$$createSource2.source[name]$$" stepKey="seeSourceNameInGrid5"/> <click selector="{{AdminProductSourcesGrid.rowDelete('1')}}" stepKey="clickOnDelete2"/> - <dontSee selector="{{AdminProductSourcesGrid.rowByIndex('0')}}" userInput="$$createSource2.source[source_code]$$" stepKey="dontSeeSourceIdInGrid2"/> <dontSee selector="{{AdminProductSourcesGrid.rowByIndex('0')}}" userInput="$$createSource2.source[name]$$" stepKey="dontSeeSourceNameInGrid2"/> - <see selector="{{AdminProductSourcesGrid.rowByIndex('0')}}" userInput="default" stepKey="seeSourceIdInGrid6"/> <see selector="{{AdminProductSourcesGrid.rowByIndex('0')}}" userInput="Default Source" stepKey="seeSourceNameInGrid6"/> <click selector="{{AdminGridMainControls.saveAndContinue}}" stepKey="clickOnSaveAndContinue2"/> - <see selector="{{AdminProductSourcesGrid.rowByIndex('0')}}" userInput="default" stepKey="seeSourceIdInGrid7"/> <see selector="{{AdminProductSourcesGrid.rowByIndex('0')}}" userInput="Default Source" stepKey="seeSourceNameInGrid7"/> <seeNumberOfElements selector=".data-row" userInput="1" stepKey="seeOneSourceRow1"/> </test> diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminSourceForEachQuantityCanBeSetByAdminTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminSourceForEachQuantityCanBeSetByAdminTest.xml index 891a99407ce3..1f5d1abf2629 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminSourceForEachQuantityCanBeSetByAdminTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminSourceForEachQuantityCanBeSetByAdminTest.xml @@ -43,7 +43,6 @@ <fillField selector="{{AdminProductSourcesGrid.rowQty('0')}}" userInput="100" stepKey="fillDefaultQuantityField1"/> - <see selector="{{AdminProductSourcesGrid.rowByIndex('0')}}" userInput="{{_defaultSource.source_code}}" stepKey="seeSourceIdInGrid1"/> <see selector="{{AdminProductSourcesGrid.rowByIndex('0')}}" userInput="{{_defaultSource.name}}" stepKey="seeSourceNameInGrid1"/> <seeInField selector="{{AdminProductSourcesGrid.rowQty('0')}}" userInput="100" stepKey="seeSourceNameInGrid2"/> @@ -54,7 +53,6 @@ <fillField selector="{{AdminProductSourcesGrid.rowQty('1')}}" userInput="100" stepKey="fillDefaultQuantityField2"/> - <see selector="{{AdminProductSourcesGrid.rowByIndex('1')}}" userInput="$$createSource1.source[source_code]$$" stepKey="seeSourceIdInGrid2"/> <see selector="{{AdminProductSourcesGrid.rowByIndex('1')}}" userInput="$$createSource1.source[name]$$" stepKey="seeSourceNameInGrid3"/> <seeInField selector="{{AdminProductSourcesGrid.rowQty('1')}}" userInput="100" stepKey="seeSourceNameInGrid4"/> @@ -65,7 +63,6 @@ <fillField selector="{{AdminProductSourcesGrid.rowQty('2')}}" userInput="100" stepKey="fillDefaultQuantityField5"/> - <see selector="{{AdminProductSourcesGrid.rowByIndex('2')}}" userInput="$$createSource2.source[source_code]$$" stepKey="seeSourceIdInGrid3"/> <see selector="{{AdminProductSourcesGrid.rowByIndex('2')}}" userInput="$$createSource2.source[name]$$" stepKey="seeSourceNameInGrid6"/> <seeInField selector="{{AdminProductSourcesGrid.rowQty('2')}}" userInput="100" stepKey="seeSourceNameInGrid7"/> diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminSourcePrioritySelectionAlgorithmSimpleProductCustomStockTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminSourcePrioritySelectionAlgorithmSimpleProductCustomStockTest.xml index d3bc06fd5fbc..2dfbcfdda8f9 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminSourcePrioritySelectionAlgorithmSimpleProductCustomStockTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminSourcePrioritySelectionAlgorithmSimpleProductCustomStockTest.xml @@ -134,9 +134,7 @@ <click selector="{{AdminAssignSourcesSlideOutGridSection.checkboxByCode($$highPrioritySource.source[source_code]$$)}}" stepKey="selectHighPrioritySource"/> <click selector="{{AdminAssignSourcesSlideOutSection.done}}" stepKey="doneSelectHighPrioritySource"/> - <see selector="{{AdminProductSourcesGrid.rowByIndex('0')}}" userInput="$$lowPrioritySource.source[source_code]$$" stepKey="checkLowPrioritySourceCode"/> <see selector="{{AdminProductSourcesGrid.rowByIndex('0')}}" userInput="$$lowPrioritySource.source[name]$$" stepKey="checkLowPrioritySourceName"/> - <see selector="{{AdminProductSourcesGrid.rowByIndex('1')}}" userInput="$$highPrioritySource.source[source_code]$$" stepKey="checkHighPrioritySourceCode"/> <see selector="{{AdminProductSourcesGrid.rowByIndex('1')}}" userInput="$$highPrioritySource.source[name]$$" stepKey="checkHighPrioritySourceName"/> <fillField selector="{{AdminProductSourcesGrid.rowQty('0')}}" userInput="{{SimpleProduct.quantity}}" stepKey="fillSourceQtyFieldForLowPrioritySource"/> diff --git a/InventoryAdminUi/Test/Mftf/Test/StorefrontSimpleProductAssignedToNonDefaultSourceCreatedByAdminUserTest.xml b/InventoryAdminUi/Test/Mftf/Test/StorefrontSimpleProductAssignedToNonDefaultSourceCreatedByAdminUserTest.xml index 0dffa5825bec..350650159ce0 100644 --- a/InventoryAdminUi/Test/Mftf/Test/StorefrontSimpleProductAssignedToNonDefaultSourceCreatedByAdminUserTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/StorefrontSimpleProductAssignedToNonDefaultSourceCreatedByAdminUserTest.xml @@ -55,7 +55,6 @@ </actionGroup> <click selector="{{AdminAssignSourcesSlideOutGridSection.checkboxByCode($$createCustomSource.source[source_code]$$)}}" stepKey="clickOnCheckboxOnProductPage"/> <click selector="{{AdminAssignSourcesSlideOutSection.done}}" stepKey="clickOnDoneOnProductPage"/> - <see selector="{{AdminProductSourcesGrid.rowByIndex('0')}}" userInput="$$createCustomSource.source[source_code]$$" stepKey="seeSourceIdInGridOnProductPage"/> <see selector="{{AdminProductSourcesGrid.rowByIndex('0')}}" userInput="$$createCustomSource.source[name]$$" stepKey="seeSourceNameInGridOnProductPage"/> <fillField selector="{{AdminProductSourcesGrid.rowQty('0')}}" userInput="{{SimpleMsiProduct.quantity}}" stepKey="fillSourceQtyFieldOnProductPage"/> From f9a57caf38774879b06cae19a2b8df69df9ca254 Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Thu, 11 Apr 2019 01:18:08 +0300 Subject: [PATCH 110/231] MSI-2062: Inventory Export Stock implementation. Prototype. --- .../Model/ExportStockData.php | 39 ++++++ .../Model/ExportStockDataSearchResult.php | 18 +++ .../Model/GetExportStockData.php | 124 ++++++++++++++++++ .../Model/ProductStockData.php | 49 +++++++ InventoryExportStock/etc/di.xml | 16 +++ .../ExportStockDataSearchResultInterface.php | 4 +- .../Api/Data/ProductStockDataInterface.php | 47 +++++++ .../Api/ExportStockDataInterface.php | 14 +- InventoryExportStockApi/etc/webapi.xml | 17 +++ 9 files changed, 320 insertions(+), 8 deletions(-) create mode 100644 InventoryExportStock/Model/ExportStockData.php create mode 100644 InventoryExportStock/Model/ExportStockDataSearchResult.php create mode 100644 InventoryExportStock/Model/GetExportStockData.php create mode 100644 InventoryExportStock/Model/ProductStockData.php create mode 100644 InventoryExportStock/etc/di.xml create mode 100644 InventoryExportStockApi/Api/Data/ProductStockDataInterface.php create mode 100644 InventoryExportStockApi/etc/webapi.xml diff --git a/InventoryExportStock/Model/ExportStockData.php b/InventoryExportStock/Model/ExportStockData.php new file mode 100644 index 000000000000..48bb70a47ddc --- /dev/null +++ b/InventoryExportStock/Model/ExportStockData.php @@ -0,0 +1,39 @@ +<?php + +namespace Magento\InventoryExportStock\Model; + +use Magento\Framework\Api\SearchCriteriaInterface; +use Magento\InventoryExportStockApi\Api\Data\ExportStockDataSearchResultInterface; +use Magento\InventoryExportStockApi\Api\ExportStockDataInterface; + +class ExportStockData implements ExportStockDataInterface +{ + /** + * @var GetExportStockData + */ + private $getExportStockData; + + /** + * ExportStockData constructor. + * @param GetExportStockData $getExportStockData + */ + public function __construct( + GetExportStockData $getExportStockData + ) { + $this->getExportStockData = $getExportStockData; + } + + /** + * @inheritDoc + * + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function execute( + SearchCriteriaInterface $searchCriteria, + int $stockId = null, + int $qtyForNotManageStock = 1 + ): ExportStockDataSearchResultInterface { + return $this->getExportStockData->execute($searchCriteria, $stockId, $qtyForNotManageStock); + } +} diff --git a/InventoryExportStock/Model/ExportStockDataSearchResult.php b/InventoryExportStock/Model/ExportStockDataSearchResult.php new file mode 100644 index 000000000000..5861ed1416c6 --- /dev/null +++ b/InventoryExportStock/Model/ExportStockDataSearchResult.php @@ -0,0 +1,18 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryExportStock\Model; + +use Magento\Framework\Api\Search\SearchResult; +use Magento\InventoryExportStockApi\Api\Data\ExportStockDataSearchResultInterface; + +/** + * Class ExportStockDataSearchResult + */ +class ExportStockDataSearchResult extends SearchResult implements ExportStockDataSearchResultInterface +{ +} diff --git a/InventoryExportStock/Model/GetExportStockData.php b/InventoryExportStock/Model/GetExportStockData.php new file mode 100644 index 000000000000..07156a39b96b --- /dev/null +++ b/InventoryExportStock/Model/GetExportStockData.php @@ -0,0 +1,124 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryExportStock\Model; + +use Exception; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Framework\Api\SearchCriteriaInterface; +use Magento\Framework\Api\SearchResultsInterface; +use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface; +use Magento\InventoryExportStockApi\Api\Data\ExportStockDataSearchResultInterface; +use Magento\InventoryExportStockApi\Api\Data\ExportStockDataSearchResultInterfaceFactory; +use Magento\InventorySalesApi\Api\GetProductSalableQtyInterface; + +/** + * Class GetExportStockData + */ +class GetExportStockData +{ + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var ExportStockDataSearchResultInterfaceFactory + */ + private $exportStockDataSearchResultFactory; + + /** + * @var GetProductSalableQtyInterface + */ + private $getProductSalableQty; + + /** + * @var DefaultStockProviderInterface + */ + private $defaultStockProvider; + + /** + * ExportStockData constructor + * + * @param ProductRepositoryInterface $productRepository + * @param ExportStockDataSearchResultInterfaceFactory $exportStockDataSearchResultFactory + * @param GetProductSalableQtyInterface $getProductSalableQty + * @param DefaultStockProviderInterface $defaultStockProvider + */ + public function __construct( + ProductRepositoryInterface $productRepository, + ExportStockDataSearchResultInterfaceFactory $exportStockDataSearchResultFactory, + GetProductSalableQtyInterface $getProductSalableQty, + DefaultStockProviderInterface $defaultStockProvider + ) { + $this->productRepository = $productRepository; + $this->exportStockDataSearchResultFactory = $exportStockDataSearchResultFactory; + $this->getProductSalableQty = $getProductSalableQty; + $this->defaultStockProvider = $defaultStockProvider; + } + + /** + * Provides product stock data according to search criteria + * + * @param SearchCriteriaInterface $searchCriteria + * @param int $stockId + * @param int $qtyForNotManageStock + * @return ExportStockDataSearchResultInterface + * @throws Exception + */ + public function execute( + SearchCriteriaInterface $searchCriteria, + int $stockId=null, + int $qtyForNotManageStock=1 + ): ExportStockDataSearchResultInterface { + $productSearchResult = $this->getProducts($searchCriteria); + $items = $this->getProductStockDataArray($productSearchResult->getItems(), $stockId, $qtyForNotManageStock); + $searchResult = $this->exportStockDataSearchResultFactory->create(); + $searchResult->setSearchCriteria($productSearchResult->getSearchCriteria()); + $searchResult->setItems($items); + $searchResult->setTotalCount($productSearchResult->getTotalCount()); + + return $searchResult; + } + + /** + * Provides product search result by search criteria + * + * @param SearchCriteriaInterface $searchCriteria + * @return SearchResultsInterface + */ + private function getProducts(SearchCriteriaInterface $searchCriteria): SearchResultsInterface + { + return $this->productRepository->getList($searchCriteria); + } + + /** + * Provides salable qty and sku in array + * + * @param Product[] $products + * @param int $stockId + * @param int $qtyForNotManageStock + * @return array + * @throws Exception + */ + private function getProductStockDataArray(array $products, int $stockId=null, int $qtyForNotManageStock=1): array + { + if (!$stockId) { + $stockId = $this->defaultStockProvider->getId(); + } + $items = []; + foreach ($products as $product) { + $items[] = [ + 'sku' => $product->getSku(), + 'qty' => $this->getProductSalableQty->execute($product->getSku(), $stockId) ?: $qtyForNotManageStock + ]; + } + + return $items; + } +} diff --git a/InventoryExportStock/Model/ProductStockData.php b/InventoryExportStock/Model/ProductStockData.php new file mode 100644 index 000000000000..14e72bfa57c6 --- /dev/null +++ b/InventoryExportStock/Model/ProductStockData.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryExportStock\Model; + +use Magento\Framework\Model\AbstractModel; +use Magento\InventoryExportStockApi\Api\Data\ProductStockDataInterface; + +/** + * Class ProductStockData + */ +class ProductStockData extends AbstractModel implements ProductStockDataInterface +{ + /** + * @inheritDoc + */ + public function setSku(string $sku): void + { + $this->setData(self::SKU, $sku); + } + + /** + * @inheritDoc + */ + public function getSku(): string + { + return $this->getData(self::SKU); + } + + /** + * @inheritDoc + */ + public function setQty(int $qty): void + { + $this->setData(self::QTY, $qty); + } + + /** + * @inheritDoc + */ + public function getQty(): int + { + return $this->getData(self::QTY); + } +} diff --git a/InventoryExportStock/etc/di.xml b/InventoryExportStock/etc/di.xml new file mode 100644 index 000000000000..d9b008fcfc4e --- /dev/null +++ b/InventoryExportStock/etc/di.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:framework:ObjectManager/etc/config.xsd"> + <preference for="Magento\InventoryExportStockApi\Api\Data\ExportStockDataSearchResultInterface" + type="Magento\InventoryExportStock\Model\ExportStockDataSearchResult" /> + <preference for="Magento\InventoryExportStockApi\Api\Data\ProductStockDataInterface" + type="Magento\InventoryExportStock\Model\ProductStockData"/> + <preference for="Magento\InventoryExportStockApi\Api\ExportStockDataInterface" + type="Magento\InventoryExportStock\Model\ExportStockData"/> +</config> diff --git a/InventoryExportStockApi/Api/Data/ExportStockDataSearchResultInterface.php b/InventoryExportStockApi/Api/Data/ExportStockDataSearchResultInterface.php index 083e8e114e57..58435a150f83 100644 --- a/InventoryExportStockApi/Api/Data/ExportStockDataSearchResultInterface.php +++ b/InventoryExportStockApi/Api/Data/ExportStockDataSearchResultInterface.php @@ -20,7 +20,7 @@ interface ExportStockDataSearchResultInterface extends SearchResultsInterface * * @return array */ - public function getItems(): array; + public function getItems(); /** * Set stock data array @@ -28,5 +28,5 @@ public function getItems(): array; * @param array $items * @return $this */ - public function setItems(array $items): ExportStockDataSearchResultInterface; + public function setItems(array $items); } diff --git a/InventoryExportStockApi/Api/Data/ProductStockDataInterface.php b/InventoryExportStockApi/Api/Data/ProductStockDataInterface.php new file mode 100644 index 000000000000..02129b9506e1 --- /dev/null +++ b/InventoryExportStockApi/Api/Data/ProductStockDataInterface.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryExportStockApi\Api\Data; + +/** + * Interface ProductStockDataInterface + */ +interface ProductStockDataInterface +{ + public const SKU = 'sku'; + public const QTY = 'qty'; + + /** + * Sets SKU + * + * @param string $sku + * @return void + */ + public function setSku(string $sku): void; + + /** + * Provides SKU + * + * @return string + */ + public function getSku(): string; + + /** + * Sets QTY + * + * @param int $qty + * @return void + */ + public function setQty(int $qty): void; + + /** + * Provides QTY + * + * @return int + */ + public function getQty(): int; +} diff --git a/InventoryExportStockApi/Api/ExportStockDataInterface.php b/InventoryExportStockApi/Api/ExportStockDataInterface.php index 46fff5c656e8..0ff3b5fd0730 100644 --- a/InventoryExportStockApi/Api/ExportStockDataInterface.php +++ b/InventoryExportStockApi/Api/ExportStockDataInterface.php @@ -7,9 +7,6 @@ namespace Magento\InventoryExportStockApi\Api; -use Magento\Framework\Api\SearchCriteriaInterface; -use Magento\InventoryExportStockApi\Api\Data\ExportStockDataSearchResultInterface; - /** * Interface for ExportStockData * @api @@ -19,9 +16,14 @@ interface ExportStockDataInterface /** * Provides stock export data * - * @param SearchCriteriaInterface $searchCriteria + * @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria + * @param int $stockId * @param int $qtyForNotManageStock - * @return ExportStockDataSearchResultInterface + * @return \Magento\InventoryExportStockApi\Api\Data\ExportStockDataSearchResultInterface */ - public function execute(SearchCriteriaInterface $searchCriteria, int $qtyForNotManageStock): ExportStockDataSearchResultInterface; + public function execute( + \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria, + int $stockId = null, + int $qtyForNotManageStock = 1 + ): \Magento\InventoryExportStockApi\Api\Data\ExportStockDataSearchResultInterface; } diff --git a/InventoryExportStockApi/etc/webapi.xml b/InventoryExportStockApi/etc/webapi.xml new file mode 100644 index 000000000000..fb269bb2d853 --- /dev/null +++ b/InventoryExportStockApi/etc/webapi.xml @@ -0,0 +1,17 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Webapi:etc/webapi.xsd"> + <route url="/V1/inventory/export-stock-data" method="GET"> + <service class="Magento\InventoryExportStockApi\Api\ExportStockDataInterface" method="execute"/> + <resources> +<!-- <resource ref="Magento_Catalog::products"/>--> + <resource ref="anonymous"/> + </resources> + </route> +</routes> From a55bc15c2a2ecaadbbf0ca6397415d790d7c85c6 Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Thu, 11 Apr 2019 01:22:57 +0300 Subject: [PATCH 111/231] MSI-2062: Inventory Export Stock implementation. Static fix --- InventoryExportStock/Model/ExportStockData.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/InventoryExportStock/Model/ExportStockData.php b/InventoryExportStock/Model/ExportStockData.php index 48bb70a47ddc..6479e57c95eb 100644 --- a/InventoryExportStock/Model/ExportStockData.php +++ b/InventoryExportStock/Model/ExportStockData.php @@ -1,11 +1,20 @@ <?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); namespace Magento\InventoryExportStock\Model; +use Exception; use Magento\Framework\Api\SearchCriteriaInterface; use Magento\InventoryExportStockApi\Api\Data\ExportStockDataSearchResultInterface; use Magento\InventoryExportStockApi\Api\ExportStockDataInterface; +/** + * Class ExportStockData + */ class ExportStockData implements ExportStockDataInterface { /** @@ -26,8 +35,7 @@ public function __construct( /** * @inheritDoc * - * @throws \Magento\Framework\Exception\InputException - * @throws \Magento\Framework\Exception\LocalizedException + * @throws Exception */ public function execute( SearchCriteriaInterface $searchCriteria, From 9303e966f4bdfc11c2e3572943ddac89cb9550a6 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Thu, 11 Apr 2019 09:59:26 +0300 Subject: [PATCH 112/231] Inventory Reservations CLI --- .../ShowInconsistenciesInCompletedOrders.php | 15 +++++++++++++++ .../Model/GetOrdersInFinalState.php | 5 +++++ .../GetOrdersWithNotCompensatedReservations.php | 5 +++++ .../GetListNotEqualizedReservations.php | 8 ++++++++ .../Model/ResourceModel/GetReservations.php | 5 +++++ .../Model/ResourceModel/GetReservationsList.php | 9 +++++++-- 6 files changed, 45 insertions(+), 2 deletions(-) diff --git a/InventoryReservationCli/Command/ShowInconsistenciesInCompletedOrders.php b/InventoryReservationCli/Command/ShowInconsistenciesInCompletedOrders.php index 0875bed2b10c..7afa4a0cb281 100644 --- a/InventoryReservationCli/Command/ShowInconsistenciesInCompletedOrders.php +++ b/InventoryReservationCli/Command/ShowInconsistenciesInCompletedOrders.php @@ -16,6 +16,12 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +/** + * Outputs a list of uncompensated reservations linked to the orders in final state (Completed, Closed, Canceled). + * + * This command may be used to simplify migrations from Magento versions without new Inventory or to track down + * incorrect behavior of customizations. + */ class ShowInconsistenciesInCompletedOrders extends Command { /** @@ -40,6 +46,9 @@ public function __construct( parent::__construct(); } + /** + * @inheritdoc + */ protected function configure() { $this @@ -51,6 +60,8 @@ protected function configure() } /** + * Format output + * * @param OutputInterface $output * @param array $itemsNotCompensated */ @@ -80,6 +91,8 @@ private function prettyOutput(OutputInterface $output, array $itemsNotCompensate } /** + * Output without formatting + * * @param OutputInterface $output * @param array $itemsNotCompensated */ @@ -101,6 +114,8 @@ private function rawOutput(OutputInterface $output, array $itemsNotCompensated): } /** + * {@inheritdoc} + * * @param InputInterface $input * @param OutputInterface $output * @return int diff --git a/InventoryReservationCli/Model/GetOrdersInFinalState.php b/InventoryReservationCli/Model/GetOrdersInFinalState.php index d823d0f72c93..2cc6e8bcabca 100644 --- a/InventoryReservationCli/Model/GetOrdersInFinalState.php +++ b/InventoryReservationCli/Model/GetOrdersInFinalState.php @@ -13,6 +13,9 @@ use Magento\Sales\Api\OrderRepositoryInterface; use Magento\Sales\Model\Order; +/** + * Get list of orders in any of the final states (Complete, Closed, Canceled). + */ class GetOrdersInFinalState { /** @@ -38,6 +41,8 @@ public function __construct ( } /** + * Get list of orders in any of the final states (Complete, Closed, Canceled). + * * @param array $orderIds * @return OrderInterface[] */ diff --git a/InventoryReservationCli/Model/GetOrdersWithNotCompensatedReservations.php b/InventoryReservationCli/Model/GetOrdersWithNotCompensatedReservations.php index 6d3c48172793..816f953b6b29 100644 --- a/InventoryReservationCli/Model/GetOrdersWithNotCompensatedReservations.php +++ b/InventoryReservationCli/Model/GetOrdersWithNotCompensatedReservations.php @@ -10,6 +10,9 @@ use Magento\Framework\Serialize\SerializerInterface; use Magento\InventoryReservationCli\Model\ResourceModel\GetReservationsList; +/** + * Get list of reservations for Order entity. + */ class GetOrdersWithNotCompensatedReservations { /** @@ -35,6 +38,8 @@ public function __construct( } /** + * Get list of reservations for Order entity. + * * @return array */ public function execute(): array diff --git a/InventoryReservationCli/Model/ResourceModel/GetListNotEqualizedReservations.php b/InventoryReservationCli/Model/ResourceModel/GetListNotEqualizedReservations.php index 2b7834bb2761..57cc94d95bff 100644 --- a/InventoryReservationCli/Model/ResourceModel/GetListNotEqualizedReservations.php +++ b/InventoryReservationCli/Model/ResourceModel/GetListNotEqualizedReservations.php @@ -9,6 +9,9 @@ use Magento\Framework\App\ResourceConnection; +/** + * Load a list of uncompensated reservations. + */ class GetListNotEqualizedReservations { /** @@ -25,6 +28,11 @@ public function __construct ( $this->resourceConnection = $resourceConnection; } + /** + * Load a list of uncompensated reservations. + * + * @return array + */ public function execute(): array { $connection = $this->resourceConnection->getConnection(); diff --git a/InventoryReservationCli/Model/ResourceModel/GetReservations.php b/InventoryReservationCli/Model/ResourceModel/GetReservations.php index 800e3f991e3a..d7bf5dc0f3e8 100644 --- a/InventoryReservationCli/Model/ResourceModel/GetReservations.php +++ b/InventoryReservationCli/Model/ResourceModel/GetReservations.php @@ -9,6 +9,9 @@ use Magento\Framework\App\ResourceConnection; +/** + * Load a list of reservations by id. + */ class GetReservations { /** @@ -26,6 +29,8 @@ public function __construct ( } /** + * Load a list of reservations by id. + * * @param array $reservationIds * @return array */ diff --git a/InventoryReservationCli/Model/ResourceModel/GetReservationsList.php b/InventoryReservationCli/Model/ResourceModel/GetReservationsList.php index 838e6d7c139f..2cf3e0d64b3c 100644 --- a/InventoryReservationCli/Model/ResourceModel/GetReservationsList.php +++ b/InventoryReservationCli/Model/ResourceModel/GetReservationsList.php @@ -9,6 +9,9 @@ use Magento\Framework\App\ResourceConnection; +/** + * Load reservations from database. + */ class GetReservationsList { /** @@ -26,6 +29,8 @@ public function __construct ( } /** + * Load reservations from database. + * * @return array */ public function execute(): array @@ -33,9 +38,9 @@ public function execute(): array $connection = $this->resourceConnection->getConnection(); $tableName = $this->resourceConnection->getTableName('inventory_reservation'); - $qry = $connection + $query = $connection ->select() ->from($tableName); - return $connection->fetchAll($qry); + return $connection->fetchAll($query); } } From fe6eae0b6f044a9685b1f75eb0c340f41bde796c Mon Sep 17 00:00:00 2001 From: Riccardo Ugolini <riccardo.ugolini@magespecialist.it> Date: Thu, 11 Apr 2019 09:28:30 +0200 Subject: [PATCH 113/231] delted useless classes --- .../GetListNotEqualizedReservations.php | 40 ----------------- .../Model/ResourceModel/GetReservations.php | 43 ------------------- InventoryReservationCli/registration.php | 5 +++ 3 files changed, 5 insertions(+), 83 deletions(-) delete mode 100644 InventoryReservationCli/Model/ResourceModel/GetListNotEqualizedReservations.php delete mode 100644 InventoryReservationCli/Model/ResourceModel/GetReservations.php diff --git a/InventoryReservationCli/Model/ResourceModel/GetListNotEqualizedReservations.php b/InventoryReservationCli/Model/ResourceModel/GetListNotEqualizedReservations.php deleted file mode 100644 index 2b7834bb2761..000000000000 --- a/InventoryReservationCli/Model/ResourceModel/GetListNotEqualizedReservations.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryReservationCli\Model\ResourceModel; - -use Magento\Framework\App\ResourceConnection; - -class GetListNotEqualizedReservations -{ - /** - * @var ResourceConnection - */ - private $resourceConnection; - - /** - * @param ResourceConnection $resourceConnection - */ - public function __construct ( - ResourceConnection $resourceConnection - ) { - $this->resourceConnection = $resourceConnection; - } - - public function execute(): array - { - $connection = $this->resourceConnection->getConnection(); - $tableName = $this->resourceConnection->getTableName('inventory_reservation'); - - $qry = $connection - ->select() - ->from(['r' => $tableName], ['reservations' => 'GROUP_CONCAT(r.reservation_id)']) - ->group(['r.stock_id', 'r.sku']) - ->having('SUM(r.quantity) != 0'); - return $connection->fetchRow($qry); - } -} diff --git a/InventoryReservationCli/Model/ResourceModel/GetReservations.php b/InventoryReservationCli/Model/ResourceModel/GetReservations.php deleted file mode 100644 index 800e3f991e3a..000000000000 --- a/InventoryReservationCli/Model/ResourceModel/GetReservations.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryReservationCli\Model\ResourceModel; - -use Magento\Framework\App\ResourceConnection; - -class GetReservations -{ - /** - * @var ResourceConnection - */ - private $resourceConnection; - - /** - * @param ResourceConnection $resourceConnection - */ - public function __construct ( - ResourceConnection $resourceConnection - ) { - $this->resourceConnection = $resourceConnection; - } - - /** - * @param array $reservationIds - * @return array - */ - public function execute(array $reservationIds): array - { - $connection = $this->resourceConnection->getConnection(); - $tableName = $this->resourceConnection->getTableName('inventory_reservation'); - - $qry = $connection - ->select() - ->from($tableName) - ->where('reservation_id in (?)', $reservationIds); - return $connection->fetchAll($qry); - } -} diff --git a/InventoryReservationCli/registration.php b/InventoryReservationCli/registration.php index 7807ebab57b6..cb8a18dcf51d 100644 --- a/InventoryReservationCli/registration.php +++ b/InventoryReservationCli/registration.php @@ -1,4 +1,9 @@ <?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); \Magento\Framework\Component\ComponentRegistrar::register( \Magento\Framework\Component\ComponentRegistrar::MODULE, From d54d4e0a7d5fa7b432f4739cecc6e76358a212a9 Mon Sep 17 00:00:00 2001 From: Riccardo Ugolini <riccardo.ugolini@magespecialist.it> Date: Thu, 11 Apr 2019 09:29:40 +0200 Subject: [PATCH 114/231] delted useless classes --- InventoryReservationCli/registration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InventoryReservationCli/registration.php b/InventoryReservationCli/registration.php index cb8a18dcf51d..b08d2c9fb98d 100644 --- a/InventoryReservationCli/registration.php +++ b/InventoryReservationCli/registration.php @@ -9,4 +9,4 @@ \Magento\Framework\Component\ComponentRegistrar::MODULE, 'Magento_InventoryReservationCli', __DIR__ -); \ No newline at end of file +); From e522c83a405b3817f3a8d19f40add25ea14dbcbc Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Thu, 11 Apr 2019 13:05:47 +0300 Subject: [PATCH 115/231] MSI-2062: Inventory Export Stock implementation. Declared dependencies --- InventoryExportStock/composer.json | 4 ++++ InventoryExportStock/etc/module.xml | 10 +++++++++- InventoryExportStockApi/composer.json | 3 ++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/InventoryExportStock/composer.json b/InventoryExportStock/composer.json index cd854d1e47c1..31cad87c0f43 100644 --- a/InventoryExportStock/composer.json +++ b/InventoryExportStock/composer.json @@ -3,7 +3,11 @@ "description": "N/A", "require": { "php": "~7.1.3||~7.2.0", + "magento/framework": "*", "magento/module-inventory-api": "*", + "magento/module-inventory-catalog-api": "*", + "magento/module-inventory-sales-api": "*", + "magento/module-catalog": "*", "magento/module-inventory-export-stock-api": "*" }, "type": "magento2-module", diff --git a/InventoryExportStock/etc/module.xml b/InventoryExportStock/etc/module.xml index 68059a339174..75bdd2734ca5 100644 --- a/InventoryExportStock/etc/module.xml +++ b/InventoryExportStock/etc/module.xml @@ -6,5 +6,13 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_InventoryExportStock" setup_version="1.0.0"/> + <module name="Magento_InventoryExportStock" setup_version="1.0.0"> + <sequence> + <module name="Magento_Catalog"/> + <module name="Magento_InventoryApi"/> + <module name="Magento_InventoryCatalogApi"/> + <module name="Magento_InventoryExportStockApi"/> + <module name="Magento_InventorySalesApi"/> + </sequence> + </module> </config> diff --git a/InventoryExportStockApi/composer.json b/InventoryExportStockApi/composer.json index 8b36ac109ebb..d4cca0c653f3 100644 --- a/InventoryExportStockApi/composer.json +++ b/InventoryExportStockApi/composer.json @@ -3,7 +3,8 @@ "description": "N/A", "require": { "php": "~7.1.3||~7.2.0", - "magento/module-inventory-api": "*", + "magento/framework": "*", + "magento/module-inventory-api": "*" }, "type": "magento2-module", "license": [ From 780c359a0e6242f33ee4a921c96994a8c670bdd9 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Thu, 11 Apr 2019 14:20:50 +0300 Subject: [PATCH 116/231] Make Static Tests Happy --- InventoryLegacySynchronizationAdminUi/composer.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/InventoryLegacySynchronizationAdminUi/composer.json b/InventoryLegacySynchronizationAdminUi/composer.json index 8bb2631f646b..f291adc36ccc 100644 --- a/InventoryLegacySynchronizationAdminUi/composer.json +++ b/InventoryLegacySynchronizationAdminUi/composer.json @@ -3,8 +3,7 @@ "description": "N/A", "require": { "php": "~7.1.3||~7.2.0", - "magento/framework": "*", - "magento/module-backend": "*" + "magento/framework": "*" }, "type": "magento2-module", "license": [ From 459b22b8c36c9f1461aa5a08e2a52f9fccab8e03 Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Thu, 11 Apr 2019 23:59:12 +0300 Subject: [PATCH 117/231] MSI-2062: Inventory Export Stock: Refactoring --- .../Model/ExportStockData.php | 105 +++++++++++++-- .../Model/GetExportStockData.php | 124 ------------------ .../Model/ProductStockData.php | 49 ------- InventoryExportStock/etc/di.xml | 5 + .../Api/Data/ProductStockDataInterface.php | 47 ------- .../Api/ExportStockDataInterface.php | 7 +- InventoryExportStockApi/etc/webapi.xml | 3 +- 7 files changed, 104 insertions(+), 236 deletions(-) delete mode 100644 InventoryExportStock/Model/GetExportStockData.php delete mode 100644 InventoryExportStock/Model/ProductStockData.php delete mode 100644 InventoryExportStockApi/Api/Data/ProductStockDataInterface.php diff --git a/InventoryExportStock/Model/ExportStockData.php b/InventoryExportStock/Model/ExportStockData.php index 6479e57c95eb..222b65dba73e 100644 --- a/InventoryExportStock/Model/ExportStockData.php +++ b/InventoryExportStock/Model/ExportStockData.php @@ -8,28 +8,68 @@ namespace Magento\InventoryExportStock\Model; use Exception; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; use Magento\Framework\Api\SearchCriteriaInterface; +use Magento\Framework\Api\SearchResultsInterface; +use Magento\Framework\Exception\InputException; +use Magento\Framework\Exception\LocalizedException; +use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForSkuInterface; use Magento\InventoryExportStockApi\Api\Data\ExportStockDataSearchResultInterface; +use Magento\InventoryExportStockApi\Api\Data\ExportStockDataSearchResultInterfaceFactory; use Magento\InventoryExportStockApi\Api\ExportStockDataInterface; +use Magento\InventorySalesApi\Api\GetProductSalableQtyInterface; /** - * Class ExportStockData + * Class ExportStockData provides product stock information by search criteria */ class ExportStockData implements ExportStockDataInterface { /** - * @var GetExportStockData + * @var ProductRepositoryInterface */ - private $getExportStockData; + private $productRepository; /** - * ExportStockData constructor. - * @param GetExportStockData $getExportStockData + * @var ExportStockDataSearchResultInterfaceFactory + */ + private $exportStockDataSearchResultFactory; + + /** + * @var GetProductSalableQtyInterface + */ + private $getProductSalableQty; + + /** + * @var int + */ + private $qtyForNotManageStock; + /** + * @var IsSourceItemManagementAllowedForSkuInterface + */ + private $isSourceItemManagementAllowedForSku; + + /** + * ExportStockData constructor + * + * @param ProductRepositoryInterface $productRepository + * @param ExportStockDataSearchResultInterfaceFactory $exportStockDataSearchResultFactory + * @param GetProductSalableQtyInterface $getProductSalableQty + * @param IsSourceItemManagementAllowedForSkuInterface $isSourceItemManagementAllowedForSku + * @param int $qtyForNotManageStock */ public function __construct( - GetExportStockData $getExportStockData + ProductRepositoryInterface $productRepository, + ExportStockDataSearchResultInterfaceFactory $exportStockDataSearchResultFactory, + GetProductSalableQtyInterface $getProductSalableQty, + IsSourceItemManagementAllowedForSkuInterface $isSourceItemManagementAllowedForSku, + int $qtyForNotManageStock ) { - $this->getExportStockData = $getExportStockData; + $this->productRepository = $productRepository; + $this->exportStockDataSearchResultFactory = $exportStockDataSearchResultFactory; + $this->getProductSalableQty = $getProductSalableQty; + $this->isSourceItemManagementAllowedForSku = $isSourceItemManagementAllowedForSku; + $this->qtyForNotManageStock = $qtyForNotManageStock; } /** @@ -39,9 +79,54 @@ public function __construct( */ public function execute( SearchCriteriaInterface $searchCriteria, - int $stockId = null, - int $qtyForNotManageStock = 1 + int $stockId ): ExportStockDataSearchResultInterface { - return $this->getExportStockData->execute($searchCriteria, $stockId, $qtyForNotManageStock); + $productSearchResult = $this->getProducts($searchCriteria); + $items = $this->getProductStockDataArray($productSearchResult->getItems(), $stockId); + $searchResult = $this->exportStockDataSearchResultFactory->create(); + $searchResult->setSearchCriteria($productSearchResult->getSearchCriteria()); + $searchResult->setItems($items); + $searchResult->setTotalCount($productSearchResult->getTotalCount()); + + return $searchResult; + } + + /** + * Provides product search result by search criteria + * + * @param SearchCriteriaInterface $searchCriteria + * @return SearchResultsInterface + */ + private function getProducts(SearchCriteriaInterface $searchCriteria): SearchResultsInterface + { + return $this->productRepository->getList($searchCriteria); + } + + /** + * Provides salable qty and sku in array + * + * @param Product[] $products + * @param int $stockId + * @return array + * @throws InputException + * @throws LocalizedException + */ + private function getProductStockDataArray(array $products, int $stockId): array + { + $items = []; + foreach ($products as $product) { + $sku = $product->getSku(); + if ($this->isSourceItemManagementAllowedForSku->execute($sku)) { + $items[] = [ + 'sku' => $sku, + 'qty' => $this->getProductSalableQty->execute( + $sku, + $stockId + ) ?: $this->qtyForNotManageStock + ]; + } + } + + return $items; } } diff --git a/InventoryExportStock/Model/GetExportStockData.php b/InventoryExportStock/Model/GetExportStockData.php deleted file mode 100644 index 07156a39b96b..000000000000 --- a/InventoryExportStock/Model/GetExportStockData.php +++ /dev/null @@ -1,124 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryExportStock\Model; - -use Exception; -use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Catalog\Model\Product; -use Magento\Framework\Api\SearchCriteriaInterface; -use Magento\Framework\Api\SearchResultsInterface; -use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface; -use Magento\InventoryExportStockApi\Api\Data\ExportStockDataSearchResultInterface; -use Magento\InventoryExportStockApi\Api\Data\ExportStockDataSearchResultInterfaceFactory; -use Magento\InventorySalesApi\Api\GetProductSalableQtyInterface; - -/** - * Class GetExportStockData - */ -class GetExportStockData -{ - /** - * @var ProductRepositoryInterface - */ - private $productRepository; - - /** - * @var ExportStockDataSearchResultInterfaceFactory - */ - private $exportStockDataSearchResultFactory; - - /** - * @var GetProductSalableQtyInterface - */ - private $getProductSalableQty; - - /** - * @var DefaultStockProviderInterface - */ - private $defaultStockProvider; - - /** - * ExportStockData constructor - * - * @param ProductRepositoryInterface $productRepository - * @param ExportStockDataSearchResultInterfaceFactory $exportStockDataSearchResultFactory - * @param GetProductSalableQtyInterface $getProductSalableQty - * @param DefaultStockProviderInterface $defaultStockProvider - */ - public function __construct( - ProductRepositoryInterface $productRepository, - ExportStockDataSearchResultInterfaceFactory $exportStockDataSearchResultFactory, - GetProductSalableQtyInterface $getProductSalableQty, - DefaultStockProviderInterface $defaultStockProvider - ) { - $this->productRepository = $productRepository; - $this->exportStockDataSearchResultFactory = $exportStockDataSearchResultFactory; - $this->getProductSalableQty = $getProductSalableQty; - $this->defaultStockProvider = $defaultStockProvider; - } - - /** - * Provides product stock data according to search criteria - * - * @param SearchCriteriaInterface $searchCriteria - * @param int $stockId - * @param int $qtyForNotManageStock - * @return ExportStockDataSearchResultInterface - * @throws Exception - */ - public function execute( - SearchCriteriaInterface $searchCriteria, - int $stockId=null, - int $qtyForNotManageStock=1 - ): ExportStockDataSearchResultInterface { - $productSearchResult = $this->getProducts($searchCriteria); - $items = $this->getProductStockDataArray($productSearchResult->getItems(), $stockId, $qtyForNotManageStock); - $searchResult = $this->exportStockDataSearchResultFactory->create(); - $searchResult->setSearchCriteria($productSearchResult->getSearchCriteria()); - $searchResult->setItems($items); - $searchResult->setTotalCount($productSearchResult->getTotalCount()); - - return $searchResult; - } - - /** - * Provides product search result by search criteria - * - * @param SearchCriteriaInterface $searchCriteria - * @return SearchResultsInterface - */ - private function getProducts(SearchCriteriaInterface $searchCriteria): SearchResultsInterface - { - return $this->productRepository->getList($searchCriteria); - } - - /** - * Provides salable qty and sku in array - * - * @param Product[] $products - * @param int $stockId - * @param int $qtyForNotManageStock - * @return array - * @throws Exception - */ - private function getProductStockDataArray(array $products, int $stockId=null, int $qtyForNotManageStock=1): array - { - if (!$stockId) { - $stockId = $this->defaultStockProvider->getId(); - } - $items = []; - foreach ($products as $product) { - $items[] = [ - 'sku' => $product->getSku(), - 'qty' => $this->getProductSalableQty->execute($product->getSku(), $stockId) ?: $qtyForNotManageStock - ]; - } - - return $items; - } -} diff --git a/InventoryExportStock/Model/ProductStockData.php b/InventoryExportStock/Model/ProductStockData.php deleted file mode 100644 index 14e72bfa57c6..000000000000 --- a/InventoryExportStock/Model/ProductStockData.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryExportStock\Model; - -use Magento\Framework\Model\AbstractModel; -use Magento\InventoryExportStockApi\Api\Data\ProductStockDataInterface; - -/** - * Class ProductStockData - */ -class ProductStockData extends AbstractModel implements ProductStockDataInterface -{ - /** - * @inheritDoc - */ - public function setSku(string $sku): void - { - $this->setData(self::SKU, $sku); - } - - /** - * @inheritDoc - */ - public function getSku(): string - { - return $this->getData(self::SKU); - } - - /** - * @inheritDoc - */ - public function setQty(int $qty): void - { - $this->setData(self::QTY, $qty); - } - - /** - * @inheritDoc - */ - public function getQty(): int - { - return $this->getData(self::QTY); - } -} diff --git a/InventoryExportStock/etc/di.xml b/InventoryExportStock/etc/di.xml index d9b008fcfc4e..7acdc1758693 100644 --- a/InventoryExportStock/etc/di.xml +++ b/InventoryExportStock/etc/di.xml @@ -13,4 +13,9 @@ type="Magento\InventoryExportStock\Model\ProductStockData"/> <preference for="Magento\InventoryExportStockApi\Api\ExportStockDataInterface" type="Magento\InventoryExportStock\Model\ExportStockData"/> + <type name="Magento\InventoryExportStock\Model\ExportStockData"> + <arguments> + <argument name="qtyForNotManageStock" xsi:type="number">1</argument> + </arguments> + </type> </config> diff --git a/InventoryExportStockApi/Api/Data/ProductStockDataInterface.php b/InventoryExportStockApi/Api/Data/ProductStockDataInterface.php deleted file mode 100644 index 02129b9506e1..000000000000 --- a/InventoryExportStockApi/Api/Data/ProductStockDataInterface.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryExportStockApi\Api\Data; - -/** - * Interface ProductStockDataInterface - */ -interface ProductStockDataInterface -{ - public const SKU = 'sku'; - public const QTY = 'qty'; - - /** - * Sets SKU - * - * @param string $sku - * @return void - */ - public function setSku(string $sku): void; - - /** - * Provides SKU - * - * @return string - */ - public function getSku(): string; - - /** - * Sets QTY - * - * @param int $qty - * @return void - */ - public function setQty(int $qty): void; - - /** - * Provides QTY - * - * @return int - */ - public function getQty(): int; -} diff --git a/InventoryExportStockApi/Api/ExportStockDataInterface.php b/InventoryExportStockApi/Api/ExportStockDataInterface.php index 0ff3b5fd0730..cecadd2e85ba 100644 --- a/InventoryExportStockApi/Api/ExportStockDataInterface.php +++ b/InventoryExportStockApi/Api/ExportStockDataInterface.php @@ -2,13 +2,14 @@ /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. + * @noinspection PhpFullyQualifiedNameUsageInspection */ declare(strict_types=1); namespace Magento\InventoryExportStockApi\Api; /** - * Interface for ExportStockData + * Interface for ExportStockData provides product stock information by search criteria * @api */ interface ExportStockDataInterface @@ -18,12 +19,10 @@ interface ExportStockDataInterface * * @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria * @param int $stockId - * @param int $qtyForNotManageStock * @return \Magento\InventoryExportStockApi\Api\Data\ExportStockDataSearchResultInterface */ public function execute( \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria, - int $stockId = null, - int $qtyForNotManageStock = 1 + int $stockId ): \Magento\InventoryExportStockApi\Api\Data\ExportStockDataSearchResultInterface; } diff --git a/InventoryExportStockApi/etc/webapi.xml b/InventoryExportStockApi/etc/webapi.xml index fb269bb2d853..3f4f2b02057b 100644 --- a/InventoryExportStockApi/etc/webapi.xml +++ b/InventoryExportStockApi/etc/webapi.xml @@ -10,8 +10,7 @@ <route url="/V1/inventory/export-stock-data" method="GET"> <service class="Magento\InventoryExportStockApi\Api\ExportStockDataInterface" method="execute"/> <resources> -<!-- <resource ref="Magento_Catalog::products"/>--> - <resource ref="anonymous"/> + <resource ref="Magento_InventoryApi::stock"/> </resources> </route> </routes> From 94e71549814843a951b59a562a66763f4296f197 Mon Sep 17 00:00:00 2001 From: Riccardo Ugolini <riccardo.ugolini@magespecialist.it> Date: Fri, 12 Apr 2019 12:46:52 +0200 Subject: [PATCH 118/231] fix output issue --- .../ShowInconsistenciesInCompletedOrders.php | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/InventoryReservationCli/Command/ShowInconsistenciesInCompletedOrders.php b/InventoryReservationCli/Command/ShowInconsistenciesInCompletedOrders.php index 7afa4a0cb281..c639962f2bce 100644 --- a/InventoryReservationCli/Command/ShowInconsistenciesInCompletedOrders.php +++ b/InventoryReservationCli/Command/ShowInconsistenciesInCompletedOrders.php @@ -64,12 +64,10 @@ protected function configure() * * @param OutputInterface $output * @param array $itemsNotCompensated + * @param array $orders */ - private function prettyOutput(OutputInterface $output, array $itemsNotCompensated): void + private function prettyOutput(OutputInterface $output, array $itemsNotCompensated, array $orders): void { - /** @var OrderInterface[] $orders */ - $orders = $this->getOrderInFinalState->execute(array_keys($itemsNotCompensated)); - $output->writeln('<comment>Inconsistencies found on following entries:</comment>'); /** @var Order $order */ @@ -95,12 +93,10 @@ private function prettyOutput(OutputInterface $output, array $itemsNotCompensate * * @param OutputInterface $output * @param array $itemsNotCompensated + * @param array $orders */ - private function rawOutput(OutputInterface $output, array $itemsNotCompensated): void + private function rawOutput(OutputInterface $output, array $itemsNotCompensated, array $orders): void { - /** @var OrderInterface[] $orders */ - $orders = $this->getOrderInFinalState->execute(array_keys($itemsNotCompensated)); - /** @var Order $order */ foreach($orders as $order) { $inconsistentSkus = $itemsNotCompensated[$order->getId()]; @@ -125,15 +121,18 @@ public function execute(InputInterface $input, OutputInterface $output): int /** @var array $itemsNotCompensated */ $itemsNotCompensated = $this->getOrdersWithNotCompensatedReservations->execute(); - if (empty($itemsNotCompensated)) { + /** @var OrderInterface[] $orders */ + $orders = $this->getOrderInFinalState->execute(array_keys($itemsNotCompensated)); + + if (empty($orders)) { $output->writeln('<info>No order inconsistencies were found</info>'); return 0; } if ($input->getOption('raw')) { - $this->rawOutput($output, $itemsNotCompensated); + $this->rawOutput($output, $itemsNotCompensated, $orders); } else { - $this->prettyOutput($output, $itemsNotCompensated); + $this->prettyOutput($output, $itemsNotCompensated, $orders); } return -1; } From 1aeca386d908c9dc6112e068d945de6c2ddb8cbc Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Fri, 12 Apr 2019 16:45:59 +0300 Subject: [PATCH 119/231] MSI-2062: Inventory Export Stock: Creating rabbit and turtle processors for retrieving data fron db --- .../Model/ExportStockData.php | 66 +++----------- .../ExportStockProcessorInterface.php | 23 +++++ .../PreciseStockProcessor.php | 84 ++++++++++++++++++ .../StockExportProcessorPool.php | 58 +++++++++++++ .../StockIndexDumpProcessor.php | 79 +++++++++++++++++ .../Model/GetQtyForNotManageStock.php | 40 +++++++++ .../Model/ResourceModel/GetStockIndexDump.php | 85 +++++++++++++++++++ InventoryExportStock/etc/di.xml | 18 ++++ 8 files changed, 401 insertions(+), 52 deletions(-) create mode 100644 InventoryExportStock/Model/ExportStockProcessor/ExportStockProcessorInterface.php create mode 100644 InventoryExportStock/Model/ExportStockProcessor/PreciseStockProcessor.php create mode 100644 InventoryExportStock/Model/ExportStockProcessor/StockExportProcessorPool.php create mode 100644 InventoryExportStock/Model/ExportStockProcessor/StockIndexDumpProcessor.php create mode 100644 InventoryExportStock/Model/GetQtyForNotManageStock.php create mode 100644 InventoryExportStock/Model/ResourceModel/GetStockIndexDump.php diff --git a/InventoryExportStock/Model/ExportStockData.php b/InventoryExportStock/Model/ExportStockData.php index 222b65dba73e..e3ab10ea22d0 100644 --- a/InventoryExportStock/Model/ExportStockData.php +++ b/InventoryExportStock/Model/ExportStockData.php @@ -9,16 +9,12 @@ use Exception; use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Catalog\Model\Product; use Magento\Framework\Api\SearchCriteriaInterface; use Magento\Framework\Api\SearchResultsInterface; -use Magento\Framework\Exception\InputException; -use Magento\Framework\Exception\LocalizedException; -use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForSkuInterface; +use Magento\InventoryExportStock\Model\ExportStockProcessor\StockExportProcessorPool; use Magento\InventoryExportStockApi\Api\Data\ExportStockDataSearchResultInterface; use Magento\InventoryExportStockApi\Api\Data\ExportStockDataSearchResultInterfaceFactory; use Magento\InventoryExportStockApi\Api\ExportStockDataInterface; -use Magento\InventorySalesApi\Api\GetProductSalableQtyInterface; /** * Class ExportStockData provides product stock information by search criteria @@ -35,41 +31,34 @@ class ExportStockData implements ExportStockDataInterface */ private $exportStockDataSearchResultFactory; - /** - * @var GetProductSalableQtyInterface - */ - private $getProductSalableQty; - /** * @var int */ - private $qtyForNotManageStock; + private $processorType; + /** - * @var IsSourceItemManagementAllowedForSkuInterface + * @var StockExportProcessorPool */ - private $isSourceItemManagementAllowedForSku; + private $stockExportProcessorPool; /** * ExportStockData constructor * * @param ProductRepositoryInterface $productRepository * @param ExportStockDataSearchResultInterfaceFactory $exportStockDataSearchResultFactory - * @param GetProductSalableQtyInterface $getProductSalableQty - * @param IsSourceItemManagementAllowedForSkuInterface $isSourceItemManagementAllowedForSku - * @param int $qtyForNotManageStock + * @param StockExportProcessorPool $stockExportProcessorPool + * @param string $processor */ public function __construct( ProductRepositoryInterface $productRepository, ExportStockDataSearchResultInterfaceFactory $exportStockDataSearchResultFactory, - GetProductSalableQtyInterface $getProductSalableQty, - IsSourceItemManagementAllowedForSkuInterface $isSourceItemManagementAllowedForSku, - int $qtyForNotManageStock + StockExportProcessorPool $stockExportProcessorPool, + string $processor ) { $this->productRepository = $productRepository; $this->exportStockDataSearchResultFactory = $exportStockDataSearchResultFactory; - $this->getProductSalableQty = $getProductSalableQty; - $this->isSourceItemManagementAllowedForSku = $isSourceItemManagementAllowedForSku; - $this->qtyForNotManageStock = $qtyForNotManageStock; + $this->stockExportProcessorPool = $stockExportProcessorPool; + $this->processorType = $processor; } /** @@ -82,11 +71,12 @@ public function execute( int $stockId ): ExportStockDataSearchResultInterface { $productSearchResult = $this->getProducts($searchCriteria); - $items = $this->getProductStockDataArray($productSearchResult->getItems(), $stockId); + $processor = $this->stockExportProcessorPool->getStockExportProcessorByName($this->processorType); + $items = $processor->execute($productSearchResult->getItems(), $stockId); $searchResult = $this->exportStockDataSearchResultFactory->create(); $searchResult->setSearchCriteria($productSearchResult->getSearchCriteria()); $searchResult->setItems($items); - $searchResult->setTotalCount($productSearchResult->getTotalCount()); + $searchResult->setTotalCount(count($items)); return $searchResult; } @@ -101,32 +91,4 @@ private function getProducts(SearchCriteriaInterface $searchCriteria): SearchRes { return $this->productRepository->getList($searchCriteria); } - - /** - * Provides salable qty and sku in array - * - * @param Product[] $products - * @param int $stockId - * @return array - * @throws InputException - * @throws LocalizedException - */ - private function getProductStockDataArray(array $products, int $stockId): array - { - $items = []; - foreach ($products as $product) { - $sku = $product->getSku(); - if ($this->isSourceItemManagementAllowedForSku->execute($sku)) { - $items[] = [ - 'sku' => $sku, - 'qty' => $this->getProductSalableQty->execute( - $sku, - $stockId - ) ?: $this->qtyForNotManageStock - ]; - } - } - - return $items; - } } diff --git a/InventoryExportStock/Model/ExportStockProcessor/ExportStockProcessorInterface.php b/InventoryExportStock/Model/ExportStockProcessor/ExportStockProcessorInterface.php new file mode 100644 index 000000000000..1f41c62c96d6 --- /dev/null +++ b/InventoryExportStock/Model/ExportStockProcessor/ExportStockProcessorInterface.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryExportStock\Model\ExportStockProcessor; + +/** + * Interface StockExportProcessorInterface provides product stock data + */ +interface ExportStockProcessorInterface +{ + /** + * Provides product stock data + * + * @param array $products + * @param int $stockId + * @return array + */ + public function execute(array $products, int $stockId):array; +} diff --git a/InventoryExportStock/Model/ExportStockProcessor/PreciseStockProcessor.php b/InventoryExportStock/Model/ExportStockProcessor/PreciseStockProcessor.php new file mode 100644 index 000000000000..4f76fc5d73ad --- /dev/null +++ b/InventoryExportStock/Model/ExportStockProcessor/PreciseStockProcessor.php @@ -0,0 +1,84 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryExportStock\Model\ExportStockProcessor; + +use Magento\Framework\Exception\InputException; +use Magento\Framework\Exception\LocalizedException; +use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForSkuInterface; +use Magento\InventoryExportStock\Model\GetQtyForNotManageStock; +use Magento\InventorySalesApi\Api\GetProductSalableQtyInterface; + +/** + * Class Provides precise method of getting stock data + */ +class PreciseStockProcessor implements ExportStockProcessorInterface +{ + public const PROCESSOR_TYPE = 'precise'; + + /** + * @var IsSourceItemManagementAllowedForSkuInterface + */ + private $isSourceItemManagementAllowedForSku; + + /** + * @var GetProductSalableQtyInterface + */ + private $getProductSalableQty; + + /** + * @var GetQtyForNotManageStock + */ + private $getQtyForNotManageStock; + + /** + * PreciseStockProcessor constructor + * + * @param IsSourceItemManagementAllowedForSkuInterface $isSourceItemManagementAllowedForSku + * @param GetQtyForNotManageStock $getQtyForNotManageStock + * @param GetProductSalableQtyInterface $getProductSalableQty + */ + public function __construct( + IsSourceItemManagementAllowedForSkuInterface $isSourceItemManagementAllowedForSku, + GetQtyForNotManageStock $getQtyForNotManageStock, + GetProductSalableQtyInterface $getProductSalableQty + ) { + $this->isSourceItemManagementAllowedForSku = $isSourceItemManagementAllowedForSku; + $this->getProductSalableQty = $getProductSalableQty; + $this->getQtyForNotManageStock = $getQtyForNotManageStock; + } + + /** + * Provides precise method for getting stock data + * + * @param array $products + * @param int $stockId + * @return array + * @throws InputException + * @throws LocalizedException + */ + public function execute(array $products, int $stockId): array + { + $qtyForNotManageStock = $this->getQtyForNotManageStock->execute(); + $items = []; + foreach ($products as $product) { + $sku = $product->getSku(); + if ($this->isSourceItemManagementAllowedForSku->execute($sku)) { + $qty = $this->getProductSalableQty->execute($sku, $stockId) ?: $qtyForNotManageStock; + } else { + $qty = null; + } + + $items[] = [ + 'sku' => $sku, + 'qty' => $qty + ]; + } + + return $items; + } +} diff --git a/InventoryExportStock/Model/ExportStockProcessor/StockExportProcessorPool.php b/InventoryExportStock/Model/ExportStockProcessor/StockExportProcessorPool.php new file mode 100644 index 000000000000..20c0ed13dbd7 --- /dev/null +++ b/InventoryExportStock/Model/ExportStockProcessor/StockExportProcessorPool.php @@ -0,0 +1,58 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryExportStock\Model\ExportStockProcessor; + +use InvalidArgumentException; + +/** + * Class ProcessorPool provides processor by it's type + */ +class StockExportProcessorPool +{ + /** + * @var array + */ + private $processors; + + /** + * StockExportProcessorPool constructor + * + * @param array $processors + * @throws InvalidArgumentException + */ + public function __construct( + array $processors + ) { + $this->processors = $processors; + foreach ($this->processors as $processor) { + if (!$processor instanceof ExportStockProcessorInterface) { + throw new InvalidArgumentException( + __('One of processor is not instance of StockExportProcessorInterface class') + ); + } + } + } + + /** + * Provides processor by it's type + * + * @param string $processorsName + * @return ExportStockProcessorInterface + */ + public function getStockExportProcessorByName(string $processorsName): ExportStockProcessorInterface + { + foreach ($this->processors as $name => $processor) { + if ($processorsName === $name) { + return $processor; + } + } + throw new InvalidArgumentException( + __('Processor with such name is absent') + ); + } +} diff --git a/InventoryExportStock/Model/ExportStockProcessor/StockIndexDumpProcessor.php b/InventoryExportStock/Model/ExportStockProcessor/StockIndexDumpProcessor.php new file mode 100644 index 000000000000..816f66d6d84b --- /dev/null +++ b/InventoryExportStock/Model/ExportStockProcessor/StockIndexDumpProcessor.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryExportStock\Model\ExportStockProcessor; + +use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForSkuInterface; +use Magento\InventoryExportStock\Model\GetQtyForNotManageStock; +use Magento\InventoryExportStock\Model\ResourceModel\GetStockIndexDump; + +/** + * Class StockIndexDumpProcessor provides sku and qty of products dumping them from stock index table + */ +class StockIndexDumpProcessor implements ExportStockProcessorInterface +{ + public const PROCESSOR_TYPE = 'stock_dump'; + + /** + * @var GetQtyForNotManageStock + */ + private $getQtyForNotManageStock; + + /** + * @var IsSourceItemManagementAllowedForSkuInterface + */ + private $isSourceItemManagementAllowedForSku; + + /** + * @var GetStockIndexDump + */ + private $getStockIndexDump; + + /** + * GetStockIndexDumpProcessor constructor + * + * @param GetQtyForNotManageStock $getQtyForNotManageStock + * @param IsSourceItemManagementAllowedForSkuInterface $isSourceItemManagementAllowedForSku + * @param GetStockIndexDump $getStockIndexDump + */ + public function __construct( + GetQtyForNotManageStock $getQtyForNotManageStock, + IsSourceItemManagementAllowedForSkuInterface $isSourceItemManagementAllowedForSku, + GetStockIndexDump $getStockIndexDump + ) { + $this->getQtyForNotManageStock = $getQtyForNotManageStock; + $this->isSourceItemManagementAllowedForSku = $isSourceItemManagementAllowedForSku; + $this->getStockIndexDump = $getStockIndexDump; + } + + /** + * Provides sku and qty of products dumping them from stock index table + * + * @param array $products + * @param int $stockId + * @return array + */ + public function execute(array $products, int $stockId): array + { + $qtyForNotManageStock = $this->getQtyForNotManageStock->execute(); + $productStockIndex = $this->getStockIndexDump->execute($products, $stockId); + $items = []; + foreach ($productStockIndex as $index) { + if ($this->isSourceItemManagementAllowedForSku->execute($index['sku'])) { + $qty = $index['qty'] ?: $qtyForNotManageStock; + } else { + $qty = null; + } + $items[] = [ + 'sku' => $index['sku'], + 'qty' => $qty + ]; + } + + return $items; + } +} diff --git a/InventoryExportStock/Model/GetQtyForNotManageStock.php b/InventoryExportStock/Model/GetQtyForNotManageStock.php new file mode 100644 index 000000000000..50e3504d4d3c --- /dev/null +++ b/InventoryExportStock/Model/GetQtyForNotManageStock.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryExportStock\Model; + +/** + * Class Provides qty for not manage stock from di configuration + */ +class GetQtyForNotManageStock +{ + /** + * @var int + */ + private $qtyForNotManageStock; + + /** + * GetQtyForNotManageStock constructor + * + * @param int $qtyForNotManageStock + */ + public function __construct( + int $qtyForNotManageStock + ) { + $this->qtyForNotManageStock = $qtyForNotManageStock; + } + + /** + * Provides qty for not manage stock from di configuration + * + * @return int + */ + public function execute(): int + { + return $this->qtyForNotManageStock; + } +} diff --git a/InventoryExportStock/Model/ResourceModel/GetStockIndexDump.php b/InventoryExportStock/Model/ResourceModel/GetStockIndexDump.php new file mode 100644 index 000000000000..f7bbc031f8b7 --- /dev/null +++ b/InventoryExportStock/Model/ResourceModel/GetStockIndexDump.php @@ -0,0 +1,85 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryExportStock\Model\ResourceModel; + +use Magento\Framework\App\ResourceConnection; +use Magento\InventoryIndexer\Model\StockIndexTableNameResolverInterface; + +/** + * Class GetStockIndexDump provides sku and qty of products dumping them from stock index table + */ +class GetStockIndexDump +{ + /** + * @var StockIndexTableNameResolverInterface + */ + private $stockIndexTableNameResolver; + + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * GetStockIndexDump constructor + * + * @param StockIndexTableNameResolverInterface $stockIndexTableNameResolver + * @param ResourceConnection $resourceConnection + */ + public function __construct( + StockIndexTableNameResolverInterface $stockIndexTableNameResolver, + ResourceConnection $resourceConnection + + ) { + $this->stockIndexTableNameResolver = $stockIndexTableNameResolver; + $this->resourceConnection = $resourceConnection; + } + + /** + * Provides sku and qty of products dumping them from stock index table + * + * @param array $products + * @param int $stockId + * @return array + */ + public function execute(array $products, int $stockId): array + { + $stockIndexTableName = $this->stockIndexTableNameResolver->execute($stockId); + $tableName = $this->resourceConnection->getTableName($stockIndexTableName); + $connection = $this->resourceConnection->getConnection(); + $select = $connection->select() + ->from($tableName) + ->columns( + [ + 'sku' => 'sku', + 'qty' => 'quantity', + ] + )->where( + 'sku IN (?)', + $this->getProductSkus($products) + ); + + return $connection->fetchAll($select); + } + + /** + * Provides list of product skus by product array + * + * @param array $products + * @return string[] + */ + private function getProductSkus(array $products): array + { + $skus = []; + foreach ($products as $product) { + $skus[] = $product->getSku(); + } + + return $skus; + } +} diff --git a/InventoryExportStock/etc/di.xml b/InventoryExportStock/etc/di.xml index 7acdc1758693..2569625c59db 100644 --- a/InventoryExportStock/etc/di.xml +++ b/InventoryExportStock/etc/di.xml @@ -14,8 +14,26 @@ <preference for="Magento\InventoryExportStockApi\Api\ExportStockDataInterface" type="Magento\InventoryExportStock\Model\ExportStockData"/> <type name="Magento\InventoryExportStock\Model\ExportStockData"> + <arguments> + <argument name="processor" + xsi:type="const">Magento\InventoryExportStock\Model\ExportStockProcessor\StockIndexDumpProcessor::PROCESSOR_TYPE</argument> +<!-- <argument name="processor"--> +<!-- xsi:type="const">Magento\InventoryExportStock\Model\ExportStockProcessor\PreciseStockProcessor::PROCESSOR_TYPE</argument>--> + </arguments> + </type> + <type name="Magento\InventoryExportStock\Model\GetQtyForNotManageStock"> <arguments> <argument name="qtyForNotManageStock" xsi:type="number">1</argument> </arguments> </type> + <type name="Magento\InventoryExportStock\Model\ExportStockProcessor\StockExportProcessorPool"> + <arguments> + <argument name="processors" xsi:type="array"> + <item name="stock_dump" + xsi:type="object">Magento\InventoryExportStock\Model\ExportStockProcessor\StockIndexDumpProcessor</item> + <item name="precise" + xsi:type="object">Magento\InventoryExportStock\Model\ExportStockProcessor\PreciseStockProcessor</item> + </argument> + </arguments> + </type> </config> From f62072eaba41215eeafc5605c11289900fe42cbb Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Fri, 12 Apr 2019 16:49:09 +0200 Subject: [PATCH 120/231] FIX reindexation issue while settin in stock items --- .../Model/ToLegacy/SetDataToLegacyInventory.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/InventoryLegacySynchronization/Model/ToLegacy/SetDataToLegacyInventory.php b/InventoryLegacySynchronization/Model/ToLegacy/SetDataToLegacyInventory.php index 7531b00322b0..7e2251950d4a 100644 --- a/InventoryLegacySynchronization/Model/ToLegacy/SetDataToLegacyInventory.php +++ b/InventoryLegacySynchronization/Model/ToLegacy/SetDataToLegacyInventory.php @@ -14,6 +14,7 @@ use Magento\Framework\Exception\LocalizedException; use Magento\InventoryApi\Api\Data\SourceItemInterface; use Magento\InventoryCatalog\Model\GetProductIdsBySkus; +use Magento\InventoryIndexer\Indexer\SourceItem\SourceItemIndexer; use Magento\InventoryLegacySynchronization\Model\GetLegacyStockItemsByProductIds; use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; use Magento\InventoryLegacySynchronization\Model\ResourceModel\UpdateLegacyStockItemsData; @@ -53,10 +54,16 @@ class SetDataToLegacyInventory */ private $updateLegacyStockItemsData; + /** + * @var SourceItemIndexer + */ + private $sourceItemIndexer; + /** * @param UpdateLegacyStockItemsData $updateLegacyStockItemsData * @param DefaultSourceProviderInterface $defaultSourceProvider * @param GetLegacyStockItemsByProductIds $getLegacyStockItemsByProductIds + * @param SourceItemIndexer $sourceItemIndexer * @param StockStateProviderInterface $stockStateProvider * @param Processor $indexerProcessor * @param ProductResourceModel $productResourceModel @@ -66,6 +73,7 @@ public function __construct( UpdateLegacyStockItemsData $updateLegacyStockItemsData, DefaultSourceProviderInterface $defaultSourceProvider, GetLegacyStockItemsByProductIds $getLegacyStockItemsByProductIds, + SourceItemIndexer $sourceItemIndexer, StockStateProviderInterface $stockStateProvider, Processor $indexerProcessor, ProductResourceModel $productResourceModel @@ -76,6 +84,7 @@ public function __construct( $this->productResourceModel = $productResourceModel; $this->defaultSourceProvider = $defaultSourceProvider; $this->updateLegacyStockItemsData = $updateLegacyStockItemsData; + $this->sourceItemIndexer = $sourceItemIndexer; } /** @@ -141,6 +150,7 @@ public function execute(array $sourceItemsData): void if (!empty($productIds)) { $this->indexerProcessor->reindexList($productIds); + $this->sourceItemIndexer->executeList(array_column($sourceItemsData, 'source_item_id')); } } } From 62e35c125bab0bb0a393b47c51f53e462d65fb99 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Sat, 13 Apr 2019 13:05:26 +0300 Subject: [PATCH 121/231] magento-engcom/msi#2049: disabled source selection when disabled manage stock - Resolved unwanted dependencies --- .../Model/IsOrderSourceManageable.php | 27 ++++++++----------- InventoryShippingAdminUi/composer.json | 3 +-- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/InventoryShippingAdminUi/Model/IsOrderSourceManageable.php b/InventoryShippingAdminUi/Model/IsOrderSourceManageable.php index e09bc1ce844b..770c9bb09af0 100644 --- a/InventoryShippingAdminUi/Model/IsOrderSourceManageable.php +++ b/InventoryShippingAdminUi/Model/IsOrderSourceManageable.php @@ -7,11 +7,10 @@ namespace Magento\InventoryShippingAdminUi\Model; -use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Catalog\Model\Product; +use Magento\InventorySalesApi\Model\GetSkuFromOrderItemInterface; use Magento\InventoryApi\Api\Data\StockInterface; use Magento\InventoryApi\Api\StockRepositoryInterface; -use Magento\InventoryConfiguration\Model\GetStockItemConfiguration; +use Magento\InventoryConfigurationApi\Api\GetStockItemConfigurationInterface; use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForProductTypeInterface; use Magento\Sales\Api\Data\OrderInterface; @@ -21,12 +20,12 @@ class IsOrderSourceManageable { /** - * @var ProductRepositoryInterface + * @var GetSkuFromOrderItemInterface */ - private $productRepository; + private $getSkuFromOrderItem; /** - * @var GetStockItemConfiguration + * @var GetStockItemConfigurationInterface */ private $getStockItemConfiguration; @@ -41,18 +40,18 @@ class IsOrderSourceManageable private $isSourceItemManagementAllowedForProductType; /** - * @param ProductRepositoryInterface $productRepository - * @param GetStockItemConfiguration $getStockItemConfiguration + * @param GetSkuFromOrderItemInterface $productRepository + * @param GetStockItemConfigurationInterface $getStockItemConfiguration * @param StockRepositoryInterface $stockRepository * @param IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemManagementAllowedForProductType */ public function __construct( - ProductRepositoryInterface $productRepository, - GetStockItemConfiguration $getStockItemConfiguration, + GetSkuFromOrderItemInterface $productRepository, + GetStockItemConfigurationInterface $getStockItemConfiguration, StockRepositoryInterface $stockRepository, IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemManagementAllowedForProductType ) { - $this->productRepository = $productRepository; + $this->getSkuFromOrderItem = $productRepository; $this->getStockItemConfiguration = $getStockItemConfiguration; $this->stockRepository = $stockRepository; $this->isSourceItemManagementAllowedForProductType = $isSourceItemManagementAllowedForProductType; @@ -73,14 +72,10 @@ public function execute(OrderInterface $order): bool continue; } - $productId = $orderItem->getProductId(); - /** @var Product $product */ - $product = $this->productRepository->getById($productId); - /** @var StockInterface $stock */ foreach ($stocks as $stock) { $inventoryConfiguration = $this->getStockItemConfiguration->execute( - $product->getSku(), + $this->getSkuFromOrderItem->execute($orderItem), $stock->getStockId() ); diff --git a/InventoryShippingAdminUi/composer.json b/InventoryShippingAdminUi/composer.json index 4d81bf864de1..d3cd28df10b8 100644 --- a/InventoryShippingAdminUi/composer.json +++ b/InventoryShippingAdminUi/composer.json @@ -11,8 +11,7 @@ "magento/module-inventory-source-selection-api": "*", "magento/module-sales": "*", "magento/module-shipping": "*", - "magento/module-ui": "*", - "magento/module-catalog-inventory": "*" + "magento/module-ui": "*" }, "type": "magento2-module", "license": [ From 7e3bcda159eeefda82550041d8925559ed73e315 Mon Sep 17 00:00:00 2001 From: Maksym Novik <m.novik@ism-ukraine.com> Date: Sat, 13 Apr 2019 15:26:31 +0300 Subject: [PATCH 122/231] Product status is 'In Stock' on storefront even it is Out of Stock in all Sources #2156. Added integration test for case when there are backorders along with out of stock items --- .../Test/_files/source_items_out_of_stock.php | 89 +++++++++++++++++++ .../source_items_out_of_stock_rollback.php | 34 +++++++ .../IsProductSalableConditionChainTest.php | 74 +++++++++++++++ 3 files changed, 197 insertions(+) create mode 100644 InventoryApi/Test/_files/source_items_out_of_stock.php create mode 100644 InventoryApi/Test/_files/source_items_out_of_stock_rollback.php create mode 100644 InventorySales/Test/Integration/IsProductSalable/IsProductSalableConditionChainTest.php diff --git a/InventoryApi/Test/_files/source_items_out_of_stock.php b/InventoryApi/Test/_files/source_items_out_of_stock.php new file mode 100644 index 000000000000..8c49581f5768 --- /dev/null +++ b/InventoryApi/Test/_files/source_items_out_of_stock.php @@ -0,0 +1,89 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Api\DataObjectHelper; +use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryApi\Api\Data\SourceItemInterfaceFactory; +use Magento\InventoryApi\Api\SourceItemsSaveInterface; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var DataObjectHelper $dataObjectHelper */ +$dataObjectHelper = Bootstrap::getObjectManager()->get(DataObjectHelper::class); +/** @var SourceItemInterfaceFactory $sourceItemFactory */ +$sourceItemFactory = Bootstrap::getObjectManager()->get(SourceItemInterfaceFactory::class); +/** @var SourceItemsSaveInterface $sourceItemsSave */ +$sourceItemsSave = Bootstrap::getObjectManager()->get(SourceItemsSaveInterface::class); + +/** + * SKU-1 - EU-source-1(id:10) - 5.5qty + * SKU-1 - EU-source-2(id:20) - 3qty + * SKU-1 - EU-source-3(id:30) - 10qty (out of stock) + * SKU-1 - EU-source-4(id:40) - 10qty (disabled source) + * + * SKU-2 - US-source-1(id:30) - 5qty + * + * SKU-3 - EU-source-2(id:20) - 6qty (out of stock) + */ +$sourcesItemsData = [ + [ + SourceItemInterface::SOURCE_CODE => 'eu-1', + SourceItemInterface::SKU => 'SKU-1', + SourceItemInterface::QUANTITY => 5.5, + SourceItemInterface::STATUS => SourceItemInterface::STATUS_OUT_OF_STOCK, + ], + [ + SourceItemInterface::SOURCE_CODE => 'eu-2', + SourceItemInterface::SKU => 'SKU-1', + SourceItemInterface::QUANTITY => 3, + SourceItemInterface::STATUS => SourceItemInterface::STATUS_OUT_OF_STOCK, + ], + [ + SourceItemInterface::SOURCE_CODE => 'eu-3', + SourceItemInterface::SKU => 'SKU-1', + SourceItemInterface::QUANTITY => 10, + SourceItemInterface::STATUS => SourceItemInterface::STATUS_OUT_OF_STOCK, + ], + [ + SourceItemInterface::SOURCE_CODE => 'eu-disabled', + SourceItemInterface::SKU => 'SKU-1', + SourceItemInterface::QUANTITY => 10, + SourceItemInterface::STATUS => SourceItemInterface::STATUS_OUT_OF_STOCK, + ], + [ + SourceItemInterface::SOURCE_CODE => 'us-1', + SourceItemInterface::SKU => 'SKU-2', + SourceItemInterface::QUANTITY => 5, + SourceItemInterface::STATUS => SourceItemInterface::STATUS_OUT_OF_STOCK, + ], + [ + SourceItemInterface::SOURCE_CODE => 'eu-2', + SourceItemInterface::SKU => 'SKU-3', + SourceItemInterface::QUANTITY => 6, + SourceItemInterface::STATUS => SourceItemInterface::STATUS_OUT_OF_STOCK, + ], + [ + SourceItemInterface::SOURCE_CODE => 'eu-2', + SourceItemInterface::SKU => 'SKU-4', + SourceItemInterface::QUANTITY => 6, + SourceItemInterface::STATUS => SourceItemInterface::STATUS_OUT_OF_STOCK, + ], + [ + SourceItemInterface::SOURCE_CODE => 'eu-1', + SourceItemInterface::SKU => 'SKU-6', + SourceItemInterface::QUANTITY => 0, + SourceItemInterface::STATUS => SourceItemInterface::STATUS_OUT_OF_STOCK, + ], +]; + +$sourceItems = []; +foreach ($sourcesItemsData as $sourceItemData) { + /** @var SourceItemInterface $source */ + $sourceItem = $sourceItemFactory->create(); + $dataObjectHelper->populateWithArray($sourceItem, $sourceItemData, SourceItemInterface::class); + $sourceItems[] = $sourceItem; +} +$sourceItemsSave->execute($sourceItems); diff --git a/InventoryApi/Test/_files/source_items_out_of_stock_rollback.php b/InventoryApi/Test/_files/source_items_out_of_stock_rollback.php new file mode 100644 index 000000000000..14c3b615a0f2 --- /dev/null +++ b/InventoryApi/Test/_files/source_items_out_of_stock_rollback.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryApi\Api\SourceItemRepositoryInterface; +use Magento\InventoryApi\Api\SourceItemsDeleteInterface; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var SourceItemRepositoryInterface $sourceItemRepository */ +$sourceItemRepository = Bootstrap::getObjectManager()->get(SourceItemRepositoryInterface::class); +/** @var SourceItemsDeleteInterface $sourceItemsDelete */ +$sourceItemsDelete = Bootstrap::getObjectManager()->get(SourceItemsDeleteInterface::class); +/** @var SearchCriteriaBuilder $searchCriteriaBuilder */ +$searchCriteriaBuilder = Bootstrap::getObjectManager()->get(SearchCriteriaBuilder::class); + +$searchCriteria = $searchCriteriaBuilder->addFilter( + SourceItemInterface::SKU, + ['SKU-1', 'SKU-2', 'SKU-3', 'SKU-4'], + 'in' +)->create(); +$sourceItems = $sourceItemRepository->getList($searchCriteria)->getItems(); + +/** + * Tests which are wrapped with MySQL transaction clear all data by transaction rollback. + * In that case there is "if" which checks that SKU1, SKU2 and SKU3 still exists in database. + */ +if (!empty($sourceItems)) { + $sourceItemsDelete->execute($sourceItems); +} diff --git a/InventorySales/Test/Integration/IsProductSalable/IsProductSalableConditionChainTest.php b/InventorySales/Test/Integration/IsProductSalable/IsProductSalableConditionChainTest.php new file mode 100644 index 000000000000..919a4218454e --- /dev/null +++ b/InventorySales/Test/Integration/IsProductSalable/IsProductSalableConditionChainTest.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventorySales\Test\Integration\IsProductSalable; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\CatalogInventory\Api\Data\StockItemInterface; +use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory; +use Magento\CatalogInventory\Api\StockItemRepositoryInterface; +use Magento\InventorySales\Model\IsProductSalableCondition\IsProductSalableConditionChain; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +class IsProductSalableConditionChainTest extends TestCase +{ + /** + * @var IsProductSalableConditionChain + */ + private $isProductSalableConditionChain; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var StockItemCriteriaInterfaceFactory + */ + private $stockItemCriteriaFactory; + + /** + * @var StockItemRepositoryInterface + */ + private $stockItemRepository; + + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->isProductSalableConditionChain = $objectManager->get( + IsProductSalableConditionChain::class + ); + $this->productRepository = $objectManager->get(ProductRepositoryInterface::class); + $this->stockItemCriteriaFactory = $objectManager->get(StockItemCriteriaInterfaceFactory::class); + $this->stockItemRepository = $objectManager->get(StockItemRepositoryInterface::class); + } + + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stocks.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stock_source_links.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items_out_of_stock.php + * + * @magentoDbIsolation disabled + */ + public function testSourcesItemsAreOutOfStock() + { + $product = $this->productRepository->get('SKU-1'); + $stockItemSearchCriteria = $this->stockItemCriteriaFactory->create(); + $stockItemSearchCriteria->setProductsFilter($product->getId()); + $stockItemsCollection = $this->stockItemRepository->getList($stockItemSearchCriteria); + + /** @var StockItemInterface $legacyStockItem */ + $legacyStockItem = current($stockItemsCollection->getItems()); + $legacyStockItem->setBackorders(1); + $legacyStockItem->setUseConfigBackorders(0); + $this->stockItemRepository->save($legacyStockItem); + $this->assertFalse($this->isProductSalableConditionChain->execute('SKU-1', 10)); + } +} From 9967acec6ed7d8d76cc68592be0fcb5ec4bfeb75 Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Sat, 13 Apr 2019 14:47:19 +0200 Subject: [PATCH 123/231] FIXES after merging partial inventory transfer bulk operations --- .../TransferInventoryPartially.php | 51 +++++------ .../Model/Synchronize.php | 2 +- ...nventoryAtBulkPartialInventoryTransfer.php | 85 +++++++++++++++++++ InventoryLegacySynchronization/etc/di.xml | 4 + 4 files changed, 113 insertions(+), 29 deletions(-) create mode 100644 InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtBulkPartialInventoryTransfer.php diff --git a/InventoryCatalog/Model/ResourceModel/TransferInventoryPartially.php b/InventoryCatalog/Model/ResourceModel/TransferInventoryPartially.php index 05d565bd9c53..141ec3f007ee 100644 --- a/InventoryCatalog/Model/ResourceModel/TransferInventoryPartially.php +++ b/InventoryCatalog/Model/ResourceModel/TransferInventoryPartially.php @@ -11,33 +11,22 @@ use Magento\Framework\App\ResourceConnection; use Magento\Inventory\Model\ResourceModel\SourceItem; use Magento\InventoryApi\Api\Data\SourceItemInterface; -use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferItemInterface; class TransferInventoryPartially { - /** @var ResourceConnection */ + /** + * @var ResourceConnection + */ private $resourceConnection; - /** @var DefaultSourceProviderInterface */ - private $defaultSourceProvider; - - /** @var SetDataToLegacyStockItem */ - private $setDataToLegacyStockItemCommand; - /** * @param ResourceConnection $resourceConnection - * @param DefaultSourceProviderInterface $defaultSourceProvider - * @param SetDataToLegacyStockItem $setDataToLegacyCatalogInventoryCommand */ public function __construct( - ResourceConnection $resourceConnection, - DefaultSourceProviderInterface $defaultSourceProvider, - SetDataToLegacyStockItem $setDataToLegacyCatalogInventoryCommand + ResourceConnection $resourceConnection ) { $this->resourceConnection = $resourceConnection; - $this->defaultSourceProvider = $defaultSourceProvider; - $this->setDataToLegacyStockItemCommand = $setDataToLegacyCatalogInventoryCommand; } /** @@ -45,8 +34,11 @@ public function __construct( * @param string $originSourceCode * @param string $destinationSourceCode */ - public function execute(PartialInventoryTransferItemInterface $transfer, string $originSourceCode, string $destinationSourceCode): void - { + public function execute( + PartialInventoryTransferItemInterface $transfer, + string $originSourceCode, + string $destinationSourceCode + ): void { $tableName = $this->resourceConnection->getTableName(SourceItem::TABLE_NAME_SOURCE_ITEM); $connection = $this->resourceConnection->getConnection(); $connection->beginTransaction(); @@ -54,11 +46,20 @@ public function execute(PartialInventoryTransferItemInterface $transfer, string $originSourceItemData = $this->getSourceItemData($transfer->getSku(), $originSourceCode); $destSourceItemData = $this->getSourceItemData($transfer->getSku(), $destinationSourceCode); - $updatedQtyAtOrigin = $originSourceItemData === null ? 0.0 : (float) $originSourceItemData[SourceItemInterface::QUANTITY] - $transfer->getQty(); - $updatedQtyAtDest = $destSourceItemData === null ? 0.0 : (float) $destSourceItemData[SourceItemInterface::QUANTITY] + $transfer->getQty(); - - $originUpdate = [SourceItemInterface::QUANTITY => $updatedQtyAtOrigin]; - $destUpdate = [SourceItemInterface::QUANTITY => $updatedQtyAtDest, SourceItemInterface::STATUS => SourceItemInterface::STATUS_IN_STOCK]; + $updatedQtyAtOrigin = $originSourceItemData === null ? + 0.0 : + (float) $originSourceItemData[SourceItemInterface::QUANTITY] - $transfer->getQty(); + $updatedQtyAtDest = $destSourceItemData === null ? + 0.0 : + (float) $destSourceItemData[SourceItemInterface::QUANTITY] + $transfer->getQty(); + + $originUpdate = [ + SourceItemInterface::QUANTITY => $updatedQtyAtOrigin + ]; + $destUpdate = [ + SourceItemInterface::QUANTITY => $updatedQtyAtDest, + SourceItemInterface::STATUS => SourceItemInterface::STATUS_IN_STOCK + ]; $connection->update($tableName, $originUpdate, [ SourceItemInterface::SOURCE_CODE . '=?' => $originSourceCode, @@ -69,12 +70,6 @@ public function execute(PartialInventoryTransferItemInterface $transfer, string SourceItemInterface::SKU . '=?' => $transfer->getSku(), ]); - if ($originSourceCode === $this->defaultSourceProvider->getCode()) { - $this->setDataToLegacyStockItemCommand->execute($transfer->getSku(), $updatedQtyAtOrigin, $originSourceItemData[SourceItemInterface::STATUS]); - } elseif ($destinationSourceCode === $this->defaultSourceProvider->getCode()) { - $this->setDataToLegacyStockItemCommand->execute($transfer->getSku(), $updatedQtyAtDest, SourceItemInterface::STATUS_IN_STOCK); - } - $connection->commit(); } diff --git a/InventoryLegacySynchronization/Model/Synchronize.php b/InventoryLegacySynchronization/Model/Synchronize.php index d60bde0a8706..7c46be8728aa 100644 --- a/InventoryLegacySynchronization/Model/Synchronize.php +++ b/InventoryLegacySynchronization/Model/Synchronize.php @@ -121,7 +121,7 @@ private function executeAsync(string $destination, array $items): void ] ]; - /** @var \Magento\AsynchronousOperations\Api\Data\OperationInterface $asyncOperation */ + /** @var OperationInterface $asyncOperation */ $asyncOperation = $this->operationInterfaceFactory->create($data); $asyncOperations[] = $asyncOperation; } diff --git a/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtBulkPartialInventoryTransfer.php b/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtBulkPartialInventoryTransfer.php new file mode 100644 index 000000000000..146b8caaac3f --- /dev/null +++ b/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtBulkPartialInventoryTransfer.php @@ -0,0 +1,85 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryLegacySynchronization\Plugin; + +use Magento\Framework\Exception\LocalizedException; +use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryCatalog\Model\GetDefaultSourceItemBySku; +use Magento\InventoryCatalog\Model\ResourceModel\TransferInventoryPartially; +use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferItemInterface; +use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; +use Magento\InventoryLegacySynchronization\Model\Synchronize; + +class SetDataToLegacyCatalogInventoryAtBulkPartialInventoryTransfer +{ + /** + * @var Synchronize + */ + private $synchronize; + + /** + * @var DefaultSourceProviderInterface + */ + private $defaultSourceProvider; + + /** + * @var GetDefaultSourceItemBySku + */ + private $getDefaultSourceItemBySku; + + /** + * @param Synchronize $synchronize + * @param DefaultSourceProviderInterface $defaultSourceProvider + * @param GetDefaultSourceItemBySku $getDefaultSourceItemBySku + * @SuppressWarnings(PHPMD.LongVariable) + */ + public function __construct( + Synchronize $synchronize, + DefaultSourceProviderInterface $defaultSourceProvider, + GetDefaultSourceItemBySku $getDefaultSourceItemBySku + ) { + $this->synchronize = $synchronize; + $this->defaultSourceProvider = $defaultSourceProvider; + $this->getDefaultSourceItemBySku = $getDefaultSourceItemBySku; + } + + /** + * @param TransferInventoryPartially $subject + * @param $result + * @param PartialInventoryTransferItemInterface $transfer + * @param string $originSourceCode + * @param string $destinationSourceCode + * @throws LocalizedException + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @SuppressWarnings(PHPMD.LongVariable) + */ + public function afterExecute( + TransferInventoryPartially $subject, + $result, + PartialInventoryTransferItemInterface $transfer, + string $originSourceCode, + string $destinationSourceCode + ): void { + $defaultSourceCode = $this->defaultSourceProvider->getCode(); + + if ($originSourceCode === $defaultSourceCode || $destinationSourceCode === $defaultSourceCode) { + $sourceItem = $this->getDefaultSourceItemBySku->execute($transfer->getSku()); + if ($sourceItem !== null) { + $this->synchronize->execute( + Synchronize::MSI_TO_LEGACY, + [ + SourceItemInterface::QUANTITY => $sourceItem->getQuantity(), + SourceItemInterface::SKU => $transfer->getSku(), + SourceItemInterface::SOURCE_CODE => $defaultSourceCode, + SourceItemInterface::STATUS => $sourceItem->getStatus() + ] + ); + } + } + } +} diff --git a/InventoryLegacySynchronization/etc/di.xml b/InventoryLegacySynchronization/etc/di.xml index 5ebf6d53b707..8d9e66b27595 100644 --- a/InventoryLegacySynchronization/etc/di.xml +++ b/InventoryLegacySynchronization/etc/di.xml @@ -40,4 +40,8 @@ <plugin name="set_data_to_legacy_catalog_inventory_at_bulk_unassign" type="Magento\InventoryLegacySynchronization\Plugin\SetDataToLegacyCatalogInventoryAtBulkTransfer"/> </type> + <type name="Magento\InventoryCatalog\Model\ResourceModel\TransferInventoryPartially"> + <plugin sortOrder="1" name="magentoInventoryLegacySynchronizationTransferInventoryPartially" + type="Magento\InventoryLegacySynchronization\Plugin\SetDataToLegacyCatalogInventoryOnBulkPartialInventiryTransfer.php"/> + </type> </config> From c20ca90afa0409b173d5ce81f8d26783092772aa Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Sat, 13 Apr 2019 14:53:33 +0200 Subject: [PATCH 124/231] FIX class name typo --- ...ataToLegacyCatalogInventoryAtTransferInventoryPartial.php} | 2 +- InventoryLegacySynchronization/etc/di.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename InventoryLegacySynchronization/Plugin/{SetDataToLegacyCatalogInventoryAtBulkPartialInventoryTransfer.php => SetDataToLegacyCatalogInventoryAtTransferInventoryPartial.php} (97%) diff --git a/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtBulkPartialInventoryTransfer.php b/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtTransferInventoryPartial.php similarity index 97% rename from InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtBulkPartialInventoryTransfer.php rename to InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtTransferInventoryPartial.php index 146b8caaac3f..0db4b098f9c7 100644 --- a/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtBulkPartialInventoryTransfer.php +++ b/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtTransferInventoryPartial.php @@ -15,7 +15,7 @@ use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; use Magento\InventoryLegacySynchronization\Model\Synchronize; -class SetDataToLegacyCatalogInventoryAtBulkPartialInventoryTransfer +class SetDataToLegacyCatalogInventoryAtTransferInventoryPartial { /** * @var Synchronize diff --git a/InventoryLegacySynchronization/etc/di.xml b/InventoryLegacySynchronization/etc/di.xml index 8d9e66b27595..3569a596aa27 100644 --- a/InventoryLegacySynchronization/etc/di.xml +++ b/InventoryLegacySynchronization/etc/di.xml @@ -41,7 +41,7 @@ type="Magento\InventoryLegacySynchronization\Plugin\SetDataToLegacyCatalogInventoryAtBulkTransfer"/> </type> <type name="Magento\InventoryCatalog\Model\ResourceModel\TransferInventoryPartially"> - <plugin sortOrder="1" name="magentoInventoryLegacySynchronizationTransferInventoryPartially" - type="Magento\InventoryLegacySynchronization\Plugin\SetDataToLegacyCatalogInventoryOnBulkPartialInventiryTransfer.php"/> + <plugin name="set_data_to_legacy_catalog_inventory_at_transfer_inventory_partial" + type="Magento\InventoryLegacySynchronization\Plugin\SetDataToLegacyCatalogInventoryAtTransferInventoryPartial"/> </type> </config> From e2e79729941cb54ed75e5fd8f3956742b3f876bf Mon Sep 17 00:00:00 2001 From: Vadim Justus <v.justus@techdivision.com> Date: Sat, 13 Apr 2019 13:51:36 +0200 Subject: [PATCH 125/231] magento-engcom/msi#2169: Implement reservations validation in CLI for incomplete orders - added CLI command `inventory:reservation:list-missing-reservation` - adden integration tests --- ...ShowInconsistenciesForIncompleteOrders.php | 137 ++++++++++++++++++ .../Model/GetOrdersInNotFinalState.php | 62 ++++++++ ...etOrdersWithMissingInitialReservations.php | 84 +++++++++++ ...ingReservationsForIncompleteOrdersTest.php | 57 ++++++++ ...eate_incomplete_order_with_reservation.php | 75 ++++++++++ InventoryReservationCli/etc/di.xml | 3 + 6 files changed, 418 insertions(+) create mode 100644 InventoryReservationCli/Command/ShowInconsistenciesForIncompleteOrders.php create mode 100644 InventoryReservationCli/Model/GetOrdersInNotFinalState.php create mode 100644 InventoryReservationCli/Model/GetOrdersWithMissingInitialReservations.php create mode 100644 InventoryReservationCli/Test/Integration/Model/GetListMissingReservationsForIncompleteOrdersTest.php create mode 100644 InventoryReservationCli/Test/Integration/_fixtures/create_incomplete_order_with_reservation.php diff --git a/InventoryReservationCli/Command/ShowInconsistenciesForIncompleteOrders.php b/InventoryReservationCli/Command/ShowInconsistenciesForIncompleteOrders.php new file mode 100644 index 000000000000..e42d4612aa73 --- /dev/null +++ b/InventoryReservationCli/Command/ShowInconsistenciesForIncompleteOrders.php @@ -0,0 +1,137 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservationCli\Command; + +use Magento\InventoryReservationCli\Model\GetOrdersInNotFinalState; +use Magento\InventoryReservationCli\Model\GetOrdersWithMissingInitialReservations; +use Magento\InventoryReservationCli\Model\GetOrdersWithNotCompensatedReservations; +use Magento\Sales\Model\Order; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Outputs a list of incomplete orders, which have not a initial reservation. + * + * This command may be used to simplify migrations from Magento versions without new Inventory or to track down + * incorrect behavior of customizations. + */ +class ShowInconsistenciesForIncompleteOrders extends Command +{ + /** + * @var GetOrdersInNotFinalState + */ + private $getOrdersInNotFinalState; + + /** + * @var GetOrdersWithMissingInitialReservations + */ + private $getOrdersWithMissingInitialReservations; + + /** + * @param GetOrdersWithMissingInitialReservations $getOrdersWithMissingInitialReservations + * @param GetOrdersInNotFinalState $getOrdersInNotFinalState + */ + public function __construct( + GetOrdersWithMissingInitialReservations $getOrdersWithMissingInitialReservations, + GetOrdersInNotFinalState $getOrdersInNotFinalState + ) { + $this->getOrdersWithMissingInitialReservations = $getOrdersWithMissingInitialReservations; + $this->getOrdersInNotFinalState = $getOrdersInNotFinalState; + parent::__construct(); + } + + /** + * @inheritdoc + */ + protected function configure() + { + $this + ->setName('inventory:reservation:list-missing-reservation') + ->setDescription('Show all orders and products without initial reservation') + ->addOption('raw', 'r', InputOption::VALUE_NONE, 'Raw output'); + + parent::configure(); + } + + /** + * Format output + * + * @param OutputInterface $output + * @param array $inconsistentData + */ + private function prettyOutput(OutputInterface $output, array $inconsistentData): void + { + $output->writeln('<comment>Inconsistencies found on following entries:</comment>'); + + /** @var Order $order */ + foreach ($inconsistentData as $inconsistentOrder) { + $inconsistentSkus = $inconsistentOrder['skus']; + $incrementId = $inconsistentOrder['increment_id']; + + $output->writeln(sprintf('Order <comment>%s</comment>:', $incrementId)); + + foreach ($inconsistentSkus as $inconsistentSku => $qty) { + $output->writeln( + sprintf( + ' - Product <comment>%s</comment> should be compensated by <comment>%+f</comment>', + $inconsistentSku, + -$qty + ) + ); + } + } + } + + /** + * Output without formatting + * + * @param OutputInterface $output + * @param array $inconsistentData + */ + private function rawOutput(OutputInterface $output, array $inconsistentData): void + { + /** @var Order $order */ + foreach ($inconsistentData as $inconsistentOrder) { + $inconsistentSkus = $inconsistentOrder['skus']; + $incrementId = $inconsistentOrder['increment_id']; + + foreach ($inconsistentSkus as $inconsistentSku => $qty) { + $output->writeln( + sprintf('%s:%s:%f', $incrementId, $inconsistentSku, -$qty) + ); + } + } + } + + /** + * {@inheritdoc} + * + * @param InputInterface $input + * @param OutputInterface $output + * @return int + */ + public function execute(InputInterface $input, OutputInterface $output): int + { + $incompleteOrders = $this->getOrdersInNotFinalState->execute(); + $inconsistentData = $this->getOrdersWithMissingInitialReservations->execute($incompleteOrders); + + if (empty($inconsistentData)) { + $output->writeln('<info>No order inconsistencies were found</info>'); + return 0; + } + + if ($input->getOption('raw')) { + $this->rawOutput($output, $inconsistentData); + } else { + $this->prettyOutput($output, $inconsistentData); + } + return -1; + } +} diff --git a/InventoryReservationCli/Model/GetOrdersInNotFinalState.php b/InventoryReservationCli/Model/GetOrdersInNotFinalState.php new file mode 100644 index 000000000000..358c0d16a947 --- /dev/null +++ b/InventoryReservationCli/Model/GetOrdersInNotFinalState.php @@ -0,0 +1,62 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservationCli\Model; + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Api\SearchCriteriaInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order; + +/** + * Get list of orders, which are not in any of the final states (Complete, Closed, Canceled). + */ +class GetOrdersInNotFinalState +{ + /** + * @var OrderRepositoryInterface + */ + private $orderRepository; + + /** + * @var SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + + /** + * @param OrderRepositoryInterface $orderRepository + * @param SearchCriteriaBuilder $searchCriteriaBuilder + */ + public function __construct( + OrderRepositoryInterface $orderRepository, + SearchCriteriaBuilder $searchCriteriaBuilder + ) { + $this->orderRepository = $orderRepository; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + } + + /** + * Get list of orders + * + * @return OrderInterface[] + */ + public function execute(): array + { + /** @var SearchCriteriaInterface $filter */ + $filter = $this->searchCriteriaBuilder + ->addFilter('state', [ + Order::STATE_COMPLETE, + Order::STATE_CLOSED, + Order::STATE_CANCELED + ], 'nin') + ->create(); + + $orderSearchResult = $this->orderRepository->getList($filter); + return $orderSearchResult->getItems(); + } +} diff --git a/InventoryReservationCli/Model/GetOrdersWithMissingInitialReservations.php b/InventoryReservationCli/Model/GetOrdersWithMissingInitialReservations.php new file mode 100644 index 000000000000..0bca705059d1 --- /dev/null +++ b/InventoryReservationCli/Model/GetOrdersWithMissingInitialReservations.php @@ -0,0 +1,84 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservationCli\Model; + +use Magento\Framework\Serialize\SerializerInterface; +use Magento\InventoryReservationCli\Model\ResourceModel\GetReservationsList; +use Magento\Sales\Api\Data\OrderInterface; + +/** + * Filter orders for missing initial reservation + */ +class GetOrdersWithMissingInitialReservations +{ + /** + * @var GetReservationsList + */ + private $getReservationsList; + + /** + * @var SerializerInterface + */ + private $serialize; + + /** + * @param GetReservationsList $getReservationsList + * @param SerializerInterface $serialize + */ + public function __construct( + GetReservationsList $getReservationsList, + SerializerInterface $serialize + ) { + $this->getReservationsList = $getReservationsList; + $this->serialize = $serialize; + } + + /** + * Get list of reservations for Order entity. + * + * @param OrderInterface[] $orders + * @return array + */ + public function execute(array $orders): array + { + $entityIdAndSkuList = $this->getEntityIdAndSkuList($orders); + + $reservationList = $this->getReservationsList->execute(); + foreach ($reservationList as $reservation) { + $metadata = $this->serialize->unserialize($reservation['metadata']); + $objectId = $metadata['object_id']; + $eventType = $metadata['event_type']; + + if ($eventType == 'order_placed' && in_array($objectId, array_keys($entityIdAndSkuList))) { + unset($entityIdAndSkuList[$objectId]); + } + } + + return $entityIdAndSkuList; + } + + /** + * @param OrderInterface[] $orders + * @return array + */ + private function getEntityIdAndSkuList(array $orders): array + { + $list = []; + foreach ($orders as $order) { + $entityId = $order->getEntityId(); + $list[$entityId] = [ + 'increment_id' => $order->getIncrementId(), + 'skus' => [] + ]; + foreach ($order->getItems() as $item) { + $list[$entityId]['skus'][$item->getSku()] = (float)$item->getQtyOrdered(); + } + } + return $list; + } +} diff --git a/InventoryReservationCli/Test/Integration/Model/GetListMissingReservationsForIncompleteOrdersTest.php b/InventoryReservationCli/Test/Integration/Model/GetListMissingReservationsForIncompleteOrdersTest.php new file mode 100644 index 000000000000..5f77017717f7 --- /dev/null +++ b/InventoryReservationCli/Test/Integration/Model/GetListMissingReservationsForIncompleteOrdersTest.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservationCli\Test\Integration\Model; + +use Magento\InventoryReservationCli\Model\GetOrdersInNotFinalState; +use Magento\InventoryReservationCli\Model\GetOrdersWithMissingInitialReservations; +use PHPUnit\Framework\TestCase; +use Magento\TestFramework\Helper\Bootstrap; + +class GetListMissingReservationsForIncompleteOrdersTest extends TestCase +{ + /** + * @var GetOrdersWithMissingInitialReservations + */ + private $getOrdersWithMissingInitialReservations; + + /** + * @var GetOrdersInNotFinalState + */ + private $getOrdersInNotFinalState; + + /** + * Initialize test dependencies + */ + protected function setUp() + { + $this->getOrdersWithMissingInitialReservations + = Bootstrap::getObjectManager()->get(GetOrdersWithMissingInitialReservations::class); + $this->getOrdersInNotFinalState + = Bootstrap::getObjectManager()->get(GetOrdersInNotFinalState::class); + } + + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryReservationCli/Test/Integration/_fixtures/create_incomplete_order_with_reservation.php + */ + public function testShouldNotFindAnyInconsistency(): void + { + $incompleteOrders = $this->getOrdersInNotFinalState->execute(); + $missingReservations = $this->getOrdersWithMissingInitialReservations->execute($incompleteOrders); + self::assertSame([], $missingReservations); + } + + /** + * @magentoDataFixture Magento/Sales/_files/order.php + */ + public function testShouldFindOneInconsistency(): void + { + $incompleteOrders = $this->getOrdersInNotFinalState->execute(); + $missingReservations = $this->getOrdersWithMissingInitialReservations->execute($incompleteOrders); + self::assertCount(1, $missingReservations); + } +} diff --git a/InventoryReservationCli/Test/Integration/_fixtures/create_incomplete_order_with_reservation.php b/InventoryReservationCli/Test/Integration/_fixtures/create_incomplete_order_with_reservation.php new file mode 100644 index 000000000000..c2d3a7b7c819 --- /dev/null +++ b/InventoryReservationCli/Test/Integration/_fixtures/create_incomplete_order_with_reservation.php @@ -0,0 +1,75 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Address as OrderAddress; +use Magento\Sales\Model\Order\Item as OrderItem; +use Magento\Sales\Model\Order\Payment; +use Magento\Store\Model\StoreManagerInterface; + +require __DIR__ . '/../../../../../../../dev/tests/integration/testsuite/Magento/Sales/_files/default_rollback.php'; +require __DIR__ . '/../../../../../../../dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple.php'; +/** @var \Magento\Catalog\Model\Product $product */ + +$addressData = include __DIR__ . '/../../../../../../../dev/tests/integration/testsuite/Magento/Sales/_files/address_data.php'; + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +$billingAddress = $objectManager->create(OrderAddress::class, ['data' => $addressData]); +$billingAddress->setAddressType('billing'); + +$shippingAddress = clone $billingAddress; +$shippingAddress->setId(null)->setAddressType('shipping'); + +/** @var Payment $payment */ +$payment = $objectManager->create(Payment::class); +$payment->setMethod('checkmo') + ->setAdditionalInformation('last_trans_id', '11122') + ->setAdditionalInformation( + 'metadata', + [ + 'type' => 'free', + 'fraudulent' => false, + ] + ); + +/** @var OrderItem $orderItem */ +$orderItem = $objectManager->create(OrderItem::class); +$orderItem->setProductId($product->getId()) + ->setQtyOrdered(2) + ->setSku($product->getSku()) + ->setBasePrice($product->getPrice()) + ->setPrice($product->getPrice()) + ->setRowTotal($product->getPrice()) + ->setProductType('simple') + ->setName($product->getName()); + +/** @var Order $order */ +$order = $objectManager->create(Order::class); +$order->setIncrementId('100000001') + ->setState(Order::STATE_PROCESSING) + ->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_PROCESSING)) + ->setSubtotal(100) + ->setGrandTotal(100) + ->setBaseSubtotal(100) + ->setBaseGrandTotal(100) + ->setCustomerIsGuest(true) + ->setCustomerEmail('customer@null.com') + ->setBillingAddress($billingAddress) + ->setShippingAddress($shippingAddress) + ->setStoreId($objectManager->get(StoreManagerInterface::class)->getStore()->getId()) + ->addItem($orderItem) + ->setPayment($payment); + +/** @var \Magento\Sales\Api\OrderManagementInterface $orderManagement */ +$orderManagement = $objectManager->create(\Magento\Sales\Api\OrderManagementInterface::class); + +$orderManagement->place($order); + +/** @var \Magento\Framework\DB\Transaction $transaction */ +$transaction = $objectManager->create(\Magento\Framework\DB\Transaction::class); +$transaction->addObject($order)->save(); diff --git a/InventoryReservationCli/etc/di.xml b/InventoryReservationCli/etc/di.xml index 4c86ec9b3166..ce4432ded0eb 100644 --- a/InventoryReservationCli/etc/di.xml +++ b/InventoryReservationCli/etc/di.xml @@ -12,6 +12,9 @@ <item name="inventory_complete_order_inconsistency" xsi:type="object"> Magento\InventoryReservationCli\Command\ShowInconsistenciesInCompletedOrders </item> + <item name="inventory_incomplete_order_inconsistency" xsi:type="object"> + Magento\InventoryReservationCli\Command\ShowInconsistenciesForIncompleteOrders + </item> </argument> </arguments> </type> From 1b9760f339f3705813d9560f9a0a187579a40cf1 Mon Sep 17 00:00:00 2001 From: Vladyslav Podorozhnyi <v.podorozhnyi@ism-ukraine.com> Date: Sat, 13 Apr 2019 16:05:29 +0300 Subject: [PATCH 126/231] Product status is 'In Stock' on storefront even it is Out of Stock in all Sources #2156. Added fix. --- .../IsAnySourceInStockCondition.php | 49 +++++++++++++ .../IsAnySourceInStockCondition.php | 73 +++++++++++++++++++ InventorySales/etc/di.xml | 8 ++ 3 files changed, 130 insertions(+) create mode 100644 InventorySales/Model/IsProductSalableCondition/IsAnySourceInStockCondition.php create mode 100644 InventorySales/Model/IsProductSalableForRequestedQtyCondition/IsAnySourceInStockCondition.php diff --git a/InventorySales/Model/IsProductSalableCondition/IsAnySourceInStockCondition.php b/InventorySales/Model/IsProductSalableCondition/IsAnySourceInStockCondition.php new file mode 100644 index 000000000000..4f2833046154 --- /dev/null +++ b/InventorySales/Model/IsProductSalableCondition/IsAnySourceInStockCondition.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventorySales\Model\IsProductSalableCondition; + +use Magento\InventorySalesApi\Api\IsProductSalableInterface; +use Magento\InventoryApi\Api\GetSourceItemsBySkuInterface; +use Magento\InventoryApi\Api\Data\SourceItemInterface; + +/** + * @inheritdoc + */ +class IsAnySourceInStockCondition implements IsProductSalableInterface +{ + /** + * @var GetSourceItemsBySkuInterface + */ + private $getSourceItemsBySku; + + /** + * IsAnySourceInStockCondition constructor. + * @param GetSourceItemsBySkuInterface $getSourceItemsBySku + */ + public function __construct(GetSourceItemsBySkuInterface $getSourceItemsBySku) + { + $this->getSourceItemsBySku = $getSourceItemsBySku; + } + + /** + * @inheritdoc + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function execute(string $sku, int $stockId): bool + { + $sourceItems = $this->getSourceItemsBySku->execute($sku); + foreach ($sourceItems as $sourceItem) { + if ($sourceItem->getStatus() === SourceItemInterface::STATUS_IN_STOCK) { + return true; + } + } + + return false; + } +} diff --git a/InventorySales/Model/IsProductSalableForRequestedQtyCondition/IsAnySourceInStockCondition.php b/InventorySales/Model/IsProductSalableForRequestedQtyCondition/IsAnySourceInStockCondition.php new file mode 100644 index 000000000000..63127d93ffff --- /dev/null +++ b/InventorySales/Model/IsProductSalableForRequestedQtyCondition/IsAnySourceInStockCondition.php @@ -0,0 +1,73 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventorySales\Model\IsProductSalableForRequestedQtyCondition; + +use Magento\InventorySalesApi\Api\IsProductSalableForRequestedQtyInterface; +use Magento\InventorySalesApi\Api\Data\ProductSalableResultInterface; +use Magento\InventorySalesApi\Api\Data\ProductSalableResultInterfaceFactory; +use Magento\InventorySalesApi\Api\Data\ProductSalabilityErrorInterfaceFactory; +use Magento\InventorySales\Model\IsProductSalableCondition\IsAnySourceInStockCondition + as IsAnySourceInStockConditionCondition; + +/** + * @inheritdoc + */ +class IsAnySourceInStockCondition implements IsProductSalableForRequestedQtyInterface +{ + /** + * @var IsAnySourceInStockConditionCondition + */ + private $isAnySourceInStockCondition; + + /** + * @var ProductSalabilityErrorInterfaceFactory + */ + private $productSalabilityErrorFactory; + + /** + * @var ProductSalableResultInterfaceFactory + */ + private $productSalableResultFactory; + + /** + * IsAnySourceInStockCondition constructor. + * @param IsAnySourceInStockConditionCondition $isAnySourceInStockCondition + * @param ProductSalabilityErrorInterfaceFactory $productSalabilityErrorFactory + * @param ProductSalableResultInterfaceFactory $productSalableResultFactory + */ + public function __construct( + IsAnySourceInStockConditionCondition $isAnySourceInStockCondition, + ProductSalabilityErrorInterfaceFactory $productSalabilityErrorFactory, + ProductSalableResultInterfaceFactory $productSalableResultFactory + ) { + $this->isAnySourceInStockCondition = $isAnySourceInStockCondition; + $this->productSalabilityErrorFactory = $productSalabilityErrorFactory; + $this->productSalableResultFactory = $productSalableResultFactory; + } + + /** + * @inheritdoc + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function execute(string $sku, int $stockId, float $requestedQty): ProductSalableResultInterface + { + $isValid = $this->isAnySourceInStockCondition->execute($sku, $stockId); + if (!$isValid) { + $errors = [ + $this->productSalabilityErrorFactory->create([ + 'code' => 'stock_item_is_any_source_in_stock-no_sources_in_stock', + 'message' => __('There is no sources in stock') + ]) + ]; + return $this->productSalableResultFactory->create(['errors' => $errors]); + } + + return $this->productSalableResultFactory->create(['errors' => []]); + } +} diff --git a/InventorySales/etc/di.xml b/InventorySales/etc/di.xml index caafcc4226d3..b1d2934cb018 100644 --- a/InventorySales/etc/di.xml +++ b/InventorySales/etc/di.xml @@ -59,6 +59,10 @@ <item name="required" xsi:type="boolean">true</item> <item name="object" xsi:type="object">Magento\InventorySales\Model\IsProductSalableCondition\IsSetInStockStatusForCompositeProductCondition</item> </item> + <item name="stock_item_is_any_source_in_stock" xsi:type="array"> + <item name="required" xsi:type="boolean">true</item> + <item name="object" xsi:type="object">Magento\InventorySales\Model\IsProductSalableCondition\IsAnySourceInStockCondition</item> + </item> <item name="back_order" xsi:type="array"> <item name="sort_order" xsi:type="number">10</item> <item name="object" xsi:type="object">Magento\InventorySales\Model\IsProductSalableCondition\BackOrderCondition</item> @@ -84,6 +88,10 @@ <item name="required" xsi:type="boolean">true</item> <item name="object" xsi:type="object">Magento\InventorySales\Model\IsProductSalableForRequestedQtyCondition\IsCorrectQtyCondition</item> </item> + <item name="stock_item_is_any_source_in_stock" xsi:type="array"> + <item name="required" xsi:type="boolean">true</item> + <item name="object" xsi:type="object">Magento\InventorySales\Model\IsProductSalableForRequestedQtyCondition\IsAnySourceInStockCondition</item> + </item> <item name="back_order" xsi:type="array"> <item name="sort_order" xsi:type="number">10</item> <item name="object" xsi:type="object">Magento\InventorySales\Model\IsProductSalableForRequestedQtyCondition\BackOrderCondition</item> From fc7d06bdd9202aa96d5fd66400c5a33f47663bba Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Sat, 13 Apr 2019 16:22:23 +0300 Subject: [PATCH 127/231] MSI-1912: added test coverage for get product sku from order item for a configurable product --- .../Sales/GetSkuFromOrderItemTest.php | 50 ++++++++++++ ...der_item_with_configurable_and_options.php | 80 +++++++++++++++++++ ...with_configurable_and_options_rollback.php | 9 +++ InventoryConfigurableProduct/composer.json | 3 - 4 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 InventoryConfigurableProduct/Test/Integration/Sales/GetSkuFromOrderItemTest.php create mode 100644 InventoryConfigurableProduct/Test/_files/order_item_with_configurable_and_options.php create mode 100644 InventoryConfigurableProduct/Test/_files/order_item_with_configurable_and_options_rollback.php diff --git a/InventoryConfigurableProduct/Test/Integration/Sales/GetSkuFromOrderItemTest.php b/InventoryConfigurableProduct/Test/Integration/Sales/GetSkuFromOrderItemTest.php new file mode 100644 index 000000000000..b7704810cc99 --- /dev/null +++ b/InventoryConfigurableProduct/Test/Integration/Sales/GetSkuFromOrderItemTest.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryConfigurableProduct\Test\Integration\Sales; + +use Magento\InventorySalesApi\Model\GetSkuFromOrderItemInterface; +use Magento\Sales\Api\OrderItemRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Class GetSkuFromOrderItemTest + */ +class GetSkuFromOrderItemTest extends TestCase +{ + /** + * @var OrderItemRepositoryInterface + */ + private $orderItemRepository; + + /** + * @var GetSkuFromOrderItemInterface + */ + private $getSkuFromOrderItemInterface; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->orderItemRepository = Bootstrap::getObjectManager()->get(OrderItemRepositoryInterface::class); + $this->getSkuFromOrderItemInterface = Bootstrap::getObjectManager()->get(GetSkuFromOrderItemInterface::class); + } + + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryConfigurableProduct/Test/_files/order_item_with_configurable_and_options.php + */ + public function testGetSkuFromConfigurableProductWithCustomOptionsOrderItem() + { + $orderItem = $this->orderItemRepository->get(1); + $sku = $this->getSkuFromOrderItemInterface->execute($orderItem); + $this->assertEquals('configurable', $sku); + } +} diff --git a/InventoryConfigurableProduct/Test/_files/order_item_with_configurable_and_options.php b/InventoryConfigurableProduct/Test/_files/order_item_with_configurable_and_options.php new file mode 100644 index 000000000000..04baffbe736d --- /dev/null +++ b/InventoryConfigurableProduct/Test/_files/order_item_with_configurable_and_options.php @@ -0,0 +1,80 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/../../../../../../dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable.php'; + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +$addressData = include __DIR__ . '/../../../../../../dev/tests/integration/testsuite/Magento/Sales/_files/address_data.php'; + +$billingAddress = $objectManager->create(\Magento\Sales\Model\Order\Address::class, ['data' => $addressData]); +$billingAddress->setAddressType('billing'); + +$shippingAddress = clone $billingAddress; +$shippingAddress->setId(null)->setAddressType('shipping'); + +$payment = $objectManager->create(\Magento\Sales\Model\Order\Payment::class); +$payment->setMethod('checkmo'); + +/** @var $product \Magento\Catalog\Model\Product */ +$product = $objectManager->create(\Magento\Catalog\Model\Product::class); +$product->load(1); + +/** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ +$eavConfig = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Eav\Model\Config::class); +$attribute = $eavConfig->getAttribute('catalog_product', 'test_configurable'); + +/** @var $options \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection */ +$options = $objectManager->create(\Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection::class); +$option = $options->setAttributeFilter($attribute->getId()) + ->getFirstItem(); + +$requestInfo = [ + 'qty' => 1, + 'super_attribute' => [ + $attribute->getId() => $option->getId(), + ], +]; +/** @var \Magento\Sales\Model\Order $order */ +$order = $objectManager->create(\Magento\Sales\Model\Order::class); +$order->setIncrementId('100000001'); +$order->loadByIncrementId('100000001'); +if ($order->getId()) { + $order->delete(); +} +/** @var \Magento\Sales\Model\Order\Item $orderItem */ +$orderItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class); +$orderItem->setProductId($product->getId()); +$orderItem->setQtyOrdered(1); +$orderItem->setBasePrice($product->getPrice()); +$orderItem->setPrice($product->getPrice()); +$orderItem->setRowTotal($product->getPrice()); +$orderItem->setProductType($product->getTypeId()); +$orderItem->setProductOptions([ + 'info_buyRequest' => $requestInfo, + 'simple_sku' => $product->getSku() +]); + +/** @var \Magento\Sales\Model\Order $order */ +$order = $objectManager->create(\Magento\Sales\Model\Order::class); +$order->setIncrementId('100000001'); +$order->setState(\Magento\Sales\Model\Order::STATE_NEW); +$order->setStatus($order->getConfig()->getStateDefaultStatus(\Magento\Sales\Model\Order::STATE_NEW)); +$order->setCustomerIsGuest(true); +$order->setCustomerEmail('customer@null.com'); +$order->setCustomerFirstname('firstname'); +$order->setCustomerLastname('lastname'); +$order->setBillingAddress($billingAddress); +$order->setShippingAddress($shippingAddress); +$order->setAddresses([$billingAddress, $shippingAddress]); +$order->setPayment($payment); +$order->addItem($orderItem); +$order->setStoreId($objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->getStore()->getId()); +$order->setSubtotal(100); +$order->setBaseSubtotal(100); +$order->setBaseGrandTotal(100); +$order->save(); diff --git a/InventoryConfigurableProduct/Test/_files/order_item_with_configurable_and_options_rollback.php b/InventoryConfigurableProduct/Test/_files/order_item_with_configurable_and_options_rollback.php new file mode 100644 index 000000000000..9899e4bdcc22 --- /dev/null +++ b/InventoryConfigurableProduct/Test/_files/order_item_with_configurable_and_options_rollback.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/../../../../../../dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_rollback.php'; +require __DIR__ . '/../../../../../../dev/tests/integration/testsuite/Magento/Sales/_files/default_rollback.php'; diff --git a/InventoryConfigurableProduct/composer.json b/InventoryConfigurableProduct/composer.json index e96804e65815..d193793ff8ef 100644 --- a/InventoryConfigurableProduct/composer.json +++ b/InventoryConfigurableProduct/composer.json @@ -13,9 +13,6 @@ "magento/module-sales": "*", "magento/module-configurable-product": "*" }, - "suggest": { - "magento/module-configurable-product": "*" - }, "type": "magento2-module", "license": [ "OSL-3.0", From 58ed84362eefa8819cf489e22343b1e93a5adad1 Mon Sep 17 00:00:00 2001 From: Vadim Justus <v.justus@techdivision.com> Date: Sat, 13 Apr 2019 15:34:57 +0200 Subject: [PATCH 128/231] magento-engcom/msi#2169: Change validation logic in order to check each order item --- ...etOrdersWithMissingInitialReservations.php | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/InventoryReservationCli/Model/GetOrdersWithMissingInitialReservations.php b/InventoryReservationCli/Model/GetOrdersWithMissingInitialReservations.php index 0bca705059d1..96d81dd4b358 100644 --- a/InventoryReservationCli/Model/GetOrdersWithMissingInitialReservations.php +++ b/InventoryReservationCli/Model/GetOrdersWithMissingInitialReservations.php @@ -54,11 +54,36 @@ public function execute(array $orders): array $objectId = $metadata['object_id']; $eventType = $metadata['event_type']; - if ($eventType == 'order_placed' && in_array($objectId, array_keys($entityIdAndSkuList))) { - unset($entityIdAndSkuList[$objectId]); + if ($eventType === 'order_placed' && in_array($objectId, array_keys($entityIdAndSkuList))) { + $entityIdAndSkuList = $this->filterReservedSkus((int)$objectId, $entityIdAndSkuList, $reservation); } } + $entityIdAndSkuList = array_filter($entityIdAndSkuList, function ($order) { + return !empty($order['skus']); + }); + + return $entityIdAndSkuList; + } + + /** + * @param int $objectId + * @param array $entityIdAndSkuList + * @param array $reservation + * @return array + */ + private function filterReservedSkus(int $objectId, array $entityIdAndSkuList, array $reservation): array + { + $reservedSku = $reservation['sku']; + if (!in_array($reservedSku, array_keys($entityIdAndSkuList[$objectId]['skus']))) { + return $entityIdAndSkuList; + } + + $reservedQuantity = $reservation['quantity']; + $entityIdAndSkuList[$objectId]['skus'][$reservedSku] += (float)$reservedQuantity; + + $entityIdAndSkuList[$objectId]['skus'] = array_filter($entityIdAndSkuList[$objectId]['skus']); + return $entityIdAndSkuList; } From bde8a6dbf4d837b6db8bead04c2befbedb9cb5c4 Mon Sep 17 00:00:00 2001 From: "al.kravchuk" <al.kravchuk@ism-ukraine.com> Date: Sat, 13 Apr 2019 16:35:21 +0300 Subject: [PATCH 129/231] MSI-2126 Immutable DTO for Pickup Location. Add Pickup Location and Mapper for Source -> Pickup Location projection. --- .../Model/PickupLocation.php | 284 ++++++++++++++++++ .../Model/PickupLocation/Mapper.php | 49 +++ .../Mapper/CreateFromSource.php | 123 ++++++++ InventoryInStorePickup/etc/di.xml | 2 +- InventoryInStorePickup/etc/frontend/di.xml | 29 ++ .../Api/Data/PickupLocationInterface.php | 132 +++++++- 6 files changed, 611 insertions(+), 8 deletions(-) create mode 100644 InventoryInStorePickup/Model/PickupLocation.php create mode 100644 InventoryInStorePickup/Model/PickupLocation/Mapper.php create mode 100644 InventoryInStorePickup/Model/PickupLocation/Mapper/CreateFromSource.php create mode 100644 InventoryInStorePickup/etc/frontend/di.xml diff --git a/InventoryInStorePickup/Model/PickupLocation.php b/InventoryInStorePickup/Model/PickupLocation.php new file mode 100644 index 000000000000..23685784a5af --- /dev/null +++ b/InventoryInStorePickup/Model/PickupLocation.php @@ -0,0 +1,284 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickup\Model; + +use Magento\InventoryInStorePickupApi\Api\Data\PickupLocationExtensionInterface; +use Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface; + +class PickupLocation implements PickupLocationInterface +{ + /** + * @var PickupLocationExtensionInterface + */ + private $extensionAttributes; + + /** + * @var string|null + */ + private $sourceCode; + + /** + * @var string|null + */ + private $name; + + /** + * @var string|null + */ + private $fax; + + /** + * @var string|null + */ + private $contactName; + + /** + * @var string|null + */ + private $description; + + /** + * @var float|null + */ + private $latitude; + + /** + * @var float|null + */ + private $longitude; + + /** + * @var string|null + */ + private $countryId; + + /** + * @var int|null + */ + private $regionId; + + /** + * @var int|null + */ + private $region; + + /** + * @var string|null + */ + private $city; + + /** + * @var string|null + */ + private $street; + + /** + * @var string|null + */ + private $postcode; + + /** + * @var string|null + */ + private $phone; + + /** + * @var string[]|null + */ + private $openHours; + + /** + * @param string|null $sourceCode + * @param string|null $name + * @param string|null $fax + * @param string|null $contactName + * @param string|null $description + * @param float|null $latitude + * @param float|null $longitude + * @param string|null $countryId + * @param int|null $regionId + * @param int|null $region + * @param string|null $city + * @param string|null $street + * @param string|null $postcode + * @param string|null $phone + * @param string[]|null $openHours + * @param PickupLocationExtensionInterface|null $extensionAttributes + */ + public function __construct( + ?string $sourceCode = null, + ?string $name = null, + ?string $fax = null, + ?string $contactName = null, + ?string $description = null, + ?float $latitude = null, + ?float $longitude = null, + ?string $countryId = null, + ?int $regionId = null, + ?int $region = null, + ?string $city = null, + ?string $street = null, + ?string $postcode = null, + ?string $phone = null, + ?array $openHours = null, + ?PickupLocationExtensionInterface $extensionAttributes = null + ) { + $this->sourceCode = $sourceCode; + $this->name = $name; + $this->fax = $fax; + $this->contactName = $contactName; + $this->description = $description; + $this->latitude = $latitude; + $this->longitude = $longitude; + $this->countryId = $countryId; + $this->regionId = $regionId; + $this->region = $region; + $this->city = $city; + $this->street = $street; + $this->postcode = $postcode; + $this->phone = $phone; + $this->openHours = $openHours; + $this->extensionAttributes = $extensionAttributes; + } + + /** + * @inheritdoc + */ + public function getSourceCode(): ?string + { + return $this->sourceCode; + } + + /** + * @inheritdoc + */ + public function getName(): ?string + { + return $this->name; + } + + /** + * @inheritdoc + */ + public function getFax(): ?string + { + return $this->name; + } + + /** + * @inheritdoc + */ + public function getContactName(): ?string + { + return $this->contactName; + } + + /** + * @inheritdoc + */ + public function getDescription(): ?string + { + return $this->description; + } + + /** + * @inheritdoc + */ + public function getLatitude(): ?float + { + return $this->latitude; + } + + /** + * @inheritdoc + */ + public function getLongitude(): ?float + { + return $this->longitude; + } + + /** + * @inheritdoc + */ + public function getCountryId(): ?string + { + return $this->countryId; + } + + /** + * @inheritdoc + */ + public function getRegionId(): ?int + { + return $this->regionId; + } + + /** + * @inheritdoc + */ + public function getRegion(): ?string + { + return $this->region; + } + + /** + * @inheritdoc + */ + public function getCity(): ?string + { + return $this->city; + } + + /** + * @inheritdoc + */ + public function getStreet(): ?string + { + return $this->street; + } + + /** + * @inheritdoc + */ + public function getPostcode(): ?string + { + return $this->postcode; + } + + /** + * @inheritdoc + */ + public function getPhone(): ?string + { + return $this->phone; + } + + /** + * @inheritdoc + */ + public function getOpenHours(): ?array + { + return $this->openHours; + } + + /** + * @inheritdoc + */ + public function setExtensionAttributes(?PickupLocationExtensionInterface $extensionAttributes) + { + $this->extensionAttributes = $extensionAttributes; + } + + /** + * @inheritdoc + */ + public function getExtensionAttributes(): ?PickupLocationExtensionInterface + { + return $this->extensionAttributes; + } +} diff --git a/InventoryInStorePickup/Model/PickupLocation/Mapper.php b/InventoryInStorePickup/Model/PickupLocation/Mapper.php new file mode 100644 index 000000000000..cc9b68497b45 --- /dev/null +++ b/InventoryInStorePickup/Model/PickupLocation/Mapper.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickup\Model\PickupLocation; + +use Magento\InventoryApi\Api\Data\SourceInterface; +use Magento\InventoryInStorePickup\Model\PickupLocation\Mapper\CreateFromSource; +use Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface; + +class Mapper +{ + /** + * Attributes map for projection. + * + * @var array + */ + private $map; + + /** + * @var \Magento\InventoryInStorePickup\Model\PickupLocation\Mapper\CreateFromSource + */ + private $createFromSource; + + /** + * @param \Magento\InventoryInStorePickup\Model\PickupLocation\Mapper\CreateFromSource $createFromSource + * @param array $map + */ + public function __construct( + CreateFromSource $createFromSource, + array $map = [] + ) { + $this->map = $map; + $this->createFromSource = $createFromSource; + } + + /** + * @param \Magento\InventoryApi\Api\Data\SourceInterface $source + * + * @return \Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface + */ + public function map(SourceInterface $source): PickupLocationInterface + { + return $this->createFromSource->execute($source, $this->map); + } +} diff --git a/InventoryInStorePickup/Model/PickupLocation/Mapper/CreateFromSource.php b/InventoryInStorePickup/Model/PickupLocation/Mapper/CreateFromSource.php new file mode 100644 index 000000000000..5f7cf137c9ad --- /dev/null +++ b/InventoryInStorePickup/Model/PickupLocation/Mapper/CreateFromSource.php @@ -0,0 +1,123 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickup\Model\PickupLocation\Mapper; + +use Magento\Framework\Api\ExtensionAttributesFactory; +use Magento\Framework\Api\SimpleDataObjectConverter; +use Magento\InventoryApi\Api\Data\SourceInterface; +use Magento\InventoryInStorePickup\Model\PickupLocationFactory; +use Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface; + +class CreateFromSource +{ + /** + * @var \Magento\InventoryInStorePickup\Model\PickupLocationFactory + */ + private $pickupLocationFactory; + + /** + * @var \Magento\Framework\Api\ExtensionAttributesFactory + */ + private $extensionAttributesFactory; + + /** + * CreateFromSource constructor. + * + * @param \Magento\InventoryInStorePickup\Model\PickupLocationFactory $pickupLocationFactory + * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionAttributesFactory + */ + public function __construct( + PickupLocationFactory $pickupLocationFactory, + ExtensionAttributesFactory $extensionAttributesFactory + ) { + $this->pickupLocationFactory = $pickupLocationFactory; + $this->extensionAttributesFactory = $extensionAttributesFactory; + } + + /** + * @param \Magento\InventoryApi\Api\Data\SourceInterface $source + * @param array $map + * + * @return \Magento\InventoryInStorePickup\Model\PickupLocation + */ + public function execute(SourceInterface $source, array $map) + { + $data = []; + $pickupLocationExtension = $this->extensionAttributesFactory->create(PickupLocationInterface::class); + + foreach ($map as $sourceField => $pickupLocationField) { + if ($this->isExtensionAttributeField($sourceField)) { + $fieldValue = $source->getExtensionAttributes()->{$this->getGetterMethodName( + $this->getExtensionAttributeFieldName($sourceField) + )}(); + } else { + $fieldValue = $source->{$this->getGetterMethodName($sourceField)}(); + } + + if ($this->isExtensionAttributeField($pickupLocationField)) { + $pickupLocationExtension->{$this->getSetterMethodName( + $this->getExtensionAttributeFieldName($pickupLocationField) + )}($fieldValue); + } else { + $data[SimpleDataObjectConverter::snakeCaseToCamelCase($pickupLocationField)] = $fieldValue; + } + } + + $data['extensionAttributes'] = $pickupLocationExtension; + + return $this->pickupLocationFactory->create($data); + } + + /** + * @param $fieldName + * + * @return string + */ + private function getExtensionAttributeFieldName($fieldName): string + { + $field = explode('.', $fieldName); + + return end($field); + } + + /** + * Check if field should be get from extension attributes. + * + * @param $fieldName + * + * @return bool + */ + private function isExtensionAttributeField($fieldName): bool + { + return strpos($fieldName, 'extension_attributes.') === 0; + } + + /** + * Get getter name based on field name. + * + * @param string $fieldName + * + * @return string + */ + private function getGetterMethodName(string $fieldName): string + { + return 'get' . SimpleDataObjectConverter::snakeCaseToUpperCamelCase($fieldName); + } + + /** + * Get setter name for Extension Attribute based on field name. + * + * @param string $fieldName + * + * @return string + */ + private function getSetterMethodName(string $fieldName): string + { + return 'set' . SimpleDataObjectConverter::snakeCaseToUpperCamelCase($fieldName); + } +} diff --git a/InventoryInStorePickup/etc/di.xml b/InventoryInStorePickup/etc/di.xml index 04f52ef77cdf..20c10d621b87 100644 --- a/InventoryInStorePickup/etc/di.xml +++ b/InventoryInStorePickup/etc/di.xml @@ -17,7 +17,7 @@ </type> <preference for="Magento\InventoryInStorePickupApi\Api\GetNearbySourcesByPostcodeInterface" type="Magento\InventoryInStorePickup\Model\GetNearbySourcesByPostcode"/> - + <preference for="Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface" type="Magento\InventoryInStorePickup\Model\PickupLocation" /> <type name="Magento\InventoryInStorePickupApi\Model\GetNearbySourcesByPostcode"> <arguments> <argument name="providers" xsi:type="array"> diff --git a/InventoryInStorePickup/etc/frontend/di.xml b/InventoryInStorePickup/etc/frontend/di.xml new file mode 100644 index 000000000000..e0e66c4a259a --- /dev/null +++ b/InventoryInStorePickup/etc/frontend/di.xml @@ -0,0 +1,29 @@ +<?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\InventoryInStorePickup\Model\PickupLocation\Mapper"> + <arguments> + <argument name="map" xsi:type="array"> + <item name="source_code" xsi:type="string">source_code</item> + <item name="email" xsi:type="string">email</item> + <item name="fax" xsi:type="string">fax</item> + <item name="contact_name" xsi:type="string">contact_name</item> + <item name="description" xsi:type="string">description</item> + <item name="latitude" xsi:type="string">latitude</item> + <item name="longitude" xsi:type="string">longitude</item> + <item name="country_id" xsi:type="string">country_id</item> + <item name="region_id" xsi:type="string">region_id</item> + <item name="region" xsi:type="string">region</item> + <item name="city" xsi:type="string">city</item> + <item name="street" xsi:type="string">street</item> + <item name="postcode" xsi:type="string">postcode</item> + <item name="phone" xsi:type="string">phone</item> + </argument> + </arguments> + </type> +</config> diff --git a/InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php b/InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php index d1a3b73bdf27..edb2e57a2b62 100644 --- a/InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php +++ b/InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php @@ -3,15 +3,133 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -declare(strict_types=1); - namespace Magento\InventoryInStorePickupApi\Api\Data; -/** - * @TODO Replace with autogenerated immutable realization. - * @see Please check issue for more details: https://github.com/magento-engcom/msi/issues/2126 - */ -interface PickupLocationInterface +use Magento\Framework\Api\ExtensibleDataInterface; + +interface PickupLocationInterface extends ExtensibleDataInterface { const IS_PICKUP_LOCATION_ACTIVE = 'is_pickup_location_active'; + + /** + * Get source code of Pickup Location + * + * @return string|null + */ + public function getSourceCode(): ?string; + + /** + * Get Pickup Location name + * + * @return string|null + */ + public function getName(): ?string; + + /** + * Get Fax contact info. + * + * @return string|null + */ + public function getFax(): ?string; + + /** + * Get Pickup Location contact name. + * + * @return string|null + */ + public function getContactName(): ?string; + + /** + * Get Pickup Location description. + * + * @return string|null + */ + public function getDescription(): ?string; + + /** + * Get Pickup Location latitude. + * + * @return float|null + */ + public function getLatitude(): ?float; + + /** + * Get Pickup Location longtitude. + * + * @return float|null + */ + public function getLongitude(): ?float; + + /** + * Get Pickup Location country ID. + * + * @return string|null + */ + public function getCountryId(): ?string; + + /** + * Get Pickup Location region ID. + * + * @return int|null + */ + public function getRegionId(): ?int; + + /** + * Get Pickup Location region. + * + * @return string|null + */ + public function getRegion(): ?string; + + /** + * Get Pickup Location city. + * + * @return string|null + */ + public function getCity(): ?string; + + /** + * Get Pickup Location street. + * + * @return string|null + */ + public function getStreet(): ?string; + + /** + * Get Pickup Location postcode. + * + * @return string|null + */ + public function getPostcode(): ?string; + + /** + * Get Pickup Location phone. + * + * @return string|null + */ + public function getPhone(): ?string; + + /** + * Get Pickup Location open hours. + * + * @return string[]|null + */ + public function getOpenHours(): ?array; + + /** + * Set Extension Attributes for Pickup Location. + * + * @param \Magento\InventoryInStorePickupApi\Api\Data\PickupLocationExtensionInterface|null $extensionAttributes + * + * @return void + */ + public function setExtensionAttributes(?PickupLocationExtensionInterface $extensionAttributes); + + /** + * Get Extension Attributes of Pickup Location. + * + * @return \Magento\InventoryInStorePickupApi\Api\Data\PickupLocationExtensionInterface|null + */ + public function getExtensionAttributes(): ?PickupLocationExtensionInterface; + } From 2d1cafde87a841ddd9825b8c65a840e294e4c6a0 Mon Sep 17 00:00:00 2001 From: Maksym Novik <m.novik@ism-ukraine.com> Date: Sat, 13 Apr 2019 16:57:56 +0300 Subject: [PATCH 130/231] Product-status-is-In-Stock-on-storefront-even-it-is-Out-of-Stock-in-all-Sources-2156. Test condition instead of chain --- ...est.php => IsAnySourceInStockConditionTest.php} | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) rename InventorySales/Test/Integration/IsProductSalable/{IsProductSalableConditionChainTest.php => IsAnySourceInStockConditionTest.php} (84%) diff --git a/InventorySales/Test/Integration/IsProductSalable/IsProductSalableConditionChainTest.php b/InventorySales/Test/Integration/IsProductSalable/IsAnySourceInStockConditionTest.php similarity index 84% rename from InventorySales/Test/Integration/IsProductSalable/IsProductSalableConditionChainTest.php rename to InventorySales/Test/Integration/IsProductSalable/IsAnySourceInStockConditionTest.php index 919a4218454e..20b5330678d1 100644 --- a/InventorySales/Test/Integration/IsProductSalable/IsProductSalableConditionChainTest.php +++ b/InventorySales/Test/Integration/IsProductSalable/IsAnySourceInStockConditionTest.php @@ -11,16 +11,16 @@ use Magento\CatalogInventory\Api\Data\StockItemInterface; use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory; use Magento\CatalogInventory\Api\StockItemRepositoryInterface; -use Magento\InventorySales\Model\IsProductSalableCondition\IsProductSalableConditionChain; +use Magento\InventorySales\Model\IsProductSalableCondition\IsAnySourceInStockCondition; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; -class IsProductSalableConditionChainTest extends TestCase +class IsAnySourceInStockConditionTest extends TestCase { /** - * @var IsProductSalableConditionChain + * @var IsAnySourceInStockCondition */ - private $isProductSalableConditionChain; + private $isAnySourceInStockCondition; /** * @var ProductRepositoryInterface @@ -40,8 +40,8 @@ class IsProductSalableConditionChainTest extends TestCase protected function setUp() { $objectManager = Bootstrap::getObjectManager(); - $this->isProductSalableConditionChain = $objectManager->get( - IsProductSalableConditionChain::class + $this->isAnySourceInStockCondition = $objectManager->get( + IsAnySourceInStockCondition::class ); $this->productRepository = $objectManager->get(ProductRepositoryInterface::class); $this->stockItemCriteriaFactory = $objectManager->get(StockItemCriteriaInterfaceFactory::class); @@ -69,6 +69,6 @@ public function testSourcesItemsAreOutOfStock() $legacyStockItem->setBackorders(1); $legacyStockItem->setUseConfigBackorders(0); $this->stockItemRepository->save($legacyStockItem); - $this->assertFalse($this->isProductSalableConditionChain->execute('SKU-1', 10)); + $this->assertFalse($this->isAnySourceInStockCondition->execute('SKU-1', 10)); } } From eec7c5e80665ae446043934f6681e2c707a34f28 Mon Sep 17 00:00:00 2001 From: "al.kravchuk" <al.kravchuk@ism-ukraine.com> Date: Sat, 13 Apr 2019 17:19:01 +0300 Subject: [PATCH 131/231] MSI-2126 Immutable DTO for Pickup Location. Make Source Name attribute required. --- InventoryInStorePickup/Model/PickupLocation.php | 8 ++++---- .../Api/Data/PickupLocationInterface.php | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/InventoryInStorePickup/Model/PickupLocation.php b/InventoryInStorePickup/Model/PickupLocation.php index 23685784a5af..c7394b84ce59 100644 --- a/InventoryInStorePickup/Model/PickupLocation.php +++ b/InventoryInStorePickup/Model/PickupLocation.php @@ -18,7 +18,7 @@ class PickupLocation implements PickupLocationInterface private $extensionAttributes; /** - * @var string|null + * @var string */ private $sourceCode; @@ -93,7 +93,7 @@ class PickupLocation implements PickupLocationInterface private $openHours; /** - * @param string|null $sourceCode + * @param string $sourceCode * @param string|null $name * @param string|null $fax * @param string|null $contactName @@ -111,7 +111,7 @@ class PickupLocation implements PickupLocationInterface * @param PickupLocationExtensionInterface|null $extensionAttributes */ public function __construct( - ?string $sourceCode = null, + string $sourceCode, ?string $name = null, ?string $fax = null, ?string $contactName = null, @@ -149,7 +149,7 @@ public function __construct( /** * @inheritdoc */ - public function getSourceCode(): ?string + public function getSourceCode(): string { return $this->sourceCode; } diff --git a/InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php b/InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php index edb2e57a2b62..ebe98645f41a 100644 --- a/InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php +++ b/InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php @@ -14,9 +14,9 @@ interface PickupLocationInterface extends ExtensibleDataInterface /** * Get source code of Pickup Location * - * @return string|null + * @return string */ - public function getSourceCode(): ?string; + public function getSourceCode(): string; /** * Get Pickup Location name From d39666e0baaacc51e70103f6adea8f9bef82ce8c Mon Sep 17 00:00:00 2001 From: Vladyslav Podorozhnyi <v.podorozhnyi@ism-ukraine.com> Date: Sat, 13 Apr 2019 17:22:00 +0300 Subject: [PATCH 132/231] Product status is 'In Stock' on storefront even it is Out of Stock in all Sources #2156. --- .../IsAnySourceInStockCondition.php | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/InventorySales/Model/IsProductSalableCondition/IsAnySourceInStockCondition.php b/InventorySales/Model/IsProductSalableCondition/IsAnySourceInStockCondition.php index 4f2833046154..0bc257e27197 100644 --- a/InventorySales/Model/IsProductSalableCondition/IsAnySourceInStockCondition.php +++ b/InventorySales/Model/IsProductSalableCondition/IsAnySourceInStockCondition.php @@ -7,8 +7,9 @@ namespace Magento\InventorySales\Model\IsProductSalableCondition; +use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\InventorySalesApi\Api\IsProductSalableInterface; -use Magento\InventoryApi\Api\GetSourceItemsBySkuInterface; +use Magento\InventoryApi\Api\SourceItemRepositoryInterface; use Magento\InventoryApi\Api\Data\SourceItemInterface; /** @@ -17,17 +18,26 @@ class IsAnySourceInStockCondition implements IsProductSalableInterface { /** - * @var GetSourceItemsBySkuInterface + * @var SourceItemRepositoryInterface */ - private $getSourceItemsBySku; + private $sourceItemRepository; + + /** + * @var SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; /** * IsAnySourceInStockCondition constructor. - * @param GetSourceItemsBySkuInterface $getSourceItemsBySku + * @param SourceItemRepositoryInterface $sourceItemRepository + * @param SearchCriteriaBuilder $searchCriteriaBuilder */ - public function __construct(GetSourceItemsBySkuInterface $getSourceItemsBySku) - { - $this->getSourceItemsBySku = $getSourceItemsBySku; + public function __construct( + SourceItemRepositoryInterface $sourceItemRepository, + SearchCriteriaBuilder $searchCriteriaBuilder + ) { + $this->sourceItemRepository = $sourceItemRepository; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; } /** @@ -37,13 +47,13 @@ public function __construct(GetSourceItemsBySkuInterface $getSourceItemsBySku) */ public function execute(string $sku, int $stockId): bool { - $sourceItems = $this->getSourceItemsBySku->execute($sku); - foreach ($sourceItems as $sourceItem) { - if ($sourceItem->getStatus() === SourceItemInterface::STATUS_IN_STOCK) { - return true; - } - } - - return false; + $searchCriteria = $this->searchCriteriaBuilder + ->addFilter(SourceItemInterface::SKU, $sku) + ->addFilter(SourceItemInterface::STATUS, SourceItemInterface::STATUS_IN_STOCK) + ->create(); + + $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); + + return (bool)count($sourceItems); } } From a45a93754c496f5992d5dfc849f16599cc6ef9d5 Mon Sep 17 00:00:00 2001 From: Vladyslav Podorozhnyi <v.podorozhnyi@ism-ukraine.com> Date: Sat, 13 Apr 2019 17:38:15 +0300 Subject: [PATCH 133/231] Product status is 'In Stock' on storefront even it is Out of Stock in all Sources #2156. --- ...tion.php => IsAnySourceItemInStockCondition.php} | 3 +-- ...tion.php => IsAnySourceItemInStockCondition.php} | 13 ++++++------- .../IsAnySourceInStockConditionTest.php | 6 +++--- InventorySales/etc/di.xml | 4 ++-- 4 files changed, 12 insertions(+), 14 deletions(-) rename InventorySales/Model/IsProductSalableCondition/{IsAnySourceInStockCondition.php => IsAnySourceItemInStockCondition.php} (93%) rename InventorySales/Model/IsProductSalableForRequestedQtyCondition/{IsAnySourceInStockCondition.php => IsAnySourceItemInStockCondition.php} (87%) diff --git a/InventorySales/Model/IsProductSalableCondition/IsAnySourceInStockCondition.php b/InventorySales/Model/IsProductSalableCondition/IsAnySourceItemInStockCondition.php similarity index 93% rename from InventorySales/Model/IsProductSalableCondition/IsAnySourceInStockCondition.php rename to InventorySales/Model/IsProductSalableCondition/IsAnySourceItemInStockCondition.php index 0bc257e27197..38690d717adc 100644 --- a/InventorySales/Model/IsProductSalableCondition/IsAnySourceInStockCondition.php +++ b/InventorySales/Model/IsProductSalableCondition/IsAnySourceItemInStockCondition.php @@ -15,7 +15,7 @@ /** * @inheritdoc */ -class IsAnySourceInStockCondition implements IsProductSalableInterface +class IsAnySourceItemInStockCondition implements IsProductSalableInterface { /** * @var SourceItemRepositoryInterface @@ -28,7 +28,6 @@ class IsAnySourceInStockCondition implements IsProductSalableInterface private $searchCriteriaBuilder; /** - * IsAnySourceInStockCondition constructor. * @param SourceItemRepositoryInterface $sourceItemRepository * @param SearchCriteriaBuilder $searchCriteriaBuilder */ diff --git a/InventorySales/Model/IsProductSalableForRequestedQtyCondition/IsAnySourceInStockCondition.php b/InventorySales/Model/IsProductSalableForRequestedQtyCondition/IsAnySourceItemInStockCondition.php similarity index 87% rename from InventorySales/Model/IsProductSalableForRequestedQtyCondition/IsAnySourceInStockCondition.php rename to InventorySales/Model/IsProductSalableForRequestedQtyCondition/IsAnySourceItemInStockCondition.php index 63127d93ffff..7e27ffd37277 100644 --- a/InventorySales/Model/IsProductSalableForRequestedQtyCondition/IsAnySourceInStockCondition.php +++ b/InventorySales/Model/IsProductSalableForRequestedQtyCondition/IsAnySourceItemInStockCondition.php @@ -11,13 +11,13 @@ use Magento\InventorySalesApi\Api\Data\ProductSalableResultInterface; use Magento\InventorySalesApi\Api\Data\ProductSalableResultInterfaceFactory; use Magento\InventorySalesApi\Api\Data\ProductSalabilityErrorInterfaceFactory; -use Magento\InventorySales\Model\IsProductSalableCondition\IsAnySourceInStockCondition +use Magento\InventorySales\Model\IsProductSalableCondition\IsAnySourceItemInStockCondition as IsAnySourceInStockConditionCondition; /** * @inheritdoc */ -class IsAnySourceInStockCondition implements IsProductSalableForRequestedQtyInterface +class IsAnySourceItemInStockCondition implements IsProductSalableForRequestedQtyInterface { /** * @var IsAnySourceInStockConditionCondition @@ -35,7 +35,6 @@ class IsAnySourceInStockCondition implements IsProductSalableForRequestedQtyInte private $productSalableResultFactory; /** - * IsAnySourceInStockCondition constructor. * @param IsAnySourceInStockConditionCondition $isAnySourceInStockCondition * @param ProductSalabilityErrorInterfaceFactory $productSalabilityErrorFactory * @param ProductSalableResultInterfaceFactory $productSalableResultFactory @@ -57,17 +56,17 @@ public function __construct( */ public function execute(string $sku, int $stockId, float $requestedQty): ProductSalableResultInterface { + $errors = []; $isValid = $this->isAnySourceInStockCondition->execute($sku, $stockId); if (!$isValid) { $errors = [ $this->productSalabilityErrorFactory->create([ - 'code' => 'stock_item_is_any_source_in_stock-no_sources_in_stock', - 'message' => __('There is no sources in stock') + 'code' => 'stock_item_is_any_source_in_stock-no_source_items_in_stock', + 'message' => __('There are no source items with in stock status') ]) ]; - return $this->productSalableResultFactory->create(['errors' => $errors]); } - return $this->productSalableResultFactory->create(['errors' => []]); + return $this->productSalableResultFactory->create(['errors' => $errors]); } } diff --git a/InventorySales/Test/Integration/IsProductSalable/IsAnySourceInStockConditionTest.php b/InventorySales/Test/Integration/IsProductSalable/IsAnySourceInStockConditionTest.php index 20b5330678d1..1b1d515d80b6 100644 --- a/InventorySales/Test/Integration/IsProductSalable/IsAnySourceInStockConditionTest.php +++ b/InventorySales/Test/Integration/IsProductSalable/IsAnySourceInStockConditionTest.php @@ -11,14 +11,14 @@ use Magento\CatalogInventory\Api\Data\StockItemInterface; use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory; use Magento\CatalogInventory\Api\StockItemRepositoryInterface; -use Magento\InventorySales\Model\IsProductSalableCondition\IsAnySourceInStockCondition; +use Magento\InventorySales\Model\IsProductSalableCondition\IsAnySourceItemInStockCondition; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; class IsAnySourceInStockConditionTest extends TestCase { /** - * @var IsAnySourceInStockCondition + * @var IsAnySourceItemInStockCondition */ private $isAnySourceInStockCondition; @@ -41,7 +41,7 @@ protected function setUp() { $objectManager = Bootstrap::getObjectManager(); $this->isAnySourceInStockCondition = $objectManager->get( - IsAnySourceInStockCondition::class + IsAnySourceItemInStockCondition::class ); $this->productRepository = $objectManager->get(ProductRepositoryInterface::class); $this->stockItemCriteriaFactory = $objectManager->get(StockItemCriteriaInterfaceFactory::class); diff --git a/InventorySales/etc/di.xml b/InventorySales/etc/di.xml index b1d2934cb018..e51606e18f1c 100644 --- a/InventorySales/etc/di.xml +++ b/InventorySales/etc/di.xml @@ -61,7 +61,7 @@ </item> <item name="stock_item_is_any_source_in_stock" xsi:type="array"> <item name="required" xsi:type="boolean">true</item> - <item name="object" xsi:type="object">Magento\InventorySales\Model\IsProductSalableCondition\IsAnySourceInStockCondition</item> + <item name="object" xsi:type="object">Magento\InventorySales\Model\IsProductSalableCondition\IsAnySourceItemInStockCondition</item> </item> <item name="back_order" xsi:type="array"> <item name="sort_order" xsi:type="number">10</item> @@ -90,7 +90,7 @@ </item> <item name="stock_item_is_any_source_in_stock" xsi:type="array"> <item name="required" xsi:type="boolean">true</item> - <item name="object" xsi:type="object">Magento\InventorySales\Model\IsProductSalableForRequestedQtyCondition\IsAnySourceInStockCondition</item> + <item name="object" xsi:type="object">Magento\InventorySales\Model\IsProductSalableForRequestedQtyCondition\IsAnySourceItemInStockCondition</item> </item> <item name="back_order" xsi:type="array"> <item name="sort_order" xsi:type="number">10</item> From 4e38c0d9b2b98c3b9b487528680e641e16d2936d Mon Sep 17 00:00:00 2001 From: Vadim Justus <v.justus@techdivision.com> Date: Sat, 13 Apr 2019 16:41:22 +0200 Subject: [PATCH 134/231] magento-engcom/msi#2170: refactore cli commands for inconsistency detection --- .../Model/GetOrdersInFinalState.php | 2 +- .../GetSaleableQuantityInconsistencies.php | 103 ++++++++++++++++++ .../Model/SaleableQuantityInconsistency.php | 16 +++ 3 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 InventoryReservationCli/Model/GetSaleableQuantityInconsistencies.php create mode 100644 InventoryReservationCli/Model/SaleableQuantityInconsistency.php diff --git a/InventoryReservationCli/Model/GetOrdersInFinalState.php b/InventoryReservationCli/Model/GetOrdersInFinalState.php index 2cc6e8bcabca..33aa09c26780 100644 --- a/InventoryReservationCli/Model/GetOrdersInFinalState.php +++ b/InventoryReservationCli/Model/GetOrdersInFinalState.php @@ -32,7 +32,7 @@ class GetOrdersInFinalState * @param OrderRepositoryInterface $orderRepository * @param SearchCriteriaBuilder $searchCriteriaBuilder */ - public function __construct ( + public function __construct( OrderRepositoryInterface $orderRepository, SearchCriteriaBuilder $searchCriteriaBuilder ) { diff --git a/InventoryReservationCli/Model/GetSaleableQuantityInconsistencies.php b/InventoryReservationCli/Model/GetSaleableQuantityInconsistencies.php new file mode 100644 index 000000000000..7ab303d70d40 --- /dev/null +++ b/InventoryReservationCli/Model/GetSaleableQuantityInconsistencies.php @@ -0,0 +1,103 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservationCli\Model; + +use Magento\Framework\Serialize\SerializerInterface; +use Magento\InventoryReservationCli\Model\ResourceModel\GetReservationsList; + +/** + * Filter orders for missing initial reservation + */ +class GetSaleableQuantityInconsistencies +{ + /** + * @var GetReservationsList + */ + private $getReservationsList; + + /** + * @var SerializerInterface + */ + private $serialize; + + /** + * @var GetOrdersInNotFinalState + */ + private $getOrdersInNotFinalState; + + /** + * @param GetReservationsList $getReservationsList + * @param SerializerInterface $serialize + * @param GetOrdersInNotFinalState $getOrdersInNotFinalState + */ + public function __construct( + GetReservationsList $getReservationsList, + SerializerInterface $serialize, + GetOrdersInNotFinalState $getOrdersInNotFinalState + ) { + $this->getReservationsList = $getReservationsList; + $this->serialize = $serialize; + $this->getOrdersInNotFinalState = $getOrdersInNotFinalState; + } + + /** + * @return array + */ + public function execute(): array + { + $reservationList = $this->getReservationsList->execute(); + $expectedReservations = $this->getExpectedReservations(); + + $result = []; + foreach ($reservationList as $reservation) { + /** @var array $metadata */ + $metadata = $this->serialize->unserialize($reservation['metadata']); + $objectId = $metadata['object_id']; + $sku = $reservation['sku']; + $orderType = $metadata['object_type']; + + if ($orderType !== 'order') { + continue; + } + + if (!isset($result[$objectId])) { + $result[$objectId] = []; + } + if (!isset($result[$objectId][$sku])) { + $result[$objectId][$sku] = 0.0; + } + + $result[$objectId][$sku] += (float) $reservation['quantity']; + } + + foreach ($result as &$entry) { + $entry = array_filter($entry); + } + + return array_filter($result); + } + + private function getExpectedReservations() + { + $incompleteOrders = $this->getOrdersInNotFinalState->execute(); + + $result = []; + foreach ($incompleteOrders as $order) { + $entityId = $order->getEntityId(); + $list[$entityId] = [ + 'increment_id' => $order->getIncrementId(), + 'skus' => [] + ]; + foreach ($order->getItems() as $item) { + $list[$entityId]['skus'][$item->getSku()] = (float)$item->getQtyOrdered(); + } + } + + return $list; + } +} diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency.php b/InventoryReservationCli/Model/SaleableQuantityInconsistency.php new file mode 100644 index 000000000000..7db779f9f772 --- /dev/null +++ b/InventoryReservationCli/Model/SaleableQuantityInconsistency.php @@ -0,0 +1,16 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservationCli\Model; + +/** + * Filter orders for missing initial reservation + */ +class SaleableQuantityInconsistency +{ + +} From 1a5ad70f052992d8ee1efa0f1ee3a408d98c59d4 Mon Sep 17 00:00:00 2001 From: Vladyslav Podorozhnyi <v.podorozhnyi@ism-ukraine.com> Date: Sat, 13 Apr 2019 17:41:32 +0300 Subject: [PATCH 135/231] Product status is 'In Stock' on storefront even it is Out of Stock in all Sources #2156. --- ...onditionTest.php => IsAnySourceItemInStockConditionTest.php} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename InventorySales/Test/Integration/IsProductSalable/{IsAnySourceInStockConditionTest.php => IsAnySourceItemInStockConditionTest.php} (97%) diff --git a/InventorySales/Test/Integration/IsProductSalable/IsAnySourceInStockConditionTest.php b/InventorySales/Test/Integration/IsProductSalable/IsAnySourceItemInStockConditionTest.php similarity index 97% rename from InventorySales/Test/Integration/IsProductSalable/IsAnySourceInStockConditionTest.php rename to InventorySales/Test/Integration/IsProductSalable/IsAnySourceItemInStockConditionTest.php index 1b1d515d80b6..2991fcc5eea6 100644 --- a/InventorySales/Test/Integration/IsProductSalable/IsAnySourceInStockConditionTest.php +++ b/InventorySales/Test/Integration/IsProductSalable/IsAnySourceItemInStockConditionTest.php @@ -15,7 +15,7 @@ use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; -class IsAnySourceInStockConditionTest extends TestCase +class IsAnySourceItemInStockConditionTest extends TestCase { /** * @var IsAnySourceItemInStockCondition From d52b727788f1789f349124bfb169779a4ea0a0b8 Mon Sep 17 00:00:00 2001 From: Maksym Novik <m.novik@ism-ukraine.com> Date: Sat, 13 Apr 2019 17:54:17 +0300 Subject: [PATCH 136/231] Product status is 'In Stock' on storefront even it is Out of Stock in all Sources #2156. Tests coverage IsProductSalableForRequestedQty --- .../IsAnySourceItemInStockConditionTest.php | 2 +- .../IsAnySourceItemInStockConditionTest.php | 74 +++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 InventorySales/Test/Integration/IsProductSalableForRequestedQty/IsAnySourceItemInStockConditionTest.php diff --git a/InventorySales/Test/Integration/IsProductSalable/IsAnySourceItemInStockConditionTest.php b/InventorySales/Test/Integration/IsProductSalable/IsAnySourceItemInStockConditionTest.php index 2991fcc5eea6..05f646c35baf 100644 --- a/InventorySales/Test/Integration/IsProductSalable/IsAnySourceItemInStockConditionTest.php +++ b/InventorySales/Test/Integration/IsProductSalable/IsAnySourceItemInStockConditionTest.php @@ -57,7 +57,7 @@ protected function setUp() * * @magentoDbIsolation disabled */ - public function testSourcesItemsAreOutOfStock() + public function testSourceItemsAreOutOfStock() { $product = $this->productRepository->get('SKU-1'); $stockItemSearchCriteria = $this->stockItemCriteriaFactory->create(); diff --git a/InventorySales/Test/Integration/IsProductSalableForRequestedQty/IsAnySourceItemInStockConditionTest.php b/InventorySales/Test/Integration/IsProductSalableForRequestedQty/IsAnySourceItemInStockConditionTest.php new file mode 100644 index 000000000000..669fa09046b7 --- /dev/null +++ b/InventorySales/Test/Integration/IsProductSalableForRequestedQty/IsAnySourceItemInStockConditionTest.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventorySales\Test\Integration\IsProductSalableForRequestedQty; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\CatalogInventory\Api\Data\StockItemInterface; +use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory; +use Magento\CatalogInventory\Api\StockItemRepositoryInterface; +use Magento\InventorySales\Model\IsProductSalableForRequestedQtyCondition\IsAnySourceItemInStockCondition; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +class IsAnySourceItemInStockConditionTest extends TestCase +{ + /** + * @var IsAnySourceItemInStockCondition + */ + private $isAnySourceInStockCondition; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var StockItemCriteriaInterfaceFactory + */ + private $stockItemCriteriaFactory; + + /** + * @var StockItemRepositoryInterface + */ + private $stockItemRepository; + + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->isAnySourceInStockCondition = $objectManager->get( + IsAnySourceItemInStockCondition::class + ); + $this->productRepository = $objectManager->get(ProductRepositoryInterface::class); + $this->stockItemCriteriaFactory = $objectManager->get(StockItemCriteriaInterfaceFactory::class); + $this->stockItemRepository = $objectManager->get(StockItemRepositoryInterface::class); + } + + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stocks.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stock_source_links.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items_out_of_stock.php + * + * @magentoDbIsolation disabled + */ + public function testSourceItemsAreOutOfStock() + { + $product = $this->productRepository->get('SKU-1'); + $stockItemSearchCriteria = $this->stockItemCriteriaFactory->create(); + $stockItemSearchCriteria->setProductsFilter($product->getId()); + $stockItemsCollection = $this->stockItemRepository->getList($stockItemSearchCriteria); + + /** @var StockItemInterface $legacyStockItem */ + $legacyStockItem = current($stockItemsCollection->getItems()); + $legacyStockItem->setBackorders(1); + $legacyStockItem->setUseConfigBackorders(0); + $this->stockItemRepository->save($legacyStockItem); + $this->assertFalse($this->isAnySourceInStockCondition->execute('SKU-1', 10, 1)->isSalable()); + } +} From dccf2d1d91d97091f16d585d3465fd23be35a3e7 Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Sat, 13 Apr 2019 18:14:50 +0300 Subject: [PATCH 137/231] MSI-2062: Inventory Export Stock: refactoring rabbit and turtle processors --- .../Model/ExportStockData.php | 94 ---------------- .../Model/ExportStockDataSearchResult.php | 18 ---- .../Model/ExportStockIndexData.php | 47 ++++++++ .../ExportStockProcessorInterface.php | 23 ---- ...or.php => PreciseExportStockProcessor.php} | 52 ++++++--- .../StockExportProcessorPool.php | 58 ---------- .../Model/ExportStockSalableQty.php | 86 +++++++++++++++ .../ExportStockSalableQtySearchResult.php | 18 ++++ .../Model/GetQtyForNotManageStock.php | 4 +- .../Model/ResourceModel/GetStockIndexDump.php | 102 +++++++++++++----- InventoryExportStock/etc/di.xml | 29 ++--- ...tStockSalableQtySearchResultInterface.php} | 4 +- .../Api/ExportStockIndexDataInterface.php | 25 +++++ ...php => ExportStockSalableQtyInterface.php} | 9 +- InventoryExportStockApi/etc/webapi.xml | 10 +- 15 files changed, 317 insertions(+), 262 deletions(-) delete mode 100644 InventoryExportStock/Model/ExportStockData.php delete mode 100644 InventoryExportStock/Model/ExportStockDataSearchResult.php create mode 100644 InventoryExportStock/Model/ExportStockIndexData.php delete mode 100644 InventoryExportStock/Model/ExportStockProcessor/ExportStockProcessorInterface.php rename InventoryExportStock/Model/ExportStockProcessor/{PreciseStockProcessor.php => PreciseExportStockProcessor.php} (61%) delete mode 100644 InventoryExportStock/Model/ExportStockProcessor/StockExportProcessorPool.php create mode 100644 InventoryExportStock/Model/ExportStockSalableQty.php create mode 100644 InventoryExportStock/Model/ExportStockSalableQtySearchResult.php rename InventoryExportStockApi/Api/Data/{ExportStockDataSearchResultInterface.php => ExportStockSalableQtySearchResultInterface.php} (78%) create mode 100644 InventoryExportStockApi/Api/ExportStockIndexDataInterface.php rename InventoryExportStockApi/Api/{ExportStockDataInterface.php => ExportStockSalableQtyInterface.php} (65%) diff --git a/InventoryExportStock/Model/ExportStockData.php b/InventoryExportStock/Model/ExportStockData.php deleted file mode 100644 index e3ab10ea22d0..000000000000 --- a/InventoryExportStock/Model/ExportStockData.php +++ /dev/null @@ -1,94 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryExportStock\Model; - -use Exception; -use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Framework\Api\SearchCriteriaInterface; -use Magento\Framework\Api\SearchResultsInterface; -use Magento\InventoryExportStock\Model\ExportStockProcessor\StockExportProcessorPool; -use Magento\InventoryExportStockApi\Api\Data\ExportStockDataSearchResultInterface; -use Magento\InventoryExportStockApi\Api\Data\ExportStockDataSearchResultInterfaceFactory; -use Magento\InventoryExportStockApi\Api\ExportStockDataInterface; - -/** - * Class ExportStockData provides product stock information by search criteria - */ -class ExportStockData implements ExportStockDataInterface -{ - /** - * @var ProductRepositoryInterface - */ - private $productRepository; - - /** - * @var ExportStockDataSearchResultInterfaceFactory - */ - private $exportStockDataSearchResultFactory; - - /** - * @var int - */ - private $processorType; - - /** - * @var StockExportProcessorPool - */ - private $stockExportProcessorPool; - - /** - * ExportStockData constructor - * - * @param ProductRepositoryInterface $productRepository - * @param ExportStockDataSearchResultInterfaceFactory $exportStockDataSearchResultFactory - * @param StockExportProcessorPool $stockExportProcessorPool - * @param string $processor - */ - public function __construct( - ProductRepositoryInterface $productRepository, - ExportStockDataSearchResultInterfaceFactory $exportStockDataSearchResultFactory, - StockExportProcessorPool $stockExportProcessorPool, - string $processor - ) { - $this->productRepository = $productRepository; - $this->exportStockDataSearchResultFactory = $exportStockDataSearchResultFactory; - $this->stockExportProcessorPool = $stockExportProcessorPool; - $this->processorType = $processor; - } - - /** - * @inheritDoc - * - * @throws Exception - */ - public function execute( - SearchCriteriaInterface $searchCriteria, - int $stockId - ): ExportStockDataSearchResultInterface { - $productSearchResult = $this->getProducts($searchCriteria); - $processor = $this->stockExportProcessorPool->getStockExportProcessorByName($this->processorType); - $items = $processor->execute($productSearchResult->getItems(), $stockId); - $searchResult = $this->exportStockDataSearchResultFactory->create(); - $searchResult->setSearchCriteria($productSearchResult->getSearchCriteria()); - $searchResult->setItems($items); - $searchResult->setTotalCount(count($items)); - - return $searchResult; - } - - /** - * Provides product search result by search criteria - * - * @param SearchCriteriaInterface $searchCriteria - * @return SearchResultsInterface - */ - private function getProducts(SearchCriteriaInterface $searchCriteria): SearchResultsInterface - { - return $this->productRepository->getList($searchCriteria); - } -} diff --git a/InventoryExportStock/Model/ExportStockDataSearchResult.php b/InventoryExportStock/Model/ExportStockDataSearchResult.php deleted file mode 100644 index 5861ed1416c6..000000000000 --- a/InventoryExportStock/Model/ExportStockDataSearchResult.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryExportStock\Model; - -use Magento\Framework\Api\Search\SearchResult; -use Magento\InventoryExportStockApi\Api\Data\ExportStockDataSearchResultInterface; - -/** - * Class ExportStockDataSearchResult - */ -class ExportStockDataSearchResult extends SearchResult implements ExportStockDataSearchResultInterface -{ -} diff --git a/InventoryExportStock/Model/ExportStockIndexData.php b/InventoryExportStock/Model/ExportStockIndexData.php new file mode 100644 index 000000000000..99f9f13c3bb9 --- /dev/null +++ b/InventoryExportStock/Model/ExportStockIndexData.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryExportStock\Model; + +use Magento\InventoryExportStock\Model\ResourceModel\GetStockIndexDump; +use Magento\InventoryExportStockApi\Api\ExportStockIndexDataInterface; +use Zend_Db_Select_Exception; + +/** + * Class ExportStockIndexData + */ +class ExportStockIndexData implements ExportStockIndexDataInterface +{ + /** + * @var GetStockIndexDump + */ + private $getStockIndexDump; + + /** + * ExportStockIndexData constructor + * + * @param GetStockIndexDump $getStockIndexDump + */ + public function __construct( + GetStockIndexDump $getStockIndexDump + ) { + $this->getStockIndexDump = $getStockIndexDump; + } + + /** + * Provides stock index export from inventory_stock_% table + * + * @param int $stockId + * @return array + * @throws Zend_Db_Select_Exception + */ + public function execute( + int $stockId + ): array { + return $this->getStockIndexDump->execute($stockId); + } +} diff --git a/InventoryExportStock/Model/ExportStockProcessor/ExportStockProcessorInterface.php b/InventoryExportStock/Model/ExportStockProcessor/ExportStockProcessorInterface.php deleted file mode 100644 index 1f41c62c96d6..000000000000 --- a/InventoryExportStock/Model/ExportStockProcessor/ExportStockProcessorInterface.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryExportStock\Model\ExportStockProcessor; - -/** - * Interface StockExportProcessorInterface provides product stock data - */ -interface ExportStockProcessorInterface -{ - /** - * Provides product stock data - * - * @param array $products - * @param int $stockId - * @return array - */ - public function execute(array $products, int $stockId):array; -} diff --git a/InventoryExportStock/Model/ExportStockProcessor/PreciseStockProcessor.php b/InventoryExportStock/Model/ExportStockProcessor/PreciseExportStockProcessor.php similarity index 61% rename from InventoryExportStock/Model/ExportStockProcessor/PreciseStockProcessor.php rename to InventoryExportStock/Model/ExportStockProcessor/PreciseExportStockProcessor.php index 4f76fc5d73ad..9f8b0bf5a97e 100644 --- a/InventoryExportStock/Model/ExportStockProcessor/PreciseStockProcessor.php +++ b/InventoryExportStock/Model/ExportStockProcessor/PreciseExportStockProcessor.php @@ -7,8 +7,10 @@ namespace Magento\InventoryExportStock\Model\ExportStockProcessor; +use Magento\Catalog\Api\Data\ProductInterface; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\LocalizedException; +use Magento\InventoryConfigurationApi\Api\GetStockItemConfigurationInterface; use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForSkuInterface; use Magento\InventoryExportStock\Model\GetQtyForNotManageStock; use Magento\InventorySalesApi\Api\GetProductSalableQtyInterface; @@ -16,10 +18,8 @@ /** * Class Provides precise method of getting stock data */ -class PreciseStockProcessor implements ExportStockProcessorInterface +class PreciseExportStockProcessor { - public const PROCESSOR_TYPE = 'precise'; - /** * @var IsSourceItemManagementAllowedForSkuInterface */ @@ -30,25 +30,33 @@ class PreciseStockProcessor implements ExportStockProcessorInterface */ private $getProductSalableQty; + /** + * @var GetStockItemConfigurationInterface + */ + private $getStockItemConfiguration; + /** * @var GetQtyForNotManageStock */ private $getQtyForNotManageStock; /** - * PreciseStockProcessor constructor + * PreciseExportStockProcessor constructor * * @param IsSourceItemManagementAllowedForSkuInterface $isSourceItemManagementAllowedForSku - * @param GetQtyForNotManageStock $getQtyForNotManageStock * @param GetProductSalableQtyInterface $getProductSalableQty + * @param GetStockItemConfigurationInterface $getStockItemConfiguration + * @param GetQtyForNotManageStock $getQtyForNotManageStock */ public function __construct( IsSourceItemManagementAllowedForSkuInterface $isSourceItemManagementAllowedForSku, - GetQtyForNotManageStock $getQtyForNotManageStock, - GetProductSalableQtyInterface $getProductSalableQty + GetProductSalableQtyInterface $getProductSalableQty, + GetStockItemConfigurationInterface $getStockItemConfiguration, + GetQtyForNotManageStock $getQtyForNotManageStock ) { $this->isSourceItemManagementAllowedForSku = $isSourceItemManagementAllowedForSku; $this->getProductSalableQty = $getProductSalableQty; + $this->getStockItemConfiguration = $getStockItemConfiguration; $this->getQtyForNotManageStock = $getQtyForNotManageStock; } @@ -63,14 +71,15 @@ public function __construct( */ public function execute(array $products, int $stockId): array { - $qtyForNotManageStock = $this->getQtyForNotManageStock->execute(); + $skus = $this->getProductSkus($products); $items = []; - foreach ($products as $product) { - $sku = $product->getSku(); - if ($this->isSourceItemManagementAllowedForSku->execute($sku)) { - $qty = $this->getProductSalableQty->execute($sku, $stockId) ?: $qtyForNotManageStock; - } else { + foreach ($skus as $sku) { + if (!$this->getStockItemConfiguration->execute($sku, $stockId)->isManageStock()) { + $qty = $this->getQtyForNotManageStock->execute(); + } elseif (!$this->isSourceItemManagementAllowedForSku->execute($sku)) { $qty = null; + } else { + $qty = $this->getProductSalableQty->execute($sku, $stockId); } $items[] = [ @@ -81,4 +90,21 @@ public function execute(array $products, int $stockId): array return $items; } + + /** + * Extracts product skus from $product array + * + * @param array $products + * @return array + */ + private function getProductSkus(array $products): array + { + $skus = []; + /** @var ProductInterface $product */ + foreach ($products as $product) { + $skus[] = $product->getSku(); + } + + return $skus; + } } diff --git a/InventoryExportStock/Model/ExportStockProcessor/StockExportProcessorPool.php b/InventoryExportStock/Model/ExportStockProcessor/StockExportProcessorPool.php deleted file mode 100644 index 20c0ed13dbd7..000000000000 --- a/InventoryExportStock/Model/ExportStockProcessor/StockExportProcessorPool.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryExportStock\Model\ExportStockProcessor; - -use InvalidArgumentException; - -/** - * Class ProcessorPool provides processor by it's type - */ -class StockExportProcessorPool -{ - /** - * @var array - */ - private $processors; - - /** - * StockExportProcessorPool constructor - * - * @param array $processors - * @throws InvalidArgumentException - */ - public function __construct( - array $processors - ) { - $this->processors = $processors; - foreach ($this->processors as $processor) { - if (!$processor instanceof ExportStockProcessorInterface) { - throw new InvalidArgumentException( - __('One of processor is not instance of StockExportProcessorInterface class') - ); - } - } - } - - /** - * Provides processor by it's type - * - * @param string $processorsName - * @return ExportStockProcessorInterface - */ - public function getStockExportProcessorByName(string $processorsName): ExportStockProcessorInterface - { - foreach ($this->processors as $name => $processor) { - if ($processorsName === $name) { - return $processor; - } - } - throw new InvalidArgumentException( - __('Processor with such name is absent') - ); - } -} diff --git a/InventoryExportStock/Model/ExportStockSalableQty.php b/InventoryExportStock/Model/ExportStockSalableQty.php new file mode 100644 index 000000000000..387e227da4ff --- /dev/null +++ b/InventoryExportStock/Model/ExportStockSalableQty.php @@ -0,0 +1,86 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryExportStock\Model; + +use Exception; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Api\SearchCriteriaInterface; +use Magento\Framework\Api\SearchResultsInterface; +use Magento\InventoryExportStock\Model\ExportStockProcessor\PreciseExportStockProcessor; +use Magento\InventoryExportStockApi\Api\Data\ExportStockSalableQtySearchResultInterface; +use Magento\InventoryExportStockApi\Api\Data\ExportStockSalableQtySearchResultInterfaceFactory; +use Magento\InventoryExportStockApi\Api\ExportStockSalableQtyInterface; + +/** + * Class ExportStockSalableQty provides product stock information by search criteria + */ +class ExportStockSalableQty implements ExportStockSalableQtyInterface +{ + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var ExportStockSalableQtySearchResultInterfaceFactory + */ + private $exportStockSalableQtySearchResultFactory; + + /** + * @var PreciseExportStockProcessor + */ + private $preciseExportStockProcessor; + + /** + * ExportStockSalableQty constructor + * + * @param ProductRepositoryInterface $productRepository + * @param ExportStockSalableQtySearchResultInterfaceFactory $exportStockSalableQtySearchResultFactory + * @param PreciseExportStockProcessor $preciseExportStockProcessor + */ + public function __construct( + ProductRepositoryInterface $productRepository, + ExportStockSalableQtySearchResultInterfaceFactory $exportStockSalableQtySearchResultFactory, + PreciseExportStockProcessor $preciseExportStockProcessor + ) { + $this->productRepository = $productRepository; + $this->exportStockSalableQtySearchResultFactory = $exportStockSalableQtySearchResultFactory; + $this->preciseExportStockProcessor = $preciseExportStockProcessor; + } + + /** + * @inheritDoc + * + * @throws Exception + */ + public function execute( + SearchCriteriaInterface $searchCriteria, + int $stockId + ): ExportStockSalableQtySearchResultInterface { + $productSearchResult = $this->getProducts($searchCriteria); + $items = $this->preciseExportStockProcessor->execute($productSearchResult->getItems(), $stockId); + /** @var ExportStockSalableQtySearchResultInterface $searchResult */ + $searchResult = $this->exportStockSalableQtySearchResultFactory->create(); + $searchResult->setSearchCriteria($productSearchResult->getSearchCriteria()); + $searchResult->setItems($items); + $searchResult->setTotalCount(count($items)); + + return $searchResult; + } + + /** + * Provides product search result by search criteria + * + * @param SearchCriteriaInterface $searchCriteria + * @return SearchResultsInterface + */ + private function getProducts(SearchCriteriaInterface $searchCriteria): SearchResultsInterface + { + return $this->productRepository->getList($searchCriteria); + } +} diff --git a/InventoryExportStock/Model/ExportStockSalableQtySearchResult.php b/InventoryExportStock/Model/ExportStockSalableQtySearchResult.php new file mode 100644 index 000000000000..6f569f6141f7 --- /dev/null +++ b/InventoryExportStock/Model/ExportStockSalableQtySearchResult.php @@ -0,0 +1,18 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryExportStock\Model; + +use Magento\Framework\Api\Search\SearchResult; +use Magento\InventoryExportStockApi\Api\Data\ExportStockSalableQtySearchResultInterface; + +/** + * Class ExportStockSalableQtyExportStockSalableQtySearchResult + */ +class ExportStockSalableQtySearchResult extends SearchResult implements ExportStockSalableQtySearchResultInterface +{ +} diff --git a/InventoryExportStock/Model/GetQtyForNotManageStock.php b/InventoryExportStock/Model/GetQtyForNotManageStock.php index 50e3504d4d3c..b39ea9b95571 100644 --- a/InventoryExportStock/Model/GetQtyForNotManageStock.php +++ b/InventoryExportStock/Model/GetQtyForNotManageStock.php @@ -8,7 +8,7 @@ namespace Magento\InventoryExportStock\Model; /** - * Class Provides qty for not manage stock from di configuration + * Class GetQtyForNotManageStock provides qtyForNotManageStock from di configuration */ class GetQtyForNotManageStock { @@ -29,7 +29,7 @@ public function __construct( } /** - * Provides qty for not manage stock from di configuration + * Provides qtyForNotManageStock from di configuration * * @return int */ diff --git a/InventoryExportStock/Model/ResourceModel/GetStockIndexDump.php b/InventoryExportStock/Model/ResourceModel/GetStockIndexDump.php index f7bbc031f8b7..a9523b98ea36 100644 --- a/InventoryExportStock/Model/ResourceModel/GetStockIndexDump.php +++ b/InventoryExportStock/Model/ResourceModel/GetStockIndexDump.php @@ -8,7 +8,13 @@ namespace Magento\InventoryExportStock\Model\ResourceModel; use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Select; +use Magento\InventoryExportStock\Model\GetQtyForNotManageStock; use Magento\InventoryIndexer\Model\StockIndexTableNameResolverInterface; +use Magento\InventorySales\Model\ResourceModel\IsStockItemSalableCondition\ManageStockCondition; +use Zend_Db_Expr; +use Zend_Db_Select_Exception; /** * Class GetStockIndexDump provides sku and qty of products dumping them from stock index table @@ -25,61 +31,109 @@ class GetStockIndexDump */ private $resourceConnection; + /** + * @var ManageStockCondition + */ + private $manageStockCondition; + + /** + * @var AdapterInterface + */ + private $connection; + + /** + * @var GetQtyForNotManageStock + */ + private $getQtyForNotManageStock; + /** * GetStockIndexDump constructor * * @param StockIndexTableNameResolverInterface $stockIndexTableNameResolver * @param ResourceConnection $resourceConnection + * @param ManageStockCondition $manageStockCondition + * @param GetQtyForNotManageStock $getQtyForNotManageStock */ public function __construct( StockIndexTableNameResolverInterface $stockIndexTableNameResolver, - ResourceConnection $resourceConnection + ResourceConnection $resourceConnection, + ManageStockCondition $manageStockCondition, + GetQtyForNotManageStock $getQtyForNotManageStock ) { $this->stockIndexTableNameResolver = $stockIndexTableNameResolver; $this->resourceConnection = $resourceConnection; + $this->manageStockCondition = $manageStockCondition; + $this->getQtyForNotManageStock = $getQtyForNotManageStock; } /** * Provides sku and qty of products dumping them from stock index table * - * @param array $products * @param int $stockId * @return array + * @throws Zend_Db_Select_Exception + */ + public function execute(int $stockId): array + { + $this->connection = $this->resourceConnection->getConnection(); + $select = $this->connection->select(); + $select->union([ + $this->getStockItemSelect($stockId), + $this->getStockIndexSelect($stockId) + ]); + + return $this->connection->fetchAll($select); + } + + /** + * Provides stock select + * + * @param int $stockId + * @return Select */ - public function execute(array $products, int $stockId): array + private function getStockIndexSelect(int $stockId): Select { - $stockIndexTableName = $this->stockIndexTableNameResolver->execute($stockId); - $tableName = $this->resourceConnection->getTableName($stockIndexTableName); - $connection = $this->resourceConnection->getConnection(); - $select = $connection->select() - ->from($tableName) - ->columns( + $stockIndexTableName = $this->resourceConnection + ->getTableName($this->stockIndexTableNameResolver->execute($stockId)); + return $this->connection->select() + ->from( + $stockIndexTableName, [ - 'sku' => 'sku', 'qty' => 'quantity', + 'sku' => 'sku' ] - )->where( - 'sku IN (?)', - $this->getProductSkus($products) ); - - return $connection->fetchAll($select); } /** - * Provides list of product skus by product array + * Provides stock item select * - * @param array $products - * @return string[] + * @param int $stockId + * @return Select */ - private function getProductSkus(array $products): array + private function getStockItemSelect(int $stockId): Select { - $skus = []; - foreach ($products as $product) { - $skus[] = $product->getSku(); - } + $legacyStockItemTable = $this->resourceConnection + ->getTableName('cataloginventory_stock_item'); + $productEntityTable = $this->resourceConnection + ->getTableName('catalog_product_entity'); + $select = $this->connection->select(); + + $select->from( + ['legacy_stock_item' => $legacyStockItemTable], + new Zend_Db_Expr('"' . $this->getQtyForNotManageStock->execute() . '" as qty') + )->join( + ['product_entity' => $productEntityTable], + 'legacy_stock_item.product_id = product_entity.entity_id', + ['sku'] + )->where( + $this->manageStockCondition->execute($select) + )->where( + 'legacy_stock_item.stock_id = ?', + $stockId + ); - return $skus; + return $select; } } diff --git a/InventoryExportStock/etc/di.xml b/InventoryExportStock/etc/di.xml index 2569625c59db..1470e7d4dd30 100644 --- a/InventoryExportStock/etc/di.xml +++ b/InventoryExportStock/etc/di.xml @@ -7,18 +7,15 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> - <preference for="Magento\InventoryExportStockApi\Api\Data\ExportStockDataSearchResultInterface" - type="Magento\InventoryExportStock\Model\ExportStockDataSearchResult" /> - <preference for="Magento\InventoryExportStockApi\Api\Data\ProductStockDataInterface" - type="Magento\InventoryExportStock\Model\ProductStockData"/> - <preference for="Magento\InventoryExportStockApi\Api\ExportStockDataInterface" - type="Magento\InventoryExportStock\Model\ExportStockData"/> - <type name="Magento\InventoryExportStock\Model\ExportStockData"> + <preference for="Magento\InventoryExportStockApi\Api\Data\ExportStockSalableQtySearchResultInterface" + type="Magento\InventoryExportStock\Model\ExportStockSalableQtySearchResult"/> + <preference for="Magento\InventoryExportStockApi\Api\ExportStockSalableQtyInterface" + type="Magento\InventoryExportStock\Model\ExportStockSalableQty"/> + <preference for="Magento\InventoryExportStockApi\Api\ExportStockIndexDataInterface" + type="Magento\InventoryExportStock\Model\ExportStockIndexData"/> + <type name="Magento\InventoryExportStock\Model\ExportStockProcessor\PreciseExportStockProcessor"> <arguments> - <argument name="processor" - xsi:type="const">Magento\InventoryExportStock\Model\ExportStockProcessor\StockIndexDumpProcessor::PROCESSOR_TYPE</argument> -<!-- <argument name="processor"--> -<!-- xsi:type="const">Magento\InventoryExportStock\Model\ExportStockProcessor\PreciseStockProcessor::PROCESSOR_TYPE</argument>--> + <argument name="qtyForNotManageStock" xsi:type="number">1</argument> </arguments> </type> <type name="Magento\InventoryExportStock\Model\GetQtyForNotManageStock"> @@ -26,14 +23,4 @@ <argument name="qtyForNotManageStock" xsi:type="number">1</argument> </arguments> </type> - <type name="Magento\InventoryExportStock\Model\ExportStockProcessor\StockExportProcessorPool"> - <arguments> - <argument name="processors" xsi:type="array"> - <item name="stock_dump" - xsi:type="object">Magento\InventoryExportStock\Model\ExportStockProcessor\StockIndexDumpProcessor</item> - <item name="precise" - xsi:type="object">Magento\InventoryExportStock\Model\ExportStockProcessor\PreciseStockProcessor</item> - </argument> - </arguments> - </type> </config> diff --git a/InventoryExportStockApi/Api/Data/ExportStockDataSearchResultInterface.php b/InventoryExportStockApi/Api/Data/ExportStockSalableQtySearchResultInterface.php similarity index 78% rename from InventoryExportStockApi/Api/Data/ExportStockDataSearchResultInterface.php rename to InventoryExportStockApi/Api/Data/ExportStockSalableQtySearchResultInterface.php index 58435a150f83..69d40075f53f 100644 --- a/InventoryExportStockApi/Api/Data/ExportStockDataSearchResultInterface.php +++ b/InventoryExportStockApi/Api/Data/ExportStockSalableQtySearchResultInterface.php @@ -10,10 +10,10 @@ use Magento\Framework\Api\SearchResultsInterface; /** - * Interface for ExportStockDataSearchResult + * Interface for ExportStockSalableQtySearchResult * @api */ -interface ExportStockDataSearchResultInterface extends SearchResultsInterface +interface ExportStockSalableQtySearchResultInterface extends SearchResultsInterface { /** * Get stock data array diff --git a/InventoryExportStockApi/Api/ExportStockIndexDataInterface.php b/InventoryExportStockApi/Api/ExportStockIndexDataInterface.php new file mode 100644 index 000000000000..f23bb610f30d --- /dev/null +++ b/InventoryExportStockApi/Api/ExportStockIndexDataInterface.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryExportStockApi\Api; + +/** + * Interface for ExportStockIndexData which provides stock index export + * @api + */ +interface ExportStockIndexDataInterface +{ + /** + * Provides stock index export from inventory_stock_% table + * + * @param int $stockId + * @return array + */ + public function execute( + int $stockId + ): array; +} diff --git a/InventoryExportStockApi/Api/ExportStockDataInterface.php b/InventoryExportStockApi/Api/ExportStockSalableQtyInterface.php similarity index 65% rename from InventoryExportStockApi/Api/ExportStockDataInterface.php rename to InventoryExportStockApi/Api/ExportStockSalableQtyInterface.php index cecadd2e85ba..6c4dccbf001b 100644 --- a/InventoryExportStockApi/Api/ExportStockDataInterface.php +++ b/InventoryExportStockApi/Api/ExportStockSalableQtyInterface.php @@ -2,27 +2,26 @@ /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. - * @noinspection PhpFullyQualifiedNameUsageInspection */ declare(strict_types=1); namespace Magento\InventoryExportStockApi\Api; /** - * Interface for ExportStockData provides product stock information by search criteria + * Interface for ExportStockSalableQty provides product's salable qty information by search criteria * @api */ -interface ExportStockDataInterface +interface ExportStockSalableQtyInterface { /** * Provides stock export data * * @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria * @param int $stockId - * @return \Magento\InventoryExportStockApi\Api\Data\ExportStockDataSearchResultInterface + * @return \Magento\InventoryExportStockApi\Api\Data\ExportStockSalableQtySearchResultInterface */ public function execute( \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria, int $stockId - ): \Magento\InventoryExportStockApi\Api\Data\ExportStockDataSearchResultInterface; + ): \Magento\InventoryExportStockApi\Api\Data\ExportStockSalableQtySearchResultInterface; } diff --git a/InventoryExportStockApi/etc/webapi.xml b/InventoryExportStockApi/etc/webapi.xml index 3f4f2b02057b..225dbdf5f9df 100644 --- a/InventoryExportStockApi/etc/webapi.xml +++ b/InventoryExportStockApi/etc/webapi.xml @@ -7,8 +7,14 @@ --> <routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Webapi:etc/webapi.xsd"> - <route url="/V1/inventory/export-stock-data" method="GET"> - <service class="Magento\InventoryExportStockApi\Api\ExportStockDataInterface" method="execute"/> + <route url="/V1/inventory/export-stock-salable-qty" method="GET"> + <service class="Magento\InventoryExportStockApi\Api\ExportStockSalableQtyInterface" method="execute"/> + <resources> + <resource ref="Magento_InventoryApi::stock"/> + </resources> + </route> + <route url="/V1/inventory/dump-stock-index-data" method="GET"> + <service class="Magento\InventoryExportStockApi\Api\ExportStockIndexDataInterface" method="execute"/> <resources> <resource ref="Magento_InventoryApi::stock"/> </resources> From 4a4c91bbf014df2e12ea6def0ea5aaff1fd75787 Mon Sep 17 00:00:00 2001 From: "al.kravchuk" <al.kravchuk@ism-ukraine.com> Date: Sat, 13 Apr 2019 19:17:22 +0300 Subject: [PATCH 138/231] MSI-2126 Immutable DTO for Pickup Location. Add missed field. --- InventoryInStorePickup/Model/PickupLocation.php | 16 ++++++++++++++++ .../Api/Data/PickupLocationInterface.php | 7 +++++++ 2 files changed, 23 insertions(+) diff --git a/InventoryInStorePickup/Model/PickupLocation.php b/InventoryInStorePickup/Model/PickupLocation.php index c7394b84ce59..114e2d813a6f 100644 --- a/InventoryInStorePickup/Model/PickupLocation.php +++ b/InventoryInStorePickup/Model/PickupLocation.php @@ -92,9 +92,15 @@ class PickupLocation implements PickupLocationInterface */ private $openHours; + /** + * @var string|null + */ + private $email; + /** * @param string $sourceCode * @param string|null $name + * @param string|null $email * @param string|null $fax * @param string|null $contactName * @param string|null $description @@ -113,6 +119,7 @@ class PickupLocation implements PickupLocationInterface public function __construct( string $sourceCode, ?string $name = null, + ?string $email = null, ?string $fax = null, ?string $contactName = null, ?string $description = null, @@ -130,6 +137,7 @@ public function __construct( ) { $this->sourceCode = $sourceCode; $this->name = $name; + $this->email = $email; $this->fax = $fax; $this->contactName = $contactName; $this->description = $description; @@ -162,6 +170,14 @@ public function getName(): ?string return $this->name; } + /** + * @inheritdoc + */ + public function getEmail(): ?string + { + return $this->email; + } + /** * @inheritdoc */ diff --git a/InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php b/InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php index ebe98645f41a..96d114190519 100644 --- a/InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php +++ b/InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php @@ -25,6 +25,13 @@ public function getSourceCode(): string; */ public function getName(): ?string; + /** + * Get Pickup Location contact email + * + * @return string|null + */ + public function getEmail(): ?string; + /** * Get Fax contact info. * From 4bcaebcde46d8d13b0d0e95f84d6692afad810f5 Mon Sep 17 00:00:00 2001 From: "al.kravchuk" <al.kravchuk@ism-ukraine.com> Date: Sat, 13 Apr 2019 19:18:15 +0300 Subject: [PATCH 139/231] MSI-2126 Immutable DTO for Pickup Location. Add integration tests coverage. --- .../Integration/PickupLocation/MapperTest.php | 156 ++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 InventoryInStorePickup/Test/Integration/PickupLocation/MapperTest.php diff --git a/InventoryInStorePickup/Test/Integration/PickupLocation/MapperTest.php b/InventoryInStorePickup/Test/Integration/PickupLocation/MapperTest.php new file mode 100644 index 000000000000..633c2a59567e --- /dev/null +++ b/InventoryInStorePickup/Test/Integration/PickupLocation/MapperTest.php @@ -0,0 +1,156 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickup\Test\Integration\PickupLocation; + +use Magento\Framework\Api\ExtensionAttributesFactory; +use Magento\InventoryApi\Api\Data\SourceExtensionInterface; +use Magento\InventoryApi\Api\SourceRepositoryInterface; +use Magento\InventoryInStorePickup\Model\PickupLocation\Mapper; +use Magento\InventoryInStorePickupApi\Api\Data\PickupLocationExtension; +use Magento\InventoryInStorePickupApi\Api\Data\PickupLocationExtensionInterface; +use Magento\TestFramework\Helper\Bootstrap; + +class MapperTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + + /** + * @var \Magento\InventoryApi\Api\SourceRepositoryInterface + */ + private $sourceRepository; + + /** + * @var string + */ + private $sourceCode; + + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->sourceRepository = $this->objectManager->create(SourceRepositoryInterface::class); + $this->sourceCode = 'source-code-1'; + } + + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source.php + */ + public function testMapPickupLocation() + { + $source = $this->sourceRepository->get($this->sourceCode); + /** @var \Magento\InventoryInStorePickup\Model\PickupLocation\Mapper $mapper */ + $mapper = $this->objectManager->create(Mapper::class, ['map' => $this->getMap()]); + $pickupLocation = $mapper->map($source); + + $this->assertEquals($source->getSourceCode(), $pickupLocation->getSourceCode()); + $this->assertEquals($source->getEmail(), $pickupLocation->getEmail()); + $this->assertEquals($source->getContactName(), $pickupLocation->getContactName()); + $this->assertEquals($source->getDescription(), $pickupLocation->getDescription()); + $this->assertEquals($source->getLatitude(), $pickupLocation->getLatitude()); + $this->assertEquals($source->getLongitude(), $pickupLocation->getLongitude()); + $this->assertEquals($source->getCountryId(), $pickupLocation->getCountryId()); + $this->assertEquals($source->getRegionId(), $pickupLocation->getRegionId()); + $this->assertEquals($source->getRegion(), $pickupLocation->getRegion()); + $this->assertEquals($source->getCity(), $pickupLocation->getCity()); + $this->assertEquals($source->getStreet(), $pickupLocation->getStreet()); + $this->assertEquals($source->getPostcode(), $pickupLocation->getPostcode()); + $this->assertEquals($source->getPhone(), $pickupLocation->getPhone()); + $this->assertInstanceOf(PickupLocationExtensionInterface::class, $pickupLocation->getExtensionAttributes()); + } + + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source.php + */ + public function testMapPickupLocationWithExtensionAttributes() + { + $source = $this->sourceRepository->get($this->sourceCode); + + $sourceExtensionAttributes = $this->getMockBuilder(SourceExtensionInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getOpenHours', 'getSomeAttribute']) + ->getMockForAbstractClass(); + $sourceExtensionAttributes->expects($this->once()) + ->method('getOpenHours') + ->willReturn(['open', 'hours']); + $sourceExtensionAttributes->expects($this->once()) + ->method('getSomeAttribute') + ->willReturn('some_value'); + $source->setExtensionAttributes($sourceExtensionAttributes); + + $pickupLocationExtension = $this->getMockBuilder(PickupLocationExtensionInterface::class) + ->disableOriginalConstructor() + ->setMethods(['setPickupLocationAttribute']) + ->getMock(); + $pickupLocationExtension->expects($this->once()) + ->method('setPickupLocationAttribute') + ->with('some_value'); + + $extensionAttributesFactory = $this->getMockBuilder(ExtensionAttributesFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $extensionAttributesFactory->expects($this->once()) + ->method('create') + ->willReturn($pickupLocationExtension); + + $createFromSource = $this->objectManager->create( + Mapper\CreateFromSource::class, + ['extensionAttributesFactory' => $extensionAttributesFactory] + ); + + $map = $this->getMap(); + $map['extension_attributes.open_hours'] = 'open_hours'; + $map['extension_attributes.some_attribute'] = 'extension_attributes.pickup_location_attribute'; + + /** @var \Magento\InventoryInStorePickup\Model\PickupLocation\Mapper $mapper */ + $mapper = $this->objectManager->create( + Mapper::class, + ['map' => $map, 'createFromSource' => $createFromSource] + ); + $pickupLocation = $mapper->map($source); + + $this->assertEquals($source->getSourceCode(), $pickupLocation->getSourceCode()); + $this->assertEquals($source->getEmail(), $pickupLocation->getEmail()); + $this->assertEquals($source->getContactName(), $pickupLocation->getContactName()); + $this->assertEquals($source->getDescription(), $pickupLocation->getDescription()); + $this->assertEquals($source->getLatitude(), $pickupLocation->getLatitude()); + $this->assertEquals($source->getLongitude(), $pickupLocation->getLongitude()); + $this->assertEquals($source->getCountryId(), $pickupLocation->getCountryId()); + $this->assertEquals($source->getRegionId(), $pickupLocation->getRegionId()); + $this->assertEquals($source->getRegion(), $pickupLocation->getRegion()); + $this->assertEquals($source->getCity(), $pickupLocation->getCity()); + $this->assertEquals($source->getStreet(), $pickupLocation->getStreet()); + $this->assertEquals($source->getPostcode(), $pickupLocation->getPostcode()); + $this->assertEquals($source->getPhone(), $pickupLocation->getPhone()); + $this->assertEquals(['open', 'hours'], $pickupLocation->getOpenHours()); + } + + /** + * @return array + */ + private function getMap(): array + { + return [ + 'source_code' => 'source_code', + 'email' => 'email', + 'fax' => 'fax', + 'contact_name' => 'contact_name', + 'description' => 'description', + 'latitude' => 'latitude', + 'longitude' => 'longitude', + 'country_id' => 'country_id', + 'region_id' => 'region_id', + 'region' => 'region', + 'city' => 'city', + 'street' => 'street', + 'postcode' => 'postcode', + 'phone' => 'phone' + ]; + } +} From 9afa5175ca5a2f49a6dbb19893d09ad4eca3ce5d Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Sun, 14 Apr 2019 13:44:34 +0300 Subject: [PATCH 140/231] MSI-2062: Inventory Export Stock: refactoring --- .../Model/ExportStockIndexData.php | 25 +++--- .../StockIndexDumpProcessor.php | 79 ------------------- .../Model/ExportStockSalableQty.php | 1 - .../PreciseExportStockProcessor.php | 3 +- ...exDump.php => StockIndexDumpProcessor.php} | 4 +- 5 files changed, 19 insertions(+), 93 deletions(-) delete mode 100644 InventoryExportStock/Model/ExportStockProcessor/StockIndexDumpProcessor.php rename InventoryExportStock/Model/{ExportStockProcessor => }/PreciseExportStockProcessor.php (96%) rename InventoryExportStock/Model/ResourceModel/{GetStockIndexDump.php => StockIndexDumpProcessor.php} (99%) diff --git a/InventoryExportStock/Model/ExportStockIndexData.php b/InventoryExportStock/Model/ExportStockIndexData.php index 99f9f13c3bb9..72cfa60194d6 100644 --- a/InventoryExportStock/Model/ExportStockIndexData.php +++ b/InventoryExportStock/Model/ExportStockIndexData.php @@ -7,9 +7,10 @@ namespace Magento\InventoryExportStock\Model; -use Magento\InventoryExportStock\Model\ResourceModel\GetStockIndexDump; +use Exception; +use Magento\Framework\Exception\LocalizedException; +use Magento\InventoryExportStock\Model\ResourceModel\StockIndexDumpProcessor; use Magento\InventoryExportStockApi\Api\ExportStockIndexDataInterface; -use Zend_Db_Select_Exception; /** * Class ExportStockIndexData @@ -17,19 +18,19 @@ class ExportStockIndexData implements ExportStockIndexDataInterface { /** - * @var GetStockIndexDump + * @var StockIndexDumpProcessor */ - private $getStockIndexDump; + private $stockIndexDumpProcessor; /** * ExportStockIndexData constructor * - * @param GetStockIndexDump $getStockIndexDump + * @param StockIndexDumpProcessor $stockIndexDumpProcessor */ public function __construct( - GetStockIndexDump $getStockIndexDump + StockIndexDumpProcessor $stockIndexDumpProcessor ) { - $this->getStockIndexDump = $getStockIndexDump; + $this->stockIndexDumpProcessor = $stockIndexDumpProcessor; } /** @@ -37,11 +38,17 @@ public function __construct( * * @param int $stockId * @return array - * @throws Zend_Db_Select_Exception + * @throws LocalizedException */ public function execute( int $stockId ): array { - return $this->getStockIndexDump->execute($stockId); + try { + $items = $this->stockIndexDumpProcessor->execute($stockId); + } catch (Exception $e) { + throw new LocalizedException(__($e->getMessage())); + } + + return $items; } } diff --git a/InventoryExportStock/Model/ExportStockProcessor/StockIndexDumpProcessor.php b/InventoryExportStock/Model/ExportStockProcessor/StockIndexDumpProcessor.php deleted file mode 100644 index 816f66d6d84b..000000000000 --- a/InventoryExportStock/Model/ExportStockProcessor/StockIndexDumpProcessor.php +++ /dev/null @@ -1,79 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryExportStock\Model\ExportStockProcessor; - -use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForSkuInterface; -use Magento\InventoryExportStock\Model\GetQtyForNotManageStock; -use Magento\InventoryExportStock\Model\ResourceModel\GetStockIndexDump; - -/** - * Class StockIndexDumpProcessor provides sku and qty of products dumping them from stock index table - */ -class StockIndexDumpProcessor implements ExportStockProcessorInterface -{ - public const PROCESSOR_TYPE = 'stock_dump'; - - /** - * @var GetQtyForNotManageStock - */ - private $getQtyForNotManageStock; - - /** - * @var IsSourceItemManagementAllowedForSkuInterface - */ - private $isSourceItemManagementAllowedForSku; - - /** - * @var GetStockIndexDump - */ - private $getStockIndexDump; - - /** - * GetStockIndexDumpProcessor constructor - * - * @param GetQtyForNotManageStock $getQtyForNotManageStock - * @param IsSourceItemManagementAllowedForSkuInterface $isSourceItemManagementAllowedForSku - * @param GetStockIndexDump $getStockIndexDump - */ - public function __construct( - GetQtyForNotManageStock $getQtyForNotManageStock, - IsSourceItemManagementAllowedForSkuInterface $isSourceItemManagementAllowedForSku, - GetStockIndexDump $getStockIndexDump - ) { - $this->getQtyForNotManageStock = $getQtyForNotManageStock; - $this->isSourceItemManagementAllowedForSku = $isSourceItemManagementAllowedForSku; - $this->getStockIndexDump = $getStockIndexDump; - } - - /** - * Provides sku and qty of products dumping them from stock index table - * - * @param array $products - * @param int $stockId - * @return array - */ - public function execute(array $products, int $stockId): array - { - $qtyForNotManageStock = $this->getQtyForNotManageStock->execute(); - $productStockIndex = $this->getStockIndexDump->execute($products, $stockId); - $items = []; - foreach ($productStockIndex as $index) { - if ($this->isSourceItemManagementAllowedForSku->execute($index['sku'])) { - $qty = $index['qty'] ?: $qtyForNotManageStock; - } else { - $qty = null; - } - $items[] = [ - 'sku' => $index['sku'], - 'qty' => $qty - ]; - } - - return $items; - } -} diff --git a/InventoryExportStock/Model/ExportStockSalableQty.php b/InventoryExportStock/Model/ExportStockSalableQty.php index 387e227da4ff..e44012b16006 100644 --- a/InventoryExportStock/Model/ExportStockSalableQty.php +++ b/InventoryExportStock/Model/ExportStockSalableQty.php @@ -11,7 +11,6 @@ use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Framework\Api\SearchCriteriaInterface; use Magento\Framework\Api\SearchResultsInterface; -use Magento\InventoryExportStock\Model\ExportStockProcessor\PreciseExportStockProcessor; use Magento\InventoryExportStockApi\Api\Data\ExportStockSalableQtySearchResultInterface; use Magento\InventoryExportStockApi\Api\Data\ExportStockSalableQtySearchResultInterfaceFactory; use Magento\InventoryExportStockApi\Api\ExportStockSalableQtyInterface; diff --git a/InventoryExportStock/Model/ExportStockProcessor/PreciseExportStockProcessor.php b/InventoryExportStock/Model/PreciseExportStockProcessor.php similarity index 96% rename from InventoryExportStock/Model/ExportStockProcessor/PreciseExportStockProcessor.php rename to InventoryExportStock/Model/PreciseExportStockProcessor.php index 9f8b0bf5a97e..60109f91873c 100644 --- a/InventoryExportStock/Model/ExportStockProcessor/PreciseExportStockProcessor.php +++ b/InventoryExportStock/Model/PreciseExportStockProcessor.php @@ -5,14 +5,13 @@ */ declare(strict_types=1); -namespace Magento\InventoryExportStock\Model\ExportStockProcessor; +namespace Magento\InventoryExportStock\Model; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\LocalizedException; use Magento\InventoryConfigurationApi\Api\GetStockItemConfigurationInterface; use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForSkuInterface; -use Magento\InventoryExportStock\Model\GetQtyForNotManageStock; use Magento\InventorySalesApi\Api\GetProductSalableQtyInterface; /** diff --git a/InventoryExportStock/Model/ResourceModel/GetStockIndexDump.php b/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php similarity index 99% rename from InventoryExportStock/Model/ResourceModel/GetStockIndexDump.php rename to InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php index a9523b98ea36..e2a6ab852e65 100644 --- a/InventoryExportStock/Model/ResourceModel/GetStockIndexDump.php +++ b/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php @@ -19,7 +19,7 @@ /** * Class GetStockIndexDump provides sku and qty of products dumping them from stock index table */ -class GetStockIndexDump +class StockIndexDumpProcessor { /** * @var StockIndexTableNameResolverInterface @@ -59,7 +59,6 @@ public function __construct( ResourceConnection $resourceConnection, ManageStockCondition $manageStockCondition, GetQtyForNotManageStock $getQtyForNotManageStock - ) { $this->stockIndexTableNameResolver = $stockIndexTableNameResolver; $this->resourceConnection = $resourceConnection; @@ -96,6 +95,7 @@ private function getStockIndexSelect(int $stockId): Select { $stockIndexTableName = $this->resourceConnection ->getTableName($this->stockIndexTableNameResolver->execute($stockId)); + return $this->connection->select() ->from( $stockIndexTableName, From 12f542f177f2104a823afdfb6ec77619e058e7d6 Mon Sep 17 00:00:00 2001 From: Vadim Justus <v.justus@techdivision.com> Date: Sat, 13 Apr 2019 17:08:22 +0200 Subject: [PATCH 141/231] magento-engcom/msi#2170: Refactore inconsistency data collector logic --- ...ShowInconsistenciesForIncompleteOrders.php | 137 ----------------- .../ShowInconsistenciesInCompletedOrders.php | 139 ------------------ .../ShowSaleableQuantityInconsistencies.php | 139 ++++++++++++++++++ ...etOrdersWithMissingInitialReservations.php | 109 -------------- ...etOrdersWithNotCompensatedReservations.php | 79 ---------- .../GetSaleableQuantityInconsistencies.php | 123 +++++++--------- .../Model/SaleableQuantityInconsistency.php | 76 ++++++++++ .../AddCompletedOrdersToUnresolved.php | 52 +++++++ .../AddExistingReservations.php | 62 ++++++++ .../AddExpectedReservations.php | 43 ++++++ .../Collector.php | 74 ++++++++++ ...RemoveReservationsWithoutRelevantOrder.php | 30 ++++ .../RemoveResolvedReservations.php | 34 +++++ InventoryReservationCli/etc/di.xml | 7 +- 14 files changed, 568 insertions(+), 536 deletions(-) delete mode 100644 InventoryReservationCli/Command/ShowInconsistenciesForIncompleteOrders.php delete mode 100644 InventoryReservationCli/Command/ShowInconsistenciesInCompletedOrders.php create mode 100644 InventoryReservationCli/Command/ShowSaleableQuantityInconsistencies.php delete mode 100644 InventoryReservationCli/Model/GetOrdersWithMissingInitialReservations.php delete mode 100644 InventoryReservationCli/Model/GetOrdersWithNotCompensatedReservations.php create mode 100644 InventoryReservationCli/Model/SaleableQuantityInconsistency/AddCompletedOrdersToUnresolved.php create mode 100644 InventoryReservationCli/Model/SaleableQuantityInconsistency/AddExistingReservations.php create mode 100644 InventoryReservationCli/Model/SaleableQuantityInconsistency/AddExpectedReservations.php create mode 100644 InventoryReservationCli/Model/SaleableQuantityInconsistency/Collector.php create mode 100644 InventoryReservationCli/Model/SaleableQuantityInconsistency/RemoveReservationsWithoutRelevantOrder.php create mode 100644 InventoryReservationCli/Model/SaleableQuantityInconsistency/RemoveResolvedReservations.php diff --git a/InventoryReservationCli/Command/ShowInconsistenciesForIncompleteOrders.php b/InventoryReservationCli/Command/ShowInconsistenciesForIncompleteOrders.php deleted file mode 100644 index e42d4612aa73..000000000000 --- a/InventoryReservationCli/Command/ShowInconsistenciesForIncompleteOrders.php +++ /dev/null @@ -1,137 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryReservationCli\Command; - -use Magento\InventoryReservationCli\Model\GetOrdersInNotFinalState; -use Magento\InventoryReservationCli\Model\GetOrdersWithMissingInitialReservations; -use Magento\InventoryReservationCli\Model\GetOrdersWithNotCompensatedReservations; -use Magento\Sales\Model\Order; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; - -/** - * Outputs a list of incomplete orders, which have not a initial reservation. - * - * This command may be used to simplify migrations from Magento versions without new Inventory or to track down - * incorrect behavior of customizations. - */ -class ShowInconsistenciesForIncompleteOrders extends Command -{ - /** - * @var GetOrdersInNotFinalState - */ - private $getOrdersInNotFinalState; - - /** - * @var GetOrdersWithMissingInitialReservations - */ - private $getOrdersWithMissingInitialReservations; - - /** - * @param GetOrdersWithMissingInitialReservations $getOrdersWithMissingInitialReservations - * @param GetOrdersInNotFinalState $getOrdersInNotFinalState - */ - public function __construct( - GetOrdersWithMissingInitialReservations $getOrdersWithMissingInitialReservations, - GetOrdersInNotFinalState $getOrdersInNotFinalState - ) { - $this->getOrdersWithMissingInitialReservations = $getOrdersWithMissingInitialReservations; - $this->getOrdersInNotFinalState = $getOrdersInNotFinalState; - parent::__construct(); - } - - /** - * @inheritdoc - */ - protected function configure() - { - $this - ->setName('inventory:reservation:list-missing-reservation') - ->setDescription('Show all orders and products without initial reservation') - ->addOption('raw', 'r', InputOption::VALUE_NONE, 'Raw output'); - - parent::configure(); - } - - /** - * Format output - * - * @param OutputInterface $output - * @param array $inconsistentData - */ - private function prettyOutput(OutputInterface $output, array $inconsistentData): void - { - $output->writeln('<comment>Inconsistencies found on following entries:</comment>'); - - /** @var Order $order */ - foreach ($inconsistentData as $inconsistentOrder) { - $inconsistentSkus = $inconsistentOrder['skus']; - $incrementId = $inconsistentOrder['increment_id']; - - $output->writeln(sprintf('Order <comment>%s</comment>:', $incrementId)); - - foreach ($inconsistentSkus as $inconsistentSku => $qty) { - $output->writeln( - sprintf( - ' - Product <comment>%s</comment> should be compensated by <comment>%+f</comment>', - $inconsistentSku, - -$qty - ) - ); - } - } - } - - /** - * Output without formatting - * - * @param OutputInterface $output - * @param array $inconsistentData - */ - private function rawOutput(OutputInterface $output, array $inconsistentData): void - { - /** @var Order $order */ - foreach ($inconsistentData as $inconsistentOrder) { - $inconsistentSkus = $inconsistentOrder['skus']; - $incrementId = $inconsistentOrder['increment_id']; - - foreach ($inconsistentSkus as $inconsistentSku => $qty) { - $output->writeln( - sprintf('%s:%s:%f', $incrementId, $inconsistentSku, -$qty) - ); - } - } - } - - /** - * {@inheritdoc} - * - * @param InputInterface $input - * @param OutputInterface $output - * @return int - */ - public function execute(InputInterface $input, OutputInterface $output): int - { - $incompleteOrders = $this->getOrdersInNotFinalState->execute(); - $inconsistentData = $this->getOrdersWithMissingInitialReservations->execute($incompleteOrders); - - if (empty($inconsistentData)) { - $output->writeln('<info>No order inconsistencies were found</info>'); - return 0; - } - - if ($input->getOption('raw')) { - $this->rawOutput($output, $inconsistentData); - } else { - $this->prettyOutput($output, $inconsistentData); - } - return -1; - } -} diff --git a/InventoryReservationCli/Command/ShowInconsistenciesInCompletedOrders.php b/InventoryReservationCli/Command/ShowInconsistenciesInCompletedOrders.php deleted file mode 100644 index c639962f2bce..000000000000 --- a/InventoryReservationCli/Command/ShowInconsistenciesInCompletedOrders.php +++ /dev/null @@ -1,139 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryReservationCli\Command; - -use Magento\InventoryReservationCli\Model\GetOrdersInFinalState; -use Magento\InventoryReservationCli\Model\GetOrdersWithNotCompensatedReservations; -use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Model\Order; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; - -/** - * Outputs a list of uncompensated reservations linked to the orders in final state (Completed, Closed, Canceled). - * - * This command may be used to simplify migrations from Magento versions without new Inventory or to track down - * incorrect behavior of customizations. - */ -class ShowInconsistenciesInCompletedOrders extends Command -{ - /** - * @var GetOrdersInFinalState - */ - private $getOrderInFinalState; - /** - * @var GetOrdersWithNotCompensatedReservations - */ - private $getOrdersWithNotCompensatedReservations; - - /** - * @param GetOrdersWithNotCompensatedReservations $getOrdersWithNotCompensatedReservations - * @param GetOrdersInFinalState $getOrderInFinalState - */ - public function __construct( - GetOrdersWithNotCompensatedReservations $getOrdersWithNotCompensatedReservations, - GetOrdersInFinalState $getOrderInFinalState - ) { - $this->getOrdersWithNotCompensatedReservations = $getOrdersWithNotCompensatedReservations; - $this->getOrderInFinalState = $getOrderInFinalState; - parent::__construct(); - } - - /** - * @inheritdoc - */ - protected function configure() - { - $this - ->setName('inventory:reservation:list-not-compensated') - ->setDescription('Show all orders and products without reservation compensation') - ->addOption('raw', 'r', InputOption::VALUE_NONE, 'Raw output'); - - parent::configure(); - } - - /** - * Format output - * - * @param OutputInterface $output - * @param array $itemsNotCompensated - * @param array $orders - */ - private function prettyOutput(OutputInterface $output, array $itemsNotCompensated, array $orders): void - { - $output->writeln('<comment>Inconsistencies found on following entries:</comment>'); - - /** @var Order $order */ - foreach($orders as $order) { - $inconsistentSkus = $itemsNotCompensated[$order->getId()]; - - $output->writeln(sprintf('Order <comment>%s</comment>:', $order->getIncrementId())); - - foreach ($inconsistentSkus as $inconsistentSku => $qty) { - $output->writeln( - sprintf( - ' - Product <comment>%s</comment> should be compensated by <comment>%+f</comment>', - $inconsistentSku, - -$qty - ) - ); - } - } - } - - /** - * Output without formatting - * - * @param OutputInterface $output - * @param array $itemsNotCompensated - * @param array $orders - */ - private function rawOutput(OutputInterface $output, array $itemsNotCompensated, array $orders): void - { - /** @var Order $order */ - foreach($orders as $order) { - $inconsistentSkus = $itemsNotCompensated[$order->getId()]; - - foreach ($inconsistentSkus as $inconsistentSku => $qty) { - $output->writeln( - sprintf('%s:%s:%f', $order->getIncrementId(), $inconsistentSku, -$qty) - ); - } - } - } - - /** - * {@inheritdoc} - * - * @param InputInterface $input - * @param OutputInterface $output - * @return int - */ - public function execute(InputInterface $input, OutputInterface $output): int - { - /** @var array $itemsNotCompensated */ - $itemsNotCompensated = $this->getOrdersWithNotCompensatedReservations->execute(); - - /** @var OrderInterface[] $orders */ - $orders = $this->getOrderInFinalState->execute(array_keys($itemsNotCompensated)); - - if (empty($orders)) { - $output->writeln('<info>No order inconsistencies were found</info>'); - return 0; - } - - if ($input->getOption('raw')) { - $this->rawOutput($output, $itemsNotCompensated, $orders); - } else { - $this->prettyOutput($output, $itemsNotCompensated, $orders); - } - return -1; - } -} diff --git a/InventoryReservationCli/Command/ShowSaleableQuantityInconsistencies.php b/InventoryReservationCli/Command/ShowSaleableQuantityInconsistencies.php new file mode 100644 index 000000000000..b9a00193c912 --- /dev/null +++ b/InventoryReservationCli/Command/ShowSaleableQuantityInconsistencies.php @@ -0,0 +1,139 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservationCli\Command; + +use Magento\InventoryReservationCli\Model\GetSaleableQuantityInconsistencies; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; +use Magento\Sales\Model\Order; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Outputs a list of uncompensated reservations linked to the orders in final state (Completed, Closed, Canceled). + * + * This command may be used to simplify migrations from Magento versions without new Inventory or to track down + * incorrect behavior of customizations. + */ +class ShowSaleableQuantityInconsistencies extends Command +{ + /** + * @var GetSaleableQuantityInconsistencies + */ + private $getSaleableQuantityInconsistencies; + + /** + * @param GetSaleableQuantityInconsistencies $getSaleableQuantityInconsistencies + */ + public function __construct( + GetSaleableQuantityInconsistencies $getSaleableQuantityInconsistencies + ) { + parent::__construct(); + $this->getSaleableQuantityInconsistencies = $getSaleableQuantityInconsistencies; + } + + /** + * @inheritdoc + */ + protected function configure() + { + $this + ->setName('inventory:reservation:list-inconsistencies') + ->setDescription('Show all orders and products with saleable quantity inconsistencies') + ->addOption( + 'filter', + 'f', + InputOption::VALUE_REQUIRED, + 'Filter for complete or incomplete orders' + ) + ->addOption( + 'raw', + 'r', + InputOption::VALUE_NONE, + 'Raw output' + ); + + parent::configure(); + } + + /** + * Format output + * + * @param OutputInterface $output + * @param SaleableQuantityInconsistency[] $inconsistencies + */ + private function prettyOutput(OutputInterface $output, array $inconsistencies): void + { + $output->writeln('<comment>Inconsistencies found on following entries:</comment>'); + + /** @var Order $order */ + foreach ($inconsistencies as $inconsistency) { + $inconsistentItems = $inconsistency->getItems(); + + $output->writeln(sprintf( + 'Order <comment>%s</comment>:', + $inconsistency->getOrder()->getIncrementId() + )); + + foreach ($inconsistentItems as $sku => $qty) { + $output->writeln( + sprintf( + ' - Product <comment>%s</comment> should be compensated by <comment>%+f</comment>', + $sku, + -$qty + ) + ); + } + } + } + + /** + * Output without formatting + * + * @param OutputInterface $output + * @param SaleableQuantityInconsistency[] $inconsistencies + */ + private function rawOutput(OutputInterface $output, array $inconsistencies): void + { + /** @var Order $order */ + foreach ($inconsistencies as $inconsistency) { + $inconsistentItems = $inconsistency->getItems(); + + foreach ($inconsistentItems as $sku => $qty) { + $output->writeln( + sprintf('%s:%s:%f', $inconsistency->getOrder()->getIncrementId(), $sku, -$qty) + ); + } + } + } + + /** + * {@inheritdoc} + * + * @param InputInterface $input + * @param OutputInterface $output + * @return int + */ + public function execute(InputInterface $input, OutputInterface $output): int + { + $inconsistencies = $this->getSaleableQuantityInconsistencies->execute(); + + if (empty($inconsistencies)) { + $output->writeln('<info>No order inconsistencies were found</info>'); + return 0; + } + + if ($input->getOption('raw')) { + $this->rawOutput($output, $inconsistencies); + } else { + $this->prettyOutput($output, $inconsistencies); + } + return -1; + } +} diff --git a/InventoryReservationCli/Model/GetOrdersWithMissingInitialReservations.php b/InventoryReservationCli/Model/GetOrdersWithMissingInitialReservations.php deleted file mode 100644 index 96d81dd4b358..000000000000 --- a/InventoryReservationCli/Model/GetOrdersWithMissingInitialReservations.php +++ /dev/null @@ -1,109 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryReservationCli\Model; - -use Magento\Framework\Serialize\SerializerInterface; -use Magento\InventoryReservationCli\Model\ResourceModel\GetReservationsList; -use Magento\Sales\Api\Data\OrderInterface; - -/** - * Filter orders for missing initial reservation - */ -class GetOrdersWithMissingInitialReservations -{ - /** - * @var GetReservationsList - */ - private $getReservationsList; - - /** - * @var SerializerInterface - */ - private $serialize; - - /** - * @param GetReservationsList $getReservationsList - * @param SerializerInterface $serialize - */ - public function __construct( - GetReservationsList $getReservationsList, - SerializerInterface $serialize - ) { - $this->getReservationsList = $getReservationsList; - $this->serialize = $serialize; - } - - /** - * Get list of reservations for Order entity. - * - * @param OrderInterface[] $orders - * @return array - */ - public function execute(array $orders): array - { - $entityIdAndSkuList = $this->getEntityIdAndSkuList($orders); - - $reservationList = $this->getReservationsList->execute(); - foreach ($reservationList as $reservation) { - $metadata = $this->serialize->unserialize($reservation['metadata']); - $objectId = $metadata['object_id']; - $eventType = $metadata['event_type']; - - if ($eventType === 'order_placed' && in_array($objectId, array_keys($entityIdAndSkuList))) { - $entityIdAndSkuList = $this->filterReservedSkus((int)$objectId, $entityIdAndSkuList, $reservation); - } - } - - $entityIdAndSkuList = array_filter($entityIdAndSkuList, function ($order) { - return !empty($order['skus']); - }); - - return $entityIdAndSkuList; - } - - /** - * @param int $objectId - * @param array $entityIdAndSkuList - * @param array $reservation - * @return array - */ - private function filterReservedSkus(int $objectId, array $entityIdAndSkuList, array $reservation): array - { - $reservedSku = $reservation['sku']; - if (!in_array($reservedSku, array_keys($entityIdAndSkuList[$objectId]['skus']))) { - return $entityIdAndSkuList; - } - - $reservedQuantity = $reservation['quantity']; - $entityIdAndSkuList[$objectId]['skus'][$reservedSku] += (float)$reservedQuantity; - - $entityIdAndSkuList[$objectId]['skus'] = array_filter($entityIdAndSkuList[$objectId]['skus']); - - return $entityIdAndSkuList; - } - - /** - * @param OrderInterface[] $orders - * @return array - */ - private function getEntityIdAndSkuList(array $orders): array - { - $list = []; - foreach ($orders as $order) { - $entityId = $order->getEntityId(); - $list[$entityId] = [ - 'increment_id' => $order->getIncrementId(), - 'skus' => [] - ]; - foreach ($order->getItems() as $item) { - $list[$entityId]['skus'][$item->getSku()] = (float)$item->getQtyOrdered(); - } - } - return $list; - } -} diff --git a/InventoryReservationCli/Model/GetOrdersWithNotCompensatedReservations.php b/InventoryReservationCli/Model/GetOrdersWithNotCompensatedReservations.php deleted file mode 100644 index 816f953b6b29..000000000000 --- a/InventoryReservationCli/Model/GetOrdersWithNotCompensatedReservations.php +++ /dev/null @@ -1,79 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryReservationCli\Model; - -use Magento\Framework\Serialize\SerializerInterface; -use Magento\InventoryReservationCli\Model\ResourceModel\GetReservationsList; - -/** - * Get list of reservations for Order entity. - */ -class GetOrdersWithNotCompensatedReservations -{ - /** - * @var GetReservationsList - */ - private $getReservationsList; - - /** - * @var SerializerInterface - */ - private $serialize; - - /** - * @param GetReservationsList $getReservationsList - * @param SerializerInterface $serialize - */ - public function __construct( - GetReservationsList $getReservationsList, - SerializerInterface $serialize - ) { - $this->getReservationsList = $getReservationsList; - $this->serialize = $serialize; - } - - /** - * Get list of reservations for Order entity. - * - * @return array - */ - public function execute(): array - { - /** @var array $reservationList */ - $reservationList = $this->getReservationsList->execute(); - - /** @var array $result */ - $result = []; - foreach ($reservationList as $reservation) { - /** @var array $metadata */ - $metadata = $this->serialize->unserialize($reservation['metadata']); - $objectId = $metadata['object_id']; - $sku = $reservation['sku']; - $orderType = $metadata['object_type']; - - if ($orderType !== 'order') { - continue; - } - - if (!isset($result[$objectId])) { - $result[$objectId] = []; - } - if (!isset($result[$objectId][$sku])) { - $result[$objectId][$sku] = 0.0; - } - - $result[$objectId][$sku] += (float) $reservation['quantity']; - } - - foreach ($result as &$entry) { - $entry = array_filter($entry); - } - - return array_filter($result); - } -} diff --git a/InventoryReservationCli/Model/GetSaleableQuantityInconsistencies.php b/InventoryReservationCli/Model/GetSaleableQuantityInconsistencies.php index 7ab303d70d40..602cbd7a3e41 100644 --- a/InventoryReservationCli/Model/GetSaleableQuantityInconsistencies.php +++ b/InventoryReservationCli/Model/GetSaleableQuantityInconsistencies.php @@ -7,8 +7,13 @@ namespace Magento\InventoryReservationCli\Model; -use Magento\Framework\Serialize\SerializerInterface; -use Magento\InventoryReservationCli\Model\ResourceModel\GetReservationsList; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\AddCompletedOrdersToUnresolved; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\AddExistingReservations; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\AddExpectedReservations; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\Collector; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\CollectorFactory; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\RemoveReservationsWithoutRelevantOrder; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\RemoveResolvedReservations; /** * Filter orders for missing initial reservation @@ -16,88 +21,72 @@ class GetSaleableQuantityInconsistencies { /** - * @var GetReservationsList + * @var CollectorFactory */ - private $getReservationsList; + private $collectorFactory; /** - * @var SerializerInterface + * @var AddExpectedReservations */ - private $serialize; + private $addExpectedReservations; /** - * @var GetOrdersInNotFinalState + * @var AddExistingReservations */ - private $getOrdersInNotFinalState; + private $addExistingReservations; /** - * @param GetReservationsList $getReservationsList - * @param SerializerInterface $serialize - * @param GetOrdersInNotFinalState $getOrdersInNotFinalState + * @var RemoveResolvedReservations */ - public function __construct( - GetReservationsList $getReservationsList, - SerializerInterface $serialize, - GetOrdersInNotFinalState $getOrdersInNotFinalState - ) { - $this->getReservationsList = $getReservationsList; - $this->serialize = $serialize; - $this->getOrdersInNotFinalState = $getOrdersInNotFinalState; - } + private $removeResolvedReservations; /** - * @return array + * @var AddCompletedOrdersToUnresolved */ - public function execute(): array - { - $reservationList = $this->getReservationsList->execute(); - $expectedReservations = $this->getExpectedReservations(); + private $addCompletedOrdersToUnresolved; - $result = []; - foreach ($reservationList as $reservation) { - /** @var array $metadata */ - $metadata = $this->serialize->unserialize($reservation['metadata']); - $objectId = $metadata['object_id']; - $sku = $reservation['sku']; - $orderType = $metadata['object_type']; - - if ($orderType !== 'order') { - continue; - } - - if (!isset($result[$objectId])) { - $result[$objectId] = []; - } - if (!isset($result[$objectId][$sku])) { - $result[$objectId][$sku] = 0.0; - } - - $result[$objectId][$sku] += (float) $reservation['quantity']; - } - - foreach ($result as &$entry) { - $entry = array_filter($entry); - } + /** + * @var RemoveReservationsWithoutRelevantOrder + */ + private $removeReservationsWithoutRelevantOrder; - return array_filter($result); + /** + * @param CollectorFactory $collectorFactory + * @param AddExpectedReservations $addExpectedReservations + * @param AddExistingReservations $addExistingReservations + * @param RemoveResolvedReservations $removeResolvedReservations + * @param AddCompletedOrdersToUnresolved $addCompletedOrdersToUnresolved + * @param RemoveReservationsWithoutRelevantOrder $removeReservationsWithoutRelevantOrder + */ + public function __construct( + CollectorFactory $collectorFactory, + AddExpectedReservations $addExpectedReservations, + AddExistingReservations $addExistingReservations, + RemoveResolvedReservations $removeResolvedReservations, + AddCompletedOrdersToUnresolved $addCompletedOrdersToUnresolved, + RemoveReservationsWithoutRelevantOrder $removeReservationsWithoutRelevantOrder + ) { + $this->collectorFactory = $collectorFactory; + $this->addExpectedReservations = $addExpectedReservations; + $this->addExistingReservations = $addExistingReservations; + $this->removeResolvedReservations = $removeResolvedReservations; + $this->addCompletedOrdersToUnresolved = $addCompletedOrdersToUnresolved; + $this->removeReservationsWithoutRelevantOrder = $removeReservationsWithoutRelevantOrder; } - private function getExpectedReservations() + /** + * Filter orders for missing initial reservation + * @return SaleableQuantityInconsistency[] + */ + public function execute(): array { - $incompleteOrders = $this->getOrdersInNotFinalState->execute(); - - $result = []; - foreach ($incompleteOrders as $order) { - $entityId = $order->getEntityId(); - $list[$entityId] = [ - 'increment_id' => $order->getIncrementId(), - 'skus' => [] - ]; - foreach ($order->getItems() as $item) { - $list[$entityId]['skus'][$item->getSku()] = (float)$item->getQtyOrdered(); - } - } - - return $list; + /** @var Collector $collector */ + $collector = $this->collectorFactory->create(); + $this->addExpectedReservations->execute($collector); + $this->addExistingReservations->execute($collector); + $this->removeResolvedReservations->execute($collector); + $this->addCompletedOrdersToUnresolved->execute($collector); + $this->removeReservationsWithoutRelevantOrder->execute($collector); + return $collector->getInconsistencies(); } } diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency.php b/InventoryReservationCli/Model/SaleableQuantityInconsistency.php index 7db779f9f772..25464ea3a195 100644 --- a/InventoryReservationCli/Model/SaleableQuantityInconsistency.php +++ b/InventoryReservationCli/Model/SaleableQuantityInconsistency.php @@ -7,10 +7,86 @@ namespace Magento\InventoryReservationCli\Model; +use Magento\Sales\Api\Data\OrderInterface; + /** * Filter orders for missing initial reservation */ class SaleableQuantityInconsistency { + /** + * @var OrderInterface + */ + private $order; + + /**+ + * @var int + */ + private $objectId; + + /** + * List of SKUs and quantity + * @var array + */ + private $items = []; + + /** + * @return OrderInterface|null + */ + public function getOrder(): ?OrderInterface + { + return $this->order; + } + + /** + * @param OrderInterface $order + */ + public function setOrder(OrderInterface $order): void + { + $this->order = $order; + } + + /** + * @return int + */ + public function getObjectId(): int + { + return $this->objectId; + } + + /** + * @param int $objectId + */ + public function setObjectId(int $objectId): void + { + $this->objectId = $objectId; + } + + /** + * @param string $sku + * @param float $qty + */ + public function addItemQty(string $sku, float $qty): void + { + if (!isset($this->items[$sku])) { + $this->items[$sku] = 0.0; + } + $this->items[$sku] += $qty; + } + + /** + * @return array + */ + public function getItems(): array + { + return $this->items; + } + /** + * @param array $items + */ + public function setItems(array $items): void + { + $this->items = $items; + } } diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddCompletedOrdersToUnresolved.php b/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddCompletedOrdersToUnresolved.php new file mode 100644 index 000000000000..d11de0008433 --- /dev/null +++ b/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddCompletedOrdersToUnresolved.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; + +use Magento\InventoryReservationCli\Model\GetOrdersInFinalState; + +/** + * Match completed orders with unresolved reservations + */ +class AddCompletedOrdersToUnresolved +{ + /** + * @var GetOrdersInFinalState + */ + private $getOrdersInFinalState; + + /** + * @param GetOrdersInFinalState $getOrdersInFinalState + */ + public function __construct( + GetOrdersInFinalState $getOrdersInFinalState + ) { + $this->getOrdersInFinalState = $getOrdersInFinalState; + } + + /** + * Remove all entries without order + * @param Collector $collector + */ + public function execute(Collector $collector): void + { + $inconsistencies = $collector->getInconsistencies(); + + $orderIds = []; + foreach ($inconsistencies as $inconsistency) { + $orderIds[] = $inconsistency->getObjectId(); + } + + foreach ($this->getOrdersInFinalState->execute($orderIds) as $order) { + if (isset($inconsistencies[$order->getEntityId()])) { + $inconsistencies[$order->getEntityId()]->setOrder($order); + } + } + + $collector->setInconsistencies($inconsistencies); + } +} diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddExistingReservations.php b/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddExistingReservations.php new file mode 100644 index 000000000000..38d113dd9456 --- /dev/null +++ b/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddExistingReservations.php @@ -0,0 +1,62 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; + +use Magento\Framework\Serialize\SerializerInterface; +use Magento\InventoryReservationCli\Model\ResourceModel\GetReservationsList; + +/** + * Add existing reservations + */ +class AddExistingReservations +{ + /** + * @var GetReservationsList + */ + private $getReservationsList; + + /** + * @var SerializerInterface + */ + private $serializer; + + /** + * @param GetReservationsList $getReservationsList + * @param SerializerInterface $serializer + */ + public function __construct( + GetReservationsList $getReservationsList, + SerializerInterface $serializer + ) { + $this->getReservationsList = $getReservationsList; + $this->serializer = $serializer; + } + + /** + * Add existing reservations + * @param Collector $collector + */ + public function execute(Collector $collector): void + { + $reservationList = $this->getReservationsList->execute(); + foreach ($reservationList as $reservation) { + /** @var array $metadata */ + $metadata = $this->serializer->unserialize($reservation['metadata']); + $objectId = (int)$metadata['object_id']; + $sku = $reservation['sku']; + $quantity = (float)$reservation['quantity']; + $orderType = $metadata['object_type']; + + if ($orderType !== 'order') { + continue; + } + + $collector->add($objectId, $sku, $quantity); + } + } +} diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddExpectedReservations.php b/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddExpectedReservations.php new file mode 100644 index 000000000000..69966b9bf4f8 --- /dev/null +++ b/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddExpectedReservations.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; + +use Magento\InventoryReservationCli\Model\GetOrdersInNotFinalState; + +/** + * Add expected reservations by current incomplete orders + */ +class AddExpectedReservations +{ + /** + * @var GetOrdersInNotFinalState + */ + private $getOrdersInNotFinalState; + + /** + * @param GetOrdersInNotFinalState $getOrdersInNotFinalState + */ + public function __construct( + GetOrdersInNotFinalState $getOrdersInNotFinalState + ) { + $this->getOrdersInNotFinalState = $getOrdersInNotFinalState; + } + + /** + * Add expected reservations by current incomplete orders + * @param Collector $collector + */ + public function execute(Collector $collector): void + { + foreach ($this->getOrdersInNotFinalState->execute() as $order) { + foreach ($order->getItems() as $item) { + $collector->add((int)$order->getEntityId(), $item->getSku(), (float)$item->getQtyOrdered(), $order); + } + } + } +} diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency/Collector.php b/InventoryReservationCli/Model/SaleableQuantityInconsistency/Collector.php new file mode 100644 index 000000000000..a5c9d181b9fb --- /dev/null +++ b/InventoryReservationCli/Model/SaleableQuantityInconsistency/Collector.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; + +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistencyFactory; +use Magento\Sales\Api\Data\OrderInterface; + +/** + * Collects all existing and missing reservations in order to calculate inconsistency + */ +class Collector +{ + /** + * @var SaleableQuantityInconsistency[] + */ + private $inconsistencies = []; + + /** + * @var \Magento\InventoryReservationCli\Model\SaleableQuantityInconsistencyFactory + */ + private $saleableQuantityInconsistencyFactory; + + /** + * @param SaleableQuantityInconsistencyFactory $saleableQuantityInconsistencyFactory + */ + public function __construct( + SaleableQuantityInconsistencyFactory $saleableQuantityInconsistencyFactory + ) { + $this->saleableQuantityInconsistencyFactory = $saleableQuantityInconsistencyFactory; + } + + /** + * @param int $objectId + * @param string $sku + * @param float $quantity + * @param OrderInterface|null $order + */ + public function add(int $objectId, string $sku, float $quantity, ?OrderInterface $order = null): void + { + if (!isset($this->inconsistencies[$objectId])) { + $this->inconsistencies[$objectId] = $this->saleableQuantityInconsistencyFactory->create(); + } + + $this->inconsistencies[$objectId]->setObjectId($objectId); + + if ($order) { + $this->inconsistencies[$objectId]->setOrder($order); + } + + $this->inconsistencies[$objectId]->addItemQty($sku, $quantity); + } + + /** + * @return SaleableQuantityInconsistency[] + */ + public function getInconsistencies(): array + { + return $this->inconsistencies; + } + + /** + * @param SaleableQuantityInconsistency[] $inconsistencies + */ + public function setInconsistencies(array $inconsistencies) + { + $this->inconsistencies = $inconsistencies; + } +} diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency/RemoveReservationsWithoutRelevantOrder.php b/InventoryReservationCli/Model/SaleableQuantityInconsistency/RemoveReservationsWithoutRelevantOrder.php new file mode 100644 index 000000000000..a116231c8068 --- /dev/null +++ b/InventoryReservationCli/Model/SaleableQuantityInconsistency/RemoveReservationsWithoutRelevantOrder.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; + +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; + +/** + * Remove all entries without order + */ +class RemoveReservationsWithoutRelevantOrder +{ + /** + * Remove all entries without order + * @param Collector $collector + */ + public function execute(Collector $collector): void + { + $collector->setInconsistencies(array_filter( + $collector->getInconsistencies(), + function (SaleableQuantityInconsistency $inconsistency) { + return (bool)$inconsistency->getOrder(); + } + )); + } +} diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency/RemoveResolvedReservations.php b/InventoryReservationCli/Model/SaleableQuantityInconsistency/RemoveResolvedReservations.php new file mode 100644 index 000000000000..3e361b796e31 --- /dev/null +++ b/InventoryReservationCli/Model/SaleableQuantityInconsistency/RemoveResolvedReservations.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; + +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; + +/** + * Remove all compensated reservations + */ +class RemoveResolvedReservations +{ + /** + * Remove all compensated reservations + * @param Collector $collector + */ + public function execute(Collector $collector): void + { + foreach ($collector->getInconsistencies() as $inconsistency) { + $inconsistency->setItems(array_filter($inconsistency->getItems())); + } + + $collector->setInconsistencies(array_filter( + $collector->getInconsistencies(), + function (SaleableQuantityInconsistency $inconsistency) { + return count($inconsistency->getItems()) > 0; + } + )); + } +} diff --git a/InventoryReservationCli/etc/di.xml b/InventoryReservationCli/etc/di.xml index ce4432ded0eb..3521731e511a 100644 --- a/InventoryReservationCli/etc/di.xml +++ b/InventoryReservationCli/etc/di.xml @@ -9,11 +9,8 @@ <type name="Magento\Framework\Console\CommandList"> <arguments> <argument name="commands" xsi:type="array"> - <item name="inventory_complete_order_inconsistency" xsi:type="object"> - Magento\InventoryReservationCli\Command\ShowInconsistenciesInCompletedOrders - </item> - <item name="inventory_incomplete_order_inconsistency" xsi:type="object"> - Magento\InventoryReservationCli\Command\ShowInconsistenciesForIncompleteOrders + <item name="inventory_saleable_quantity_inconsistency" xsi:type="object"> + Magento\InventoryReservationCli\Command\ShowSaleableQuantityInconsistencies </item> </argument> </arguments> From d7c6ed552ad3de87adcd2efe739620867113fae6 Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Mon, 15 Apr 2019 15:01:17 +0300 Subject: [PATCH 142/231] MSI-2062: Inventory Export Stock: replaced parameter stockId with websiteCode, fixed SkuIsNotAssignedToStockException issue --- .../Model/ExportStockSalableQty.php | 18 ++++++++-- .../Model/PreciseExportStockProcessor.php | 33 +++++++++++++++---- InventoryExportStock/etc/module.xml | 3 +- .../Api/ExportStockSalableQtyInterface.php | 4 +-- 4 files changed, 46 insertions(+), 12 deletions(-) diff --git a/InventoryExportStock/Model/ExportStockSalableQty.php b/InventoryExportStock/Model/ExportStockSalableQty.php index e44012b16006..844af781dc5c 100644 --- a/InventoryExportStock/Model/ExportStockSalableQty.php +++ b/InventoryExportStock/Model/ExportStockSalableQty.php @@ -14,6 +14,8 @@ use Magento\InventoryExportStockApi\Api\Data\ExportStockSalableQtySearchResultInterface; use Magento\InventoryExportStockApi\Api\Data\ExportStockSalableQtySearchResultInterfaceFactory; use Magento\InventoryExportStockApi\Api\ExportStockSalableQtyInterface; +use Magento\InventorySalesApi\Api\Data\SalesChannelInterface; +use Magento\InventorySalesApi\Api\StockResolverInterface; /** * Class ExportStockSalableQty provides product stock information by search criteria @@ -34,6 +36,10 @@ class ExportStockSalableQty implements ExportStockSalableQtyInterface * @var PreciseExportStockProcessor */ private $preciseExportStockProcessor; + /** + * @var StockResolverInterface + */ + private $stockResolver; /** * ExportStockSalableQty constructor @@ -41,15 +47,18 @@ class ExportStockSalableQty implements ExportStockSalableQtyInterface * @param ProductRepositoryInterface $productRepository * @param ExportStockSalableQtySearchResultInterfaceFactory $exportStockSalableQtySearchResultFactory * @param PreciseExportStockProcessor $preciseExportStockProcessor + * @param StockResolverInterface $stockResolver */ public function __construct( ProductRepositoryInterface $productRepository, ExportStockSalableQtySearchResultInterfaceFactory $exportStockSalableQtySearchResultFactory, - PreciseExportStockProcessor $preciseExportStockProcessor + PreciseExportStockProcessor $preciseExportStockProcessor, + StockResolverInterface $stockResolver ) { $this->productRepository = $productRepository; $this->exportStockSalableQtySearchResultFactory = $exportStockSalableQtySearchResultFactory; $this->preciseExportStockProcessor = $preciseExportStockProcessor; + $this->stockResolver = $stockResolver; } /** @@ -59,10 +68,13 @@ public function __construct( */ public function execute( SearchCriteriaInterface $searchCriteria, - int $stockId + string $websiteCode ): ExportStockSalableQtySearchResultInterface { + $stockId = $this->stockResolver + ->execute(SalesChannelInterface::TYPE_WEBSITE, $websiteCode)->getStockId(); $productSearchResult = $this->getProducts($searchCriteria); - $items = $this->preciseExportStockProcessor->execute($productSearchResult->getItems(), $stockId); + $items = $this->preciseExportStockProcessor + ->execute($productSearchResult->getItems(), $stockId); /** @var ExportStockSalableQtySearchResultInterface $searchResult */ $searchResult = $this->exportStockSalableQtySearchResultFactory->create(); $searchResult->setSearchCriteria($productSearchResult->getSearchCriteria()); diff --git a/InventoryExportStock/Model/PreciseExportStockProcessor.php b/InventoryExportStock/Model/PreciseExportStockProcessor.php index 60109f91873c..d04e154a83f2 100644 --- a/InventoryExportStock/Model/PreciseExportStockProcessor.php +++ b/InventoryExportStock/Model/PreciseExportStockProcessor.php @@ -11,6 +11,7 @@ use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\LocalizedException; use Magento\InventoryConfigurationApi\Api\GetStockItemConfigurationInterface; +use Magento\InventoryConfigurationApi\Exception\SkuIsNotAssignedToStockException; use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForSkuInterface; use Magento\InventorySalesApi\Api\GetProductSalableQtyInterface; @@ -73,12 +74,10 @@ public function execute(array $products, int $stockId): array $skus = $this->getProductSkus($products); $items = []; foreach ($skus as $sku) { - if (!$this->getStockItemConfiguration->execute($sku, $stockId)->isManageStock()) { - $qty = $this->getQtyForNotManageStock->execute(); - } elseif (!$this->isSourceItemManagementAllowedForSku->execute($sku)) { - $qty = null; - } else { - $qty = $this->getProductSalableQty->execute($sku, $stockId); + try { + $qty = $this->getProductSalableQtyByStock($sku, $stockId); + } catch (SkuIsNotAssignedToStockException $e) { + $qty = 0.00; } $items[] = [ @@ -106,4 +105,26 @@ private function getProductSkus(array $products): array return $skus; } + + /** + * Provides qty by stock and sku + * + * @param string $sku + * @param int $stockId + * @return float + * @throws InputException + * @throws LocalizedException + * @throws SkuIsNotAssignedToStockException + */ + private function getProductSalableQtyByStock(string $sku, int $stockId): ?float + { + if (!$this->getStockItemConfiguration->execute($sku, $stockId)->isManageStock()) { + return (float)$this->getQtyForNotManageStock->execute(); + } + if (!$this->isSourceItemManagementAllowedForSku->execute($sku)) { + return null; + } + + return $this->getProductSalableQty->execute($sku, $stockId); + } } diff --git a/InventoryExportStock/etc/module.xml b/InventoryExportStock/etc/module.xml index 75bdd2734ca5..ab4e81bdcef1 100644 --- a/InventoryExportStock/etc/module.xml +++ b/InventoryExportStock/etc/module.xml @@ -5,7 +5,8 @@ * See COPYING.txt for license details. */ --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="Magento_InventoryExportStock" setup_version="1.0.0"> <sequence> <module name="Magento_Catalog"/> diff --git a/InventoryExportStockApi/Api/ExportStockSalableQtyInterface.php b/InventoryExportStockApi/Api/ExportStockSalableQtyInterface.php index 6c4dccbf001b..b0b2b3d55ff3 100644 --- a/InventoryExportStockApi/Api/ExportStockSalableQtyInterface.php +++ b/InventoryExportStockApi/Api/ExportStockSalableQtyInterface.php @@ -17,11 +17,11 @@ interface ExportStockSalableQtyInterface * Provides stock export data * * @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria - * @param int $stockId + * @param string $websiteCode * @return \Magento\InventoryExportStockApi\Api\Data\ExportStockSalableQtySearchResultInterface */ public function execute( \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria, - int $stockId + string $websiteCode ): \Magento\InventoryExportStockApi\Api\Data\ExportStockSalableQtySearchResultInterface; } From 9e08d4c4ac2bb761df5545e4affc32cfb46a0ec8 Mon Sep 17 00:00:00 2001 From: Bettina Cerban <bettinacerban@gmail.com> Date: Mon, 15 Apr 2019 10:13:30 -0300 Subject: [PATCH 143/231] fix wrong license header --- .../Model/ResourceModel/TransferInventoryPartially.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/InventoryCatalog/Model/ResourceModel/TransferInventoryPartially.php b/InventoryCatalog/Model/ResourceModel/TransferInventoryPartially.php index 05d565bd9c53..de7582f593d7 100644 --- a/InventoryCatalog/Model/ResourceModel/TransferInventoryPartially.php +++ b/InventoryCatalog/Model/ResourceModel/TransferInventoryPartially.php @@ -1,11 +1,8 @@ <?php /** - * - * Copyright © 2019 ebizmarts. All rights reserved. - * See LICENSE.txt for license details. - * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. */ - namespace Magento\InventoryCatalog\Model\ResourceModel; use Magento\Framework\App\ResourceConnection; From c74de3a1b6dbf41bc3c2d5adf9f9c7cbbba54e12 Mon Sep 17 00:00:00 2001 From: Sergey Mutaf <seruymt@gmail.com> Date: Mon, 15 Apr 2019 16:17:28 +0300 Subject: [PATCH 144/231] MSI-1947:Source items deduction during Credit memo creation for unshipped order items --- .../DeductSourceItemQuantityOnRefund.php | 122 ++++++++++++++++ ...rceDeductionRequestFromSourceSelection.php | 133 +++++++++++++++++ ...urceSelectionResultFromCreditMemoItems.php | 122 ++++++++++++++++ ...ductSourceItemQuantityOnRefundObserver.php | 135 ++++++++++++++++++ InventorySales/composer.json | 1 + InventorySales/etc/events.xml | 3 + 6 files changed, 516 insertions(+) create mode 100644 InventorySales/Model/ReturnProcessor/DeductSourceItemQuantityOnRefund.php create mode 100644 InventorySales/Model/ReturnProcessor/GetSourceDeductionRequestFromSourceSelection.php create mode 100644 InventorySales/Model/ReturnProcessor/GetSourceSelectionResultFromCreditMemoItems.php create mode 100644 InventorySales/Observer/SalesInventory/DeductSourceItemQuantityOnRefundObserver.php diff --git a/InventorySales/Model/ReturnProcessor/DeductSourceItemQuantityOnRefund.php b/InventorySales/Model/ReturnProcessor/DeductSourceItemQuantityOnRefund.php new file mode 100644 index 000000000000..e261ed525fba --- /dev/null +++ b/InventorySales/Model/ReturnProcessor/DeductSourceItemQuantityOnRefund.php @@ -0,0 +1,122 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\InventorySales\Model\ReturnProcessor; + +use Magento\InventorySalesApi\Api\Data\ItemToSellInterfaceFactory; +use Magento\InventorySalesApi\Api\PlaceReservationsForSalesEventInterface; +use Magento\InventorySalesApi\Model\ReturnProcessor\Request\ItemsToRefundInterface; +use Magento\InventorySourceDeductionApi\Model\SourceDeductionRequestInterface; +use Magento\InventorySourceDeductionApi\Model\SourceDeductionServiceInterface; +use Magento\InventorySourceSelectionApi\Api\SourceSelectionServiceInterface; +use Magento\Sales\Api\Data\OrderInterface; + +class DeductSourceItemQuantityOnRefund +{ + /** + * @var GetSourceSelectionResultFromCreditMemoItems + */ + private $getSourceSelectionResultFromCreditMemoItems; + + /** + * @var GetSourceDeductionRequestFromSourceSelection + */ + private $getSourceDeductionRequestFromSourceSelection; + + /** + * @var SourceSelectionServiceInterface + */ + private $sourceSelectionService; + + /** + * @var SourceDeductionServiceInterface + */ + private $sourceDeductionService; + + /** + * @var ItemToSellInterfaceFactory + */ + private $itemsToSellFactory; + + /** + * @var PlaceReservationsForSalesEventInterface + */ + private $placeReservationsForSalesEvent; + + /** + * @param GetSourceSelectionResultFromCreditMemoItems $getSourceSelectionResultFromCreditMemoItems + * @param GetSourceDeductionRequestFromSourceSelection $getSourceDeductionRequestFromSourceSelection + * @param SourceSelectionServiceInterface $sourceSelectionService + * @param SourceDeductionServiceInterface $sourceDeductionService + * @param ItemToSellInterfaceFactory $itemsToSellFactory + * @param PlaceReservationsForSalesEventInterface $placeReservationsForSalesEvent + */ + public function __construct( + GetSourceSelectionResultFromCreditMemoItems $getSourceSelectionResultFromCreditMemoItems, + GetSourceDeductionRequestFromSourceSelection $getSourceDeductionRequestFromSourceSelection, + SourceSelectionServiceInterface $sourceSelectionService, + SourceDeductionServiceInterface $sourceDeductionService, + ItemToSellInterfaceFactory $itemsToSellFactory, + PlaceReservationsForSalesEventInterface $placeReservationsForSalesEvent + ) { + $this->getSourceSelectionResultFromCreditMemoItems = $getSourceSelectionResultFromCreditMemoItems; + $this->sourceSelectionService = $sourceSelectionService; + $this->sourceDeductionService = $sourceDeductionService; + $this->itemsToSellFactory = $itemsToSellFactory; + $this->placeReservationsForSalesEvent = $placeReservationsForSalesEvent; + $this->getSourceDeductionRequestFromSourceSelection = $getSourceDeductionRequestFromSourceSelection; + } + + /** + * @param OrderInterface $order + * @param ItemsToRefundInterface[] $itemsToRefund + * @param array $itemsToDeductFromSource + */ + public function execute( + OrderInterface $order, + array $itemsToRefund, + array $itemsToDeductFromSource + ): void { + $sourceSelectionResult = $this->getSourceSelectionResultFromCreditMemoItems->execute( + $order, + $itemsToRefund, + $itemsToDeductFromSource + ); + + $sourceDeductionRequests = $this->getSourceDeductionRequestFromSourceSelection->execute( + $order, + $sourceSelectionResult + ); + + foreach ($sourceDeductionRequests as $sourceDeductionRequest) { + $this->sourceDeductionService->execute($sourceDeductionRequest); + $this->placeCompensatingReservation($sourceDeductionRequest); + } + } + + /** + * Place compensating reservation after source deduction + * + * @param SourceDeductionRequestInterface $sourceDeductionRequest + */ + private function placeCompensatingReservation(SourceDeductionRequestInterface $sourceDeductionRequest): void + { + $items = []; + foreach ($sourceDeductionRequest->getItems() as $item) { + $items[] = $this->itemsToSellFactory->create([ + 'sku' => $item->getSku(), + 'qty' => $item->getQty() + ]); + } + $this->placeReservationsForSalesEvent->execute( + $items, + $sourceDeductionRequest->getSalesChannel(), + $sourceDeductionRequest->getSalesEvent() + ); + } +} diff --git a/InventorySales/Model/ReturnProcessor/GetSourceDeductionRequestFromSourceSelection.php b/InventorySales/Model/ReturnProcessor/GetSourceDeductionRequestFromSourceSelection.php new file mode 100644 index 000000000000..4da3c608e059 --- /dev/null +++ b/InventorySales/Model/ReturnProcessor/GetSourceDeductionRequestFromSourceSelection.php @@ -0,0 +1,133 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\InventorySales\Model\ReturnProcessor; + +use Magento\InventorySalesApi\Api\Data\SalesChannelInterface; +use Magento\InventorySalesApi\Api\Data\SalesChannelInterfaceFactory; +use Magento\InventorySalesApi\Api\Data\SalesEventInterface; +use Magento\InventorySalesApi\Api\Data\SalesEventInterfaceFactory; +use Magento\InventorySourceDeductionApi\Model\ItemToDeductInterface; +use Magento\InventorySourceDeductionApi\Model\ItemToDeductInterfaceFactory; +use Magento\InventorySourceDeductionApi\Model\SourceDeductionRequestInterface; +use Magento\InventorySourceDeductionApi\Model\SourceDeductionRequestInterfaceFactory; +use Magento\InventorySourceSelectionApi\Api\Data\SourceSelectionItemInterface; +use Magento\InventorySourceSelectionApi\Api\Data\SourceSelectionResultInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; + +class GetSourceDeductionRequestFromSourceSelection +{ + /** + * @var ItemToDeductInterfaceFactory + */ + private $itemToDeductFactory; + + /** + * @var SourceDeductionRequestInterfaceFactory + */ + private $sourceDeductionRequestFactory; + + /** + * @var SalesChannelInterfaceFactory + */ + private $salesChannelFactory; + + /** + * @var SalesEventInterfaceFactory + */ + private $salesEventFactory; + + /** + * @var WebsiteRepositoryInterface + */ + private $websiteRepository; + + /** + * @param ItemToDeductInterfaceFactory $itemToDeductFactory + * @param SourceDeductionRequestInterfaceFactory $sourceDeductionRequestFactory + * @param SalesChannelInterfaceFactory $salesChannelFactory + * @param SalesEventInterfaceFactory $salesEventFactory + * @param WebsiteRepositoryInterface $websiteRepository + */ + public function __construct( + ItemToDeductInterfaceFactory $itemToDeductFactory, + SourceDeductionRequestInterfaceFactory $sourceDeductionRequestFactory, + SalesChannelInterfaceFactory $salesChannelFactory, + SalesEventInterfaceFactory $salesEventFactory, + WebsiteRepositoryInterface $websiteRepository + ) { + $this->itemToDeductFactory = $itemToDeductFactory; + $this->sourceDeductionRequestFactory = $sourceDeductionRequestFactory; + $this->salesChannelFactory = $salesChannelFactory; + $this->salesEventFactory = $salesEventFactory; + $this->websiteRepository = $websiteRepository; + } + + /** + * @param OrderInterface $order + * @param SourceSelectionResultInterface $sourceSelectionResult + * @return array|SourceDeductionRequestInterface[] + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function execute( + OrderInterface $order, + SourceSelectionResultInterface $sourceSelectionResult + ): array { + $websiteId = (int)$order->getStore()->getWebsiteId(); + + $sourceDeductionRequests = []; + $websiteCode = $this->websiteRepository->getById($websiteId)->getCode(); + $salesChannel = $this->salesChannelFactory->create([ + 'data' => [ + 'type' => SalesChannelInterface::TYPE_WEBSITE, + 'code' => $websiteCode + ] + ]); + + $salesEvent = $this->salesEventFactory->create([ + 'type' => SalesEventInterface::EVENT_CREDITMEMO_CREATED, + 'objectType' => SalesEventInterface::OBJECT_TYPE_ORDER, + 'objectId' => (string)$order->getEntityId() + ]); + + foreach ($this->getItemsPerSource($sourceSelectionResult->getSourceSelectionItems()) as $sourceCode => $items) { + /** @var SourceDeductionRequestInterface[] $sourceDeductionRequests */ + $sourceDeductionRequests[] = $this->sourceDeductionRequestFactory->create([ + 'sourceCode' => $sourceCode, + 'items' => $items, + 'salesChannel' => $salesChannel, + 'salesEvent' => $salesEvent + ]); + } + + return $sourceDeductionRequests; + } + + /** + * @param SourceSelectionItemInterface[] $sourceSelectionItems + * @return ItemToDeductInterface[] + */ + private function getItemsPerSource(array $sourceSelectionItems): array + { + $itemsPerSource = []; + foreach ($sourceSelectionItems as $sourceSelectionItem) { + if ($sourceSelectionItem->getQtyToDeduct() < 0.000001) { + continue; + } + if (!isset($itemsPerSource[$sourceSelectionItem->getSourceCode()])) { + $itemsPerSource[$sourceSelectionItem->getSourceCode()] = []; + } + $itemsPerSource[$sourceSelectionItem->getSourceCode()][] = $this->itemToDeductFactory->create([ + 'sku' => $sourceSelectionItem->getSku(), + 'qty' => $sourceSelectionItem->getQtyToDeduct(), + ]); + } + return $itemsPerSource; + } +} diff --git a/InventorySales/Model/ReturnProcessor/GetSourceSelectionResultFromCreditMemoItems.php b/InventorySales/Model/ReturnProcessor/GetSourceSelectionResultFromCreditMemoItems.php new file mode 100644 index 000000000000..3f47e39cc657 --- /dev/null +++ b/InventorySales/Model/ReturnProcessor/GetSourceSelectionResultFromCreditMemoItems.php @@ -0,0 +1,122 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\InventorySales\Model\ReturnProcessor; + +use Magento\InventorySalesApi\Model\ReturnProcessor\GetSourceDeductedOrderItemsInterface; +use Magento\InventorySourceSelectionApi\Api\Data\ItemRequestInterfaceFactory; +use Magento\InventorySourceSelectionApi\Api\Data\SourceSelectionResultInterface; +use Magento\InventorySourceSelectionApi\Api\GetDefaultSourceSelectionAlgorithmCodeInterface; +use Magento\InventorySourceSelectionApi\Api\SourceSelectionServiceInterface; +use Magento\InventorySourceSelectionApi\Model\GetInventoryRequestFromOrder; +use Magento\Sales\Api\Data\OrderInterface; + +class GetSourceSelectionResultFromCreditMemoItems +{ + /** + * @var GetSourceDeductedOrderItemsInterface + */ + private $getSourceDeductedOrderItems; + + /** + * @var ItemRequestInterfaceFactory + */ + private $itemRequestFactory; + + /** + * @var GetInventoryRequestFromOrder + */ + private $getInventoryRequestFromOrder; + + /** + * @var SourceSelectionServiceInterface + */ + private $sourceSelectionService; + + /** + * @var GetDefaultSourceSelectionAlgorithmCodeInterface + */ + private $getDefaultSourceSelectionAlgorithmCode; + + /** + * @param GetSourceDeductedOrderItemsInterface $getSourceDeductedOrderItems + * @param ItemRequestInterfaceFactory $itemRequestFactory + * @param GetInventoryRequestFromOrder $getInventoryRequestFromOrder + * @param SourceSelectionServiceInterface $sourceSelectionService + * @param GetDefaultSourceSelectionAlgorithmCodeInterface $getDefaultSourceSelectionAlgorithmCode + */ + public function __construct( + GetSourceDeductedOrderItemsInterface $getSourceDeductedOrderItems, + ItemRequestInterfaceFactory $itemRequestFactory, + GetInventoryRequestFromOrder $getInventoryRequestFromOrder, + SourceSelectionServiceInterface $sourceSelectionService, + GetDefaultSourceSelectionAlgorithmCodeInterface $getDefaultSourceSelectionAlgorithmCode + ) { + $this->getSourceDeductedOrderItems = $getSourceDeductedOrderItems; + $this->itemRequestFactory = $itemRequestFactory; + $this->getInventoryRequestFromOrder = $getInventoryRequestFromOrder; + $this->sourceSelectionService = $sourceSelectionService; + $this->getDefaultSourceSelectionAlgorithmCode = $getDefaultSourceSelectionAlgorithmCode; + } + + /** + * @param OrderInterface $order + * @param array $itemsToRefund + * @param array $itemsToDeductFromSource + * @return SourceSelectionResultInterface + */ + public function execute( + OrderInterface $order, + array $itemsToRefund, + array $itemsToDeductFromSource + ): SourceSelectionResultInterface { + $deductedItems = $this->getSourceDeductedOrderItems->execute($order, $itemsToDeductFromSource); + $requestItems = []; + foreach ($itemsToRefund as $item) { + $sku = $item->getSku(); + + $totalDeductedQty = $this->getTotalDeductedQty($item, $deductedItems); + $processedQty = $item->getProcessedQuantity() - $totalDeductedQty; + $backQty = ($processedQty > 0) ? $item->getQuantity() - $processedQty : $item->getQuantity(); + $qtyBackToSource = ($backQty > 0) ? $item->getQuantity() - $backQty : $item->getQuantity(); + + if ($qtyBackToSource > 0) { + $requestItems[] = $this->itemRequestFactory->create([ + 'sku' => $sku, + 'qty' => (float)$qtyBackToSource + ]); + } + } + + $inventoryRequest = $this->getInventoryRequestFromOrder->execute((int)$order->getEntityId(), $requestItems); + $selectionAlgorithmCode = $this->getDefaultSourceSelectionAlgorithmCode->execute(); + + return $this->sourceSelectionService->execute($inventoryRequest, $selectionAlgorithmCode); + } + + /** + * @param $item + * @param array $deductedItems + * @return float + */ + private function getTotalDeductedQty($item, array $deductedItems): float + { + $result = 0; + + foreach ($deductedItems as $deductedItemResult) { + foreach ($deductedItemResult->getItems() as $deductedItem) { + if ($item->getSku() != $deductedItem->getSku()) { + continue; + } + $result += $deductedItem->getQuantity(); + } + } + + return $result; + } +} diff --git a/InventorySales/Observer/SalesInventory/DeductSourceItemQuantityOnRefundObserver.php b/InventorySales/Observer/SalesInventory/DeductSourceItemQuantityOnRefundObserver.php new file mode 100644 index 000000000000..7cbec55770b1 --- /dev/null +++ b/InventorySales/Observer/SalesInventory/DeductSourceItemQuantityOnRefundObserver.php @@ -0,0 +1,135 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventorySales\Observer\SalesInventory; + +use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; +use Magento\InventoryCatalogApi\Model\GetProductTypesBySkusInterface; +use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForProductTypeInterface; +use Magento\InventorySales\Model\ReturnProcessor\DeductSourceItemQuantityOnRefund; +use Magento\InventorySalesApi\Model\GetSkuFromOrderItemInterface; +use Magento\InventorySalesApi\Model\ReturnProcessor\Request\ItemsToRefundInterfaceFactory; +use Magento\Sales\Api\Data\CreditmemoItemInterface as CreditmemoItem; +use Magento\Sales\Api\Data\OrderItemInterface; + +class DeductSourceItemQuantityOnRefundObserver implements ObserverInterface +{ + /** + * @var GetSkuFromOrderItemInterface + */ + private $getSkuFromOrderItem; + + /** + * @var ItemsToRefundInterfaceFactory + */ + private $itemsToRefundFactory; + + /** + * @var IsSourceItemManagementAllowedForProductTypeInterface + */ + private $isSourceItemManagementAllowedForProductType; + + /** + * @var GetProductTypesBySkusInterface + */ + private $getProductTypesBySkus; + + /** + * @var DeductSourceItemQuantityOnRefund + */ + private $deductSourceItemQuantityOnRefund; + + /** + * @param GetSkuFromOrderItemInterface $getSkuFromOrderItem + * @param ItemsToRefundInterfaceFactory $itemsToRefundFactory + * @param IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemManagementAllowedForProductType + * @param GetProductTypesBySkusInterface $getProductTypesBySkus + * @param DeductSourceItemQuantityOnRefund $deductSourceItemQuantityOnRefund + */ + public function __construct( + GetSkuFromOrderItemInterface $getSkuFromOrderItem, + ItemsToRefundInterfaceFactory $itemsToRefundFactory, + IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemManagementAllowedForProductType, + GetProductTypesBySkusInterface $getProductTypesBySkus, + DeductSourceItemQuantityOnRefund $deductSourceItemQuantityOnRefund + ) { + $this->getSkuFromOrderItem = $getSkuFromOrderItem; + $this->itemsToRefundFactory = $itemsToRefundFactory; + $this->isSourceItemManagementAllowedForProductType = $isSourceItemManagementAllowedForProductType; + $this->getProductTypesBySkus = $getProductTypesBySkus; + $this->deductSourceItemQuantityOnRefund = $deductSourceItemQuantityOnRefund; + } + + /** + * @param Observer $observer + * @return void + */ + public function execute(Observer $observer) + { + /* @var $creditmemo \Magento\Sales\Model\Order\Creditmemo */ + $creditmemo = $observer->getEvent()->getCreditmemo(); + $order = $creditmemo->getOrder(); + $itemsToRefund = $refundedOrderItemIds = []; + /** @var CreditmemoItem $item */ + foreach ($creditmemo->getItems() as $item) { + /** @var OrderItemInterface $orderItem */ + $orderItem = $item->getOrderItem(); + $sku = $this->getSkuFromOrderItem->execute($orderItem); + + if ($this->isValidItem($sku, $item)) { + $refundedOrderItemIds[] = $item->getOrderItemId(); + $qty = (float)$item->getQty(); + $processedQty = $orderItem->getQtyInvoiced() - $orderItem->getQtyRefunded() + $qty; + $itemsToRefund[$sku] = [ + 'qty' => ($itemsToRefund[$sku]['qty'] ?? 0) + $qty, + 'processedQty' => ($itemsToRefund[$sku]['processedQty'] ?? 0) + (float)$processedQty + ]; + } + } + + $itemsToDeductFromSource = []; + foreach ($itemsToRefund as $sku => $data) { + $itemsToDeductFromSource[] = $this->itemsToRefundFactory->create([ + 'sku' => $sku, + 'qty' => $data['qty'], + 'processedQty' => $data['processedQty'] + ]); + } + + if (!empty($itemsToDeductFromSource)) { + $this->deductSourceItemQuantityOnRefund->execute( + $order, + $itemsToDeductFromSource, + $refundedOrderItemIds + ); + } + } + + /** + * @param string $sku + * @param CreditmemoItem $item + * @return bool + */ + private function isValidItem(string $sku, CreditmemoItem $item): bool + { + /** @var OrderItemInterface $orderItem */ + $orderItem = $item->getOrderItem(); + // Since simple products which are the part of a grouped product are saved in the database + // (table sales_order_item) with product type grouped, we manually change the type of + // product from grouped to simple which support source management. + $typeId = $orderItem->getProductType() === 'grouped' ? 'simple' : $orderItem->getProductType(); + + $productType = $typeId ?: $this->getProductTypesBySkus->execute( + [$sku] + )[$sku]; + + return $this->isSourceItemManagementAllowedForProductType->execute($productType) + && $item->getQty() > 0 + && !$item->getBackToStock(); + } +} diff --git a/InventorySales/composer.json b/InventorySales/composer.json index 52f30417bb62..61ec8cb87824 100644 --- a/InventorySales/composer.json +++ b/InventorySales/composer.json @@ -13,6 +13,7 @@ "magento/module-inventory-reservations-api": "*", "magento/module-inventory-sales-api": "*", "magento/module-inventory-source-deduction-api": "*", + "magento/module-inventory-source-selection-api": "*", "magento/module-sales-inventory": "*", "magento/module-store": "*", "magento/module-sales": "*" diff --git a/InventorySales/etc/events.xml b/InventorySales/etc/events.xml index 2c2f1eb4d092..1ed5a1f0e950 100644 --- a/InventorySales/etc/events.xml +++ b/InventorySales/etc/events.xml @@ -12,4 +12,7 @@ <event name="sales_order_item_cancel"> <observer name="inventory" instance="Magento\InventorySales\Observer\CatalogInventory\CancelOrderItemObserver"/> </event> + <event name="sales_order_creditmemo_save_after"> + <observer name="deduct_source_item_quantity_on_refund" instance="Magento\InventorySales\Observer\SalesInventory\DeductSourceItemQuantityOnRefundObserver"/> + </event> </config> From 86157938a312572d790e3713de7c4b56294fa3cb Mon Sep 17 00:00:00 2001 From: Maksym Novik <m.novik@ism-ukraine.com> Date: Mon, 15 Apr 2019 15:34:18 +0200 Subject: [PATCH 145/231] Product status is 'In Stock' on storefront even it is Out of Stock in all Sources #2156. Skip verification for non-manageable product types. --- .../IsAnySourceItemInStockCondition.php | 40 +++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/InventorySales/Model/IsProductSalableCondition/IsAnySourceItemInStockCondition.php b/InventorySales/Model/IsProductSalableCondition/IsAnySourceItemInStockCondition.php index 38690d717adc..75ffdb26c818 100644 --- a/InventorySales/Model/IsProductSalableCondition/IsAnySourceItemInStockCondition.php +++ b/InventorySales/Model/IsProductSalableCondition/IsAnySourceItemInStockCondition.php @@ -8,9 +8,11 @@ namespace Magento\InventorySales\Model\IsProductSalableCondition; use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\InventorySalesApi\Api\IsProductSalableInterface; -use Magento\InventoryApi\Api\SourceItemRepositoryInterface; use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryApi\Api\SourceItemRepositoryInterface; +use Magento\InventoryCatalogApi\Model\GetProductTypesBySkusInterface; +use Magento\InventoryConfigurationApi\Model\GetAllowedProductTypesForSourceItemManagementInterface; +use Magento\InventorySalesApi\Api\IsProductSalableInterface; /** * @inheritdoc @@ -27,16 +29,32 @@ class IsAnySourceItemInStockCondition implements IsProductSalableInterface */ private $searchCriteriaBuilder; + /** + * @var GetProductTypesBySkusInterface + */ + private $getProductTypesBySkus; + + /** + * @var GetAllowedProductTypesForSourceItemManagementInterface + */ + private $getManageableTypes; + /** * @param SourceItemRepositoryInterface $sourceItemRepository * @param SearchCriteriaBuilder $searchCriteriaBuilder + * @param GetProductTypesBySkusInterface $getProductTypesBySkus + * @param GetAllowedProductTypesForSourceItemManagementInterface $getManageableTypes */ public function __construct( SourceItemRepositoryInterface $sourceItemRepository, - SearchCriteriaBuilder $searchCriteriaBuilder + SearchCriteriaBuilder $searchCriteriaBuilder, + GetProductTypesBySkusInterface $getProductTypesBySkus, + GetAllowedProductTypesForSourceItemManagementInterface $getManageableTypes ) { $this->sourceItemRepository = $sourceItemRepository; $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->getProductTypesBySkus = $getProductTypesBySkus; + $this->getManageableTypes = $getManageableTypes; } /** @@ -46,6 +64,9 @@ public function __construct( */ public function execute(string $sku, int $stockId): bool { + if (!$this->isProductStockManageable($sku)) { + return true; + } $searchCriteria = $this->searchCriteriaBuilder ->addFilter(SourceItemInterface::SKU, $sku) ->addFilter(SourceItemInterface::STATUS, SourceItemInterface::STATUS_IN_STOCK) @@ -55,4 +76,17 @@ public function execute(string $sku, int $stockId): bool return (bool)count($sourceItems); } + + /** + * @param string $sku + * + * @return bool + */ + private function isProductStockManageable(string $sku): bool + { + return in_array( + $this->getProductTypesBySkus->execute([$sku])[$sku], + $this->getManageableTypes->execute() + ); + } } From e7185c63afa142e3880e8c7dbb9efc81487574e7 Mon Sep 17 00:00:00 2001 From: Sergey Mutaf <seruymt@gmail.com> Date: Mon, 15 Apr 2019 18:50:39 +0300 Subject: [PATCH 146/231] MSI-1947:Source items deduction during Credit memo creation for unshipped order items - fix fatal error on refund --- .../GetSourceSelectionResultFromCreditMemoItems.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/InventorySales/Model/ReturnProcessor/GetSourceSelectionResultFromCreditMemoItems.php b/InventorySales/Model/ReturnProcessor/GetSourceSelectionResultFromCreditMemoItems.php index 3f47e39cc657..f495f3b1b1bc 100644 --- a/InventorySales/Model/ReturnProcessor/GetSourceSelectionResultFromCreditMemoItems.php +++ b/InventorySales/Model/ReturnProcessor/GetSourceSelectionResultFromCreditMemoItems.php @@ -85,12 +85,10 @@ public function execute( $backQty = ($processedQty > 0) ? $item->getQuantity() - $processedQty : $item->getQuantity(); $qtyBackToSource = ($backQty > 0) ? $item->getQuantity() - $backQty : $item->getQuantity(); - if ($qtyBackToSource > 0) { - $requestItems[] = $this->itemRequestFactory->create([ - 'sku' => $sku, - 'qty' => (float)$qtyBackToSource - ]); - } + $requestItems[] = $this->itemRequestFactory->create([ + 'sku' => $sku, + 'qty' => (float)$qtyBackToSource + ]); } $inventoryRequest = $this->getInventoryRequestFromOrder->execute((int)$order->getEntityId(), $requestItems); From 600b1435d626f7fe43675307e421a577dfedf39a Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Mon, 15 Apr 2019 18:56:15 +0300 Subject: [PATCH 147/231] MSI-1912: avoid use id on the test --- .../Integration/Sales/GetSkuFromOrderItemTest.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/InventoryConfigurableProduct/Test/Integration/Sales/GetSkuFromOrderItemTest.php b/InventoryConfigurableProduct/Test/Integration/Sales/GetSkuFromOrderItemTest.php index b7704810cc99..64d4ad245392 100644 --- a/InventoryConfigurableProduct/Test/Integration/Sales/GetSkuFromOrderItemTest.php +++ b/InventoryConfigurableProduct/Test/Integration/Sales/GetSkuFromOrderItemTest.php @@ -7,6 +7,7 @@ namespace Magento\InventoryConfigurableProduct\Test\Integration\Sales; +use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\InventorySalesApi\Model\GetSkuFromOrderItemInterface; use Magento\Sales\Api\OrderItemRepositoryInterface; use Magento\TestFramework\Helper\Bootstrap; @@ -27,6 +28,11 @@ class GetSkuFromOrderItemTest extends TestCase */ private $getSkuFromOrderItemInterface; + /** + * @var SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + /** * @inheritdoc */ @@ -36,6 +42,7 @@ protected function setUp() $this->orderItemRepository = Bootstrap::getObjectManager()->get(OrderItemRepositoryInterface::class); $this->getSkuFromOrderItemInterface = Bootstrap::getObjectManager()->get(GetSkuFromOrderItemInterface::class); + $this->searchCriteriaBuilder = Bootstrap::getObjectManager()->get(SearchCriteriaBuilder::class); } /** @@ -43,8 +50,9 @@ protected function setUp() */ public function testGetSkuFromConfigurableProductWithCustomOptionsOrderItem() { - $orderItem = $this->orderItemRepository->get(1); - $sku = $this->getSkuFromOrderItemInterface->execute($orderItem); + $orderItems = $this->orderItemRepository->getList($this->searchCriteriaBuilder->create()) + ->getItems(); + $sku = $this->getSkuFromOrderItemInterface->execute(current($orderItems)); $this->assertEquals('configurable', $sku); } } From 0213832c86fcdce2177ac1b0dc3cfd3cf541378c Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Mon, 15 Apr 2019 19:19:06 +0300 Subject: [PATCH 148/231] MSI-2043: fixed back button on shipping step --- .../BackButtonUrlOnNewShipmentPagePlugin.php | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/InventoryShippingAdminUi/Plugin/Sales/Block/Shipment/BackButtonUrlOnNewShipmentPagePlugin.php b/InventoryShippingAdminUi/Plugin/Sales/Block/Shipment/BackButtonUrlOnNewShipmentPagePlugin.php index 0430236832bb..00bfac424094 100644 --- a/InventoryShippingAdminUi/Plugin/Sales/Block/Shipment/BackButtonUrlOnNewShipmentPagePlugin.php +++ b/InventoryShippingAdminUi/Plugin/Sales/Block/Shipment/BackButtonUrlOnNewShipmentPagePlugin.php @@ -7,9 +7,14 @@ namespace Magento\InventoryShippingAdminUi\Plugin\Sales\Block\Shipment; +use Magento\Framework\App\ObjectManager; +use Magento\InventoryShippingAdminUi\Model\IsOrderSourceManageable; use Magento\Shipping\Block\Adminhtml\Create; use Magento\InventoryShippingAdminUi\Model\IsWebsiteInMultiSourceMode; +/** + * Class BackButtonUrlOnNewShipmentPagePlugin + */ class BackButtonUrlOnNewShipmentPagePlugin { /** @@ -17,27 +22,39 @@ class BackButtonUrlOnNewShipmentPagePlugin */ private $isWebsiteInMultiSourceMode; + /** + * @var IsOrderSourceManageable + */ + private $isOrderSourceManageable; + /** * @param IsWebsiteInMultiSourceMode $isWebsiteInMultiSourceMode + * @param IsOrderSourceManageable $isOrderSourceManageable */ public function __construct( - IsWebsiteInMultiSourceMode $isWebsiteInMultiSourceMode + IsWebsiteInMultiSourceMode $isWebsiteInMultiSourceMode, + IsOrderSourceManageable $isOrderSourceManageable = null ) { $this->isWebsiteInMultiSourceMode = $isWebsiteInMultiSourceMode; + $this->isOrderSourceManageable = $isOrderSourceManageable ?? + ObjectManager::getInstance()->get(IsOrderSourceManageable::class); } /** + * Returns URL to Source Selection if source for order is manageable + * * @param Create $subject * @param $result * @return string */ public function afterGetBackUrl(Create $subject, $result) { - if (empty($subject->getShipment())) { + $order = $subject->getShipment()->getOrder(); + if (empty($subject->getShipment()) || !$this->isOrderSourceManageable->execute($order)) { return $result; } - $websiteId = (int)$subject->getShipment()->getOrder()->getStore()->getWebsiteId(); + $websiteId = (int)$order->getStore()->getWebsiteId(); if ($this->isWebsiteInMultiSourceMode->execute($websiteId)) { return $subject->getUrl( 'inventoryshipping/SourceSelection/index', From 04ef8c69d34d0b268e3e0a358657f960a309aaac Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Mon, 15 Apr 2019 14:50:05 -0500 Subject: [PATCH 149/231] MSI-2055 MSI-2057 MFTF test coverage --- ...ductFromHomepageWithFreeShippingMethod.xml | 26 +++++ ...mpleProductOnTestStockFromHomepageTest.xml | 110 ++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 InventoryAdminUi/Test/Mftf/Test/LoggedInCustomerCreatedOrderWithSimpleProductFromHomepageWithFreeShippingMethod.xml create mode 100644 InventoryAdminUi/Test/Mftf/Test/LoggedInCustomerCreatedOrderWithSimpleProductOnTestStockFromHomepageTest.xml diff --git a/InventoryAdminUi/Test/Mftf/Test/LoggedInCustomerCreatedOrderWithSimpleProductFromHomepageWithFreeShippingMethod.xml b/InventoryAdminUi/Test/Mftf/Test/LoggedInCustomerCreatedOrderWithSimpleProductFromHomepageWithFreeShippingMethod.xml new file mode 100644 index 000000000000..12809c7f0ece --- /dev/null +++ b/InventoryAdminUi/Test/Mftf/Test/LoggedInCustomerCreatedOrderWithSimpleProductFromHomepageWithFreeShippingMethod.xml @@ -0,0 +1,26 @@ +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="LoggedInCustomerCreatedOrderWithSimpleProductFromHomepageWithFreeShippingMethod" extends="LoggedInCustomerCreatedOrderWithSimpleProductOnTestStockFromHomepage"> + <annotations> + <stories value="Logged In Customer created Order with Simple product from Homepage with Free Shipping method"/> + <title value="Logged In Customer created Order with Simple product from Homepage with Free Shipping method"/> + <description value="/scenarios/1408757"/> + <testCaseId value="MSI-2057"/> + <severity value="BLOCKER"/> + <group value="msi"/> + <group value="multi_mode"/> + </annotations> + + <before> + <magentoCLI stepKey="enableFreeShipping" command="config:set carriers/freeshipping/active 1" before="loginAsAdmin"/> + </before> + <after> + <magentoCLI stepKey="disableFreeShipping" command="config:set carriers/freeshipping/active 0" after="logoutOfAdmin"/> + </after> + + <!--Free shipping--> + <click selector="{{CheckoutShippingMethodsSection.checkShippingMethodByName('Free Shipping')}}" stepKey="selectFlatShippingMethod" after="waitForPaymentSelectionPageLoad" /> + <!--End Free shipping--> + + </test> +</tests> diff --git a/InventoryAdminUi/Test/Mftf/Test/LoggedInCustomerCreatedOrderWithSimpleProductOnTestStockFromHomepageTest.xml b/InventoryAdminUi/Test/Mftf/Test/LoggedInCustomerCreatedOrderWithSimpleProductOnTestStockFromHomepageTest.xml new file mode 100644 index 000000000000..bf98d3123754 --- /dev/null +++ b/InventoryAdminUi/Test/Mftf/Test/LoggedInCustomerCreatedOrderWithSimpleProductOnTestStockFromHomepageTest.xml @@ -0,0 +1,110 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="LoggedInCustomerCreatedOrderWithSimpleProductOnTestStockFromHomepage"> + <annotations> + <stories value="Logged In Customer created Order with Simple product on Test stock from Homepage"/> + <title value="Logged In Customer created Order with Simple product on Test stock from Homepage"/> + <description value="/scenarios/1408755"/> + <testCaseId value="MSI-2055"/> + <severity value="BLOCKER"/> + <group value="msi"/> + <group value="multi_mode"/> + </annotations> + + <before> + <createData entity="MsiCustomer1" stepKey="createCustomer"/> + <createData entity="BasicMsiStockWithMainWebsite1" stepKey="createStock"/> + <createData entity="FullSource1" stepKey="createSource"/> + <createData entity="SourceStockLinked1" stepKey="linkSourceStock"> + <requiredEntity createDataKey="createStock"/> + <requiredEntity createDataKey="createSource"/> + </createData> + <createData entity="SimpleSubCategory" stepKey="simpleCategory"/> + <createData entity="SimpleProduct" stepKey="simpleProduct"> + <field key="qty">100.00</field> + <requiredEntity createDataKey="simpleCategory"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <waitForPageLoad stepKey="waitForDashboardLoad"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="simpleCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> + </after> + + <!-- As Admin, find the Product to edit --> + <actionGroup ref="AdminGoToProductGridFilterResultsByInputEditProduct" stepKey="goToProductGridFilterResultsByInputEditProduct1"> + <argument name="filter_selector" value="AdminProductGridFilterSection.skuFilter"/> + <argument name="filter_value" value="SimpleProduct.sku"/> + </actionGroup> + + <!-- As Admin, assign a source to the product --> + <actionGroup ref="AdminOnProductEditPageAssignSourceToProduct" stepKey="AdminOnProductEditPageAssignSourceToProduct1"> + <argument name="filter_selector" value="AdminManageSourcesGridFilterControls.code"/> + <argument name="filter_value" value="$$createSource.source[source_code]$$"/> + </actionGroup> + <!--Set qty to 100--> + <fillField selector="{{AdminProductSourcesGrid.rowQty('1')}}" userInput="100" stepKey="fillSourceQuantityField"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton1"/> + + <!-- Login as customer --> + <comment userInput="Login to storefront as customer." stepKey="loginToStorefrontComment"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefront"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!-- Purchase product --> + <comment userInput="Purchase the created product" stepKey="PurchaseProduct"/> + <comment userInput="Purchase 5 simple product" stepKey="purchaseSimpleProductComment"/> + <amOnPage url="{{StorefrontCategoryPage.url($$simpleCategory.name$$)}}" stepKey="navigateToCategoryPage"/> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($$simpleProduct.name$$)}}" stepKey="moveMouseOverProduct" /> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$simpleProduct.name$$)}}" stepKey="clickAddToCart" /> + <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart($$simpleProduct.name$$)}}" time="30" stepKey="assertMessage"/> + <waitForText userInput="1" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> + <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> + <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForViewAndEditCartVisible"/> + + <click selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct.name$$)}}" stepKey="clickOnQtyField1"/> + <pressKey selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct.name$$)}}" userInput="\Facebook\WebDriver\WebDriverKeys::DELETE" stepKey="deleteExistingText1"/> + + <fillField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct.name$$)}}" userInput="5" stepKey="setProductQtyToFiftyInMiniCart"/> + <click selector="{{StorefrontMinicartSection.itemQuantityUpdate($$simpleProduct.name$$)}}" stepKey="updateQtyInMiniCart"/> + <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForPaymentSelectionPageLoad"/> + <click selector=".continue" stepKey="clickOnNextPaymentPage"/> + <waitForPageLoad stepKey="waitForPageLoadCheckoutSelectPayment"/> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyOrderPayment"/> + <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButtonVisible"/> + <see selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{US_Address_TX.street[0]}}" stepKey="chooseBillingAddress"/> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="placeOrder"/> + <waitForPageLoad stepKey="waitUntilOrderPlaced"/> + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> + <see selector="{{CheckoutSuccessMainSection.success}}" userInput="Your order number is:" stepKey="checkOrderPlaceSuccessMessage"/> + + <!-- Admin area check ordered quantity --> + <comment userInput="Admin - Check ordered quantity" stepKey="AdminCheckOrderedQuantity"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderListPage"/> + <waitForLoadingMaskToDisappear stepKey="waitOrderListPageLoad"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrder"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchCheckOrderAfterCustomerSubmits"/> + <waitForLoadingMaskToDisappear stepKey="waitFilteredOrderListPageLoad"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="navigateToOrderViewPage"/> + <waitForElementVisible selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" stepKey="waitForViewOrderedQuantity"/> + <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Ordered 5" stepKey="orderedQuantity"/> + + <!--Admin Area Check source quantity and salable quantity--> + <comment userInput="Admin - Check Source quantity and salable quantity after order placed" stepKey="AdminCheckQuantityAfterOrderPlaced"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterPlaceOrder"/> + <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterPlaceOrder"> + <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> + <argument name="value" value="$$simpleProduct.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',$$createSource.source[name]$$)}}" userInput="100" stepKey="checkProductSourceQtyAfterPlaceOrder"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',$$createStock.stock[name]$$)}}" userInput="95" stepKey="checkSalableQtyAfterPlaceOrder"/> + + </test> +</tests> From 23526de7066dfb72997c9194b9a3450ac8b202c5 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Tue, 16 Apr 2019 09:33:44 +0300 Subject: [PATCH 150/231] MSI-2043: fixed back button url --- .../Shipment/BackButtonUrlOnNewShipmentPagePlugin.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/InventoryShippingAdminUi/Plugin/Sales/Block/Shipment/BackButtonUrlOnNewShipmentPagePlugin.php b/InventoryShippingAdminUi/Plugin/Sales/Block/Shipment/BackButtonUrlOnNewShipmentPagePlugin.php index 00bfac424094..ca4a117718ad 100644 --- a/InventoryShippingAdminUi/Plugin/Sales/Block/Shipment/BackButtonUrlOnNewShipmentPagePlugin.php +++ b/InventoryShippingAdminUi/Plugin/Sales/Block/Shipment/BackButtonUrlOnNewShipmentPagePlugin.php @@ -49,8 +49,9 @@ public function __construct( */ public function afterGetBackUrl(Create $subject, $result) { - $order = $subject->getShipment()->getOrder(); - if (empty($subject->getShipment()) || !$this->isOrderSourceManageable->execute($order)) { + $shipment = $subject->getShipment(); + $order = $shipment->getOrder(); + if (empty($shipment) || !$this->isOrderSourceManageable->execute($order)) { return $result; } @@ -59,7 +60,7 @@ public function afterGetBackUrl(Create $subject, $result) return $subject->getUrl( 'inventoryshipping/SourceSelection/index', [ - 'order_id' => $subject->getShipment() ? $subject->getShipment()->getOrderId() : null + 'order_id' => $shipment ? $shipment->getOrderId() : null ] ); } From 6b7b645ea3cdf5d0c386f61545371be6549fe17a Mon Sep 17 00:00:00 2001 From: Vadim Justus <v.justus@techdivision.com> Date: Tue, 16 Apr 2019 00:38:18 +0200 Subject: [PATCH 151/231] magento-engcom/msi#2170: Introduce CLI command, which creates compansations for inconsistent reservations - Refactore given logic for collection inconsistencies - Change CLI, which displays all given inconsistencies - Introduce CLI command, which creates the compensations --- .../Command/CreateCompensations.php | 211 ++++++++++++++++++ ...sistencies.php => ShowInconsistencies.php} | 49 +++- .../Model/GetCompleteOrderStatusList.php | 30 +++ .../GetSaleableQuantityCompensations.php | 77 +++++++ .../GetSaleableQuantityInconsistencies.php | 45 ++-- .../Model/SaleableQuantityInconsistency.php | 21 ++ ...etedOrdersToForUnresolvedReservations.php} | 10 +- .../AddExistingReservations.php | 25 ++- .../AddExpectedReservations.php | 44 +++- .../Collector.php | 70 ++++-- .../FilterCompleteOrders.php | 47 ++++ ...vantOrder.php => FilterExistingOrders.php} | 18 +- .../FilterIncompleteOrders.php | 47 ++++ ...s.php => FilterUnresolvedReservations.php} | 15 +- ...ingReservationsForIncompleteOrdersTest.php | 57 ----- .../GetListReservationsTotOrdersTest.php | 64 ------ ...GetSaleableQuantityInconsistenciesTest.php | 70 ++++++ ...e_incomplete_order_without_reservation.php | 71 ++++++ InventoryReservationCli/etc/di.xml | 5 +- 19 files changed, 780 insertions(+), 196 deletions(-) create mode 100644 InventoryReservationCli/Command/CreateCompensations.php rename InventoryReservationCli/Command/{ShowSaleableQuantityInconsistencies.php => ShowInconsistencies.php} (71%) create mode 100644 InventoryReservationCli/Model/GetCompleteOrderStatusList.php create mode 100644 InventoryReservationCli/Model/GetSaleableQuantityCompensations.php rename InventoryReservationCli/Model/SaleableQuantityInconsistency/{AddCompletedOrdersToUnresolved.php => AddCompletedOrdersToForUnresolvedReservations.php} (77%) create mode 100644 InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterCompleteOrders.php rename InventoryReservationCli/Model/SaleableQuantityInconsistency/{RemoveReservationsWithoutRelevantOrder.php => FilterExistingOrders.php} (55%) create mode 100644 InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterIncompleteOrders.php rename InventoryReservationCli/Model/SaleableQuantityInconsistency/{RemoveResolvedReservations.php => FilterUnresolvedReservations.php} (66%) delete mode 100644 InventoryReservationCli/Test/Integration/Model/GetListMissingReservationsForIncompleteOrdersTest.php delete mode 100644 InventoryReservationCli/Test/Integration/Model/GetListReservationsTotOrdersTest.php create mode 100644 InventoryReservationCli/Test/Integration/Model/GetSaleableQuantityInconsistenciesTest.php create mode 100644 InventoryReservationCli/Test/Integration/_fixtures/create_incomplete_order_without_reservation.php diff --git a/InventoryReservationCli/Command/CreateCompensations.php b/InventoryReservationCli/Command/CreateCompensations.php new file mode 100644 index 000000000000..0a12cf41d137 --- /dev/null +++ b/InventoryReservationCli/Command/CreateCompensations.php @@ -0,0 +1,211 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservationCli\Command; + +use Magento\Framework\Exception\CouldNotSaveException; +use Magento\Framework\Exception\InputException; +use Magento\Framework\Validation\ValidationException; +use Magento\InventoryReservationCli\Model\GetSaleableQuantityCompensations; +use Magento\InventoryReservationCli\Model\GetSaleableQuantityInconsistencies; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterCompleteOrders; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterIncompleteOrders; +use Magento\InventoryReservationsApi\Model\AppendReservationsInterface; +use Magento\InventoryReservationsApi\Model\ReservationInterface; +use Magento\Sales\Model\Order; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Create compensations for detected inconsistencies + * + * This command may be used to simplify migrations from Magento versions without new Inventory or to track down + * incorrect behavior of customizations. + */ +class CreateCompensations extends Command +{ + /** + * @var GetSaleableQuantityInconsistencies + */ + private $getSaleableQuantityInconsistencies; + + /** + * @var FilterCompleteOrders + */ + private $filterCompleteOrders; + + /** + * @var FilterIncompleteOrders + */ + private $filterIncompleteOrders; + + /** + * @var GetSaleableQuantityCompensations + */ + private $getSaleableQuantityCompensations; + + /** + * @var AppendReservationsInterface + */ + private $appendReservations; + + /** + * @param GetSaleableQuantityInconsistencies $getSaleableQuantityInconsistencies + * @param GetSaleableQuantityCompensations $getSaleableQuantityCompensations + * @param AppendReservationsInterface $appendReservations + * @param FilterCompleteOrders $filterCompleteOrders + * @param FilterIncompleteOrders $filterIncompleteOrders + */ + public function __construct( + GetSaleableQuantityInconsistencies $getSaleableQuantityInconsistencies, + GetSaleableQuantityCompensations $getSaleableQuantityCompensations, + AppendReservationsInterface $appendReservations, + FilterCompleteOrders $filterCompleteOrders, + FilterIncompleteOrders $filterIncompleteOrders + ) { + parent::__construct(); + $this->getSaleableQuantityInconsistencies = $getSaleableQuantityInconsistencies; + $this->filterCompleteOrders = $filterCompleteOrders; + $this->filterIncompleteOrders = $filterIncompleteOrders; + $this->getSaleableQuantityCompensations = $getSaleableQuantityCompensations; + $this->appendReservations = $appendReservations; + } + + /** + * @inheritdoc + */ + protected function configure() + { + $this + ->setName('inventory:reservation:create-compensations') + ->setDescription('Create compensation reservations for detected inconsistencies') + ->addOption( + 'complete-orders', + 'c', + InputOption::VALUE_NONE, + 'Compensate only inconsistencies for completed orders' + ) + ->addOption( + 'incomplete-orders', + 'i', + InputOption::VALUE_NONE, + 'Compensate only inconsistencies for incomplete orders' + ) + ->addOption( + 'dry-run', + 'd', + InputOption::VALUE_NONE, + 'Display result without applying reservations' + ) + ->addOption( + 'raw', + 'r', + InputOption::VALUE_NONE, + 'Raw output' + ); + + parent::configure(); + } + + /** + * Format output + * + * @param OutputInterface $output + * @param ReservationInterface[] $compensations + */ + private function prettyOutput(OutputInterface $output, array $compensations): void + { + $output->writeln('<info>Following reservations were created:</info>'); + + /** @var Order $order */ + foreach ($compensations as $reservation) { + $output->writeln( + sprintf( + 'Product <comment>%s</comment> compensated by <comment>%+f</comment> for stock id <comment>%s</comment>', + $reservation->getSku(), + $reservation->getQuantity(), + $reservation->getStockId() + ) + ); + } + } + + /** + * Output without formatting + * + * @param OutputInterface $output + * @param ReservationInterface[] $compensations + */ + private function rawOutput(OutputInterface $output, array $compensations): void + { + /** @var Order $order */ + foreach ($compensations as $reservation) { + $output->writeln( + sprintf( + '%s:%f:%s', + $reservation->getSku(), + $reservation->getQuantity(), + $reservation->getStockId() + ) + ); + } + } + + /** + * @param InputInterface $input + * @return SaleableQuantityInconsistency[] + * @throws ValidationException + */ + private function getFilteredInconsistencies(InputInterface $input): array + { + $inconsistencies = $this->getSaleableQuantityInconsistencies->execute(); + + if ($input->getOption('complete-orders')) { + $inconsistencies = $this->filterCompleteOrders->execute($inconsistencies); + } elseif ($input->getOption('incomplete-orders')) { + $inconsistencies = $this->filterIncompleteOrders->execute($inconsistencies); + } + + return $inconsistencies; + } + + /** + * {@inheritdoc} + * + * @param InputInterface $input + * @param OutputInterface $output + * @return int + * @throws ValidationException + * @throws InputException + * @throws CouldNotSaveException + */ + public function execute(InputInterface $input, OutputInterface $output): int + { + $inconsistencies = $this->getFilteredInconsistencies($input); + $compensations = $this->getSaleableQuantityCompensations->execute($inconsistencies); + + if (empty($compensations)) { + $output->writeln('<info>No required compensations calculated.</info>'); + return 0; + } + + if (!$input->getOption('dry-run')) { + $this->appendReservations->execute($compensations); + } + + if ($input->getOption('raw')) { + $this->rawOutput($output, $compensations); + } else { + $this->prettyOutput($output, $compensations); + } + + return 0; + } +} diff --git a/InventoryReservationCli/Command/ShowSaleableQuantityInconsistencies.php b/InventoryReservationCli/Command/ShowInconsistencies.php similarity index 71% rename from InventoryReservationCli/Command/ShowSaleableQuantityInconsistencies.php rename to InventoryReservationCli/Command/ShowInconsistencies.php index b9a00193c912..5ee935689be5 100644 --- a/InventoryReservationCli/Command/ShowSaleableQuantityInconsistencies.php +++ b/InventoryReservationCli/Command/ShowInconsistencies.php @@ -7,8 +7,11 @@ namespace Magento\InventoryReservationCli\Command; +use Magento\Framework\Validation\ValidationException; use Magento\InventoryReservationCli\Model\GetSaleableQuantityInconsistencies; use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterCompleteOrders; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterIncompleteOrders; use Magento\Sales\Model\Order; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; @@ -16,26 +19,42 @@ use Symfony\Component\Console\Output\OutputInterface; /** - * Outputs a list of uncompensated reservations linked to the orders in final state (Completed, Closed, Canceled). + * Outputs a list of uncompensated reservations linked to the orders * * This command may be used to simplify migrations from Magento versions without new Inventory or to track down * incorrect behavior of customizations. */ -class ShowSaleableQuantityInconsistencies extends Command +class ShowInconsistencies extends Command { /** * @var GetSaleableQuantityInconsistencies */ private $getSaleableQuantityInconsistencies; + /** + * @var FilterCompleteOrders + */ + private $filterCompleteOrders; + + /** + * @var FilterIncompleteOrders + */ + private $filterIncompleteOrders; + /** * @param GetSaleableQuantityInconsistencies $getSaleableQuantityInconsistencies + * @param FilterCompleteOrders $filterCompleteOrders + * @param FilterIncompleteOrders $filterIncompleteOrders */ public function __construct( - GetSaleableQuantityInconsistencies $getSaleableQuantityInconsistencies + GetSaleableQuantityInconsistencies $getSaleableQuantityInconsistencies, + FilterCompleteOrders $filterCompleteOrders, + FilterIncompleteOrders $filterIncompleteOrders ) { parent::__construct(); $this->getSaleableQuantityInconsistencies = $getSaleableQuantityInconsistencies; + $this->filterCompleteOrders = $filterCompleteOrders; + $this->filterIncompleteOrders = $filterIncompleteOrders; } /** @@ -47,10 +66,16 @@ protected function configure() ->setName('inventory:reservation:list-inconsistencies') ->setDescription('Show all orders and products with saleable quantity inconsistencies') ->addOption( - 'filter', - 'f', - InputOption::VALUE_REQUIRED, - 'Filter for complete or incomplete orders' + 'complete-orders', + 'c', + InputOption::VALUE_NONE, + 'Show only inconsistencies for complete orders' + ) + ->addOption( + 'incomplete-orders', + 'i', + InputOption::VALUE_NONE, + 'Show only inconsistencies for incomplete orders' ) ->addOption( 'raw', @@ -70,7 +95,7 @@ protected function configure() */ private function prettyOutput(OutputInterface $output, array $inconsistencies): void { - $output->writeln('<comment>Inconsistencies found on following entries:</comment>'); + $output->writeln('<info>Inconsistencies found on following entries:</info>'); /** @var Order $order */ foreach ($inconsistencies as $inconsistency) { @@ -119,11 +144,18 @@ private function rawOutput(OutputInterface $output, array $inconsistencies): voi * @param InputInterface $input * @param OutputInterface $output * @return int + * @throws ValidationException */ public function execute(InputInterface $input, OutputInterface $output): int { $inconsistencies = $this->getSaleableQuantityInconsistencies->execute(); + if ($input->getOption('complete-orders')) { + $inconsistencies = $this->filterCompleteOrders->execute($inconsistencies); + } elseif ($input->getOption('incomplete-orders')) { + $inconsistencies = $this->filterIncompleteOrders->execute($inconsistencies); + } + if (empty($inconsistencies)) { $output->writeln('<info>No order inconsistencies were found</info>'); return 0; @@ -134,6 +166,7 @@ public function execute(InputInterface $input, OutputInterface $output): int } else { $this->prettyOutput($output, $inconsistencies); } + return -1; } } diff --git a/InventoryReservationCli/Model/GetCompleteOrderStatusList.php b/InventoryReservationCli/Model/GetCompleteOrderStatusList.php new file mode 100644 index 000000000000..6aaf4b75ebd3 --- /dev/null +++ b/InventoryReservationCli/Model/GetCompleteOrderStatusList.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservationCli\Model; + +use Magento\Sales\Model\Order; + +/** + * Provides list of order status for the complete state + */ +class GetCompleteOrderStatusList +{ + /** + * Provides list of order status for the complete state + * + * @return array + */ + public function execute(): array + { + return [ + Order::STATE_COMPLETE, + Order::STATE_CLOSED, + Order::STATE_CANCELED + ]; + } +} diff --git a/InventoryReservationCli/Model/GetSaleableQuantityCompensations.php b/InventoryReservationCli/Model/GetSaleableQuantityCompensations.php new file mode 100644 index 000000000000..3a827987ee64 --- /dev/null +++ b/InventoryReservationCli/Model/GetSaleableQuantityCompensations.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservationCli\Model; + +use Magento\Framework\Serialize\SerializerInterface; +use Magento\InventoryReservationsApi\Model\ReservationBuilderInterface; +use Magento\InventoryReservationsApi\Model\ReservationInterface; +use Magento\InventorySalesApi\Model\StockByWebsiteIdResolverInterface; + +/** + * Returns compensation reservations for given inconsistencies + */ +class GetSaleableQuantityCompensations +{ + /** + * @var ReservationBuilderInterface + */ + private $reservationBuilder; + + /** + * @var SerializerInterface + */ + private $serializer; + + /** + * @var StockByWebsiteIdResolverInterface + */ + private $stockByWebsiteIdResolver; + + /** + * @param ReservationBuilderInterface $reservationBuilder + * @param SerializerInterface $serializer + * @param StockByWebsiteIdResolverInterface $stockByWebsiteIdResolver + */ + public function __construct( + ReservationBuilderInterface $reservationBuilder, + SerializerInterface $serializer, + StockByWebsiteIdResolverInterface $stockByWebsiteIdResolver + ) { + $this->reservationBuilder = $reservationBuilder; + $this->serializer = $serializer; + $this->stockByWebsiteIdResolver = $stockByWebsiteIdResolver; + } + + /** + * Returns compensation reservations for given inconsistencies + * + * @param SaleableQuantityInconsistency[] $inconsistencies + * @return ReservationInterface[] + * @throws \Magento\Framework\Validation\ValidationException + */ + public function execute(array $inconsistencies): array + { + $compensations = []; + foreach ($inconsistencies as $inconsistency) { + foreach ($inconsistency->getItems() as $sku => $quantity) { + $compensations[] = $this->reservationBuilder + ->setSku($sku) + ->setQuantity((float)$quantity * -1) + ->setStockId($inconsistency->getStockId()) + ->setMetadata($this->serializer->serialize([ + 'event_type' => 'manual_compensation', + 'object_type' => 'order', + 'object_id' => $inconsistency->getObjectId(), + ])) + ->build(); + } + } + + return $compensations; + } +} diff --git a/InventoryReservationCli/Model/GetSaleableQuantityInconsistencies.php b/InventoryReservationCli/Model/GetSaleableQuantityInconsistencies.php index 602cbd7a3e41..b6b6f42fab83 100644 --- a/InventoryReservationCli/Model/GetSaleableQuantityInconsistencies.php +++ b/InventoryReservationCli/Model/GetSaleableQuantityInconsistencies.php @@ -7,13 +7,14 @@ namespace Magento\InventoryReservationCli\Model; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\AddCompletedOrdersToUnresolved; +use Magento\Framework\Validation\ValidationException; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\AddCompletedOrdersToForUnresolvedReservations; use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\AddExistingReservations; use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\AddExpectedReservations; use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\Collector; use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\CollectorFactory; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\RemoveReservationsWithoutRelevantOrder; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\RemoveResolvedReservations; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterExistingOrders; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterUnresolvedReservations; /** * Filter orders for missing initial reservation @@ -36,47 +37,48 @@ class GetSaleableQuantityInconsistencies private $addExistingReservations; /** - * @var RemoveResolvedReservations + * @var AddCompletedOrdersToForUnresolvedReservations */ - private $removeResolvedReservations; + private $addCompletedOrdersToUnresolved; /** - * @var AddCompletedOrdersToUnresolved + * @var FilterExistingOrders */ - private $addCompletedOrdersToUnresolved; + private $filterExistingOrders; /** - * @var RemoveReservationsWithoutRelevantOrder + * @var FilterUnresolvedReservations */ - private $removeReservationsWithoutRelevantOrder; + private $filterUnresolvedReservations; /** * @param CollectorFactory $collectorFactory * @param AddExpectedReservations $addExpectedReservations * @param AddExistingReservations $addExistingReservations - * @param RemoveResolvedReservations $removeResolvedReservations - * @param AddCompletedOrdersToUnresolved $addCompletedOrdersToUnresolved - * @param RemoveReservationsWithoutRelevantOrder $removeReservationsWithoutRelevantOrder + * @param AddCompletedOrdersToForUnresolvedReservations $addCompletedOrdersToUnresolved + * @param FilterExistingOrders $filterExistingOrder + * @param FilterUnresolvedReservations $filterUnresolvedReservations */ public function __construct( CollectorFactory $collectorFactory, AddExpectedReservations $addExpectedReservations, AddExistingReservations $addExistingReservations, - RemoveResolvedReservations $removeResolvedReservations, - AddCompletedOrdersToUnresolved $addCompletedOrdersToUnresolved, - RemoveReservationsWithoutRelevantOrder $removeReservationsWithoutRelevantOrder + AddCompletedOrdersToForUnresolvedReservations $addCompletedOrdersToUnresolved, + FilterExistingOrders $filterExistingOrders, + FilterUnresolvedReservations $filterUnresolvedReservations ) { $this->collectorFactory = $collectorFactory; $this->addExpectedReservations = $addExpectedReservations; $this->addExistingReservations = $addExistingReservations; - $this->removeResolvedReservations = $removeResolvedReservations; $this->addCompletedOrdersToUnresolved = $addCompletedOrdersToUnresolved; - $this->removeReservationsWithoutRelevantOrder = $removeReservationsWithoutRelevantOrder; + $this->filterExistingOrders = $filterExistingOrders; + $this->filterUnresolvedReservations = $filterUnresolvedReservations; } /** * Filter orders for missing initial reservation * @return SaleableQuantityInconsistency[] + * @throws ValidationException */ public function execute(): array { @@ -84,9 +86,12 @@ public function execute(): array $collector = $this->collectorFactory->create(); $this->addExpectedReservations->execute($collector); $this->addExistingReservations->execute($collector); - $this->removeResolvedReservations->execute($collector); $this->addCompletedOrdersToUnresolved->execute($collector); - $this->removeReservationsWithoutRelevantOrder->execute($collector); - return $collector->getInconsistencies(); + + $items = $collector->getItems(); + $items = $this->filterUnresolvedReservations->execute($items); + $items = $this->filterExistingOrders->execute($items); + + return $items; } } diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency.php b/InventoryReservationCli/Model/SaleableQuantityInconsistency.php index 25464ea3a195..5055d4f3714e 100644 --- a/InventoryReservationCli/Model/SaleableQuantityInconsistency.php +++ b/InventoryReservationCli/Model/SaleableQuantityInconsistency.php @@ -24,6 +24,11 @@ class SaleableQuantityInconsistency */ private $objectId; + /**+ + * @var int + */ + private $stockId; + /** * List of SKUs and quantity * @var array @@ -89,4 +94,20 @@ public function setItems(array $items): void { $this->items = $items; } + + /** + * @return int + */ + public function getStockId(): int + { + return $this->stockId; + } + + /** + * @param int $stockId + */ + public function setStockId(int $stockId): void + { + $this->stockId = $stockId; + } } diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddCompletedOrdersToUnresolved.php b/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddCompletedOrdersToForUnresolvedReservations.php similarity index 77% rename from InventoryReservationCli/Model/SaleableQuantityInconsistency/AddCompletedOrdersToUnresolved.php rename to InventoryReservationCli/Model/SaleableQuantityInconsistency/AddCompletedOrdersToForUnresolvedReservations.php index d11de0008433..a9c215b67fca 100644 --- a/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddCompletedOrdersToUnresolved.php +++ b/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddCompletedOrdersToForUnresolvedReservations.php @@ -12,7 +12,7 @@ /** * Match completed orders with unresolved reservations */ -class AddCompletedOrdersToUnresolved +class AddCompletedOrdersToForUnresolvedReservations { /** * @var GetOrdersInFinalState @@ -34,7 +34,7 @@ public function __construct( */ public function execute(Collector $collector): void { - $inconsistencies = $collector->getInconsistencies(); + $inconsistencies = $collector->getItems(); $orderIds = []; foreach ($inconsistencies as $inconsistency) { @@ -42,11 +42,9 @@ public function execute(Collector $collector): void } foreach ($this->getOrdersInFinalState->execute($orderIds) as $order) { - if (isset($inconsistencies[$order->getEntityId()])) { - $inconsistencies[$order->getEntityId()]->setOrder($order); - } + $collector->addOrder($order); } - $collector->setInconsistencies($inconsistencies); + $collector->setItems($inconsistencies); } } diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddExistingReservations.php b/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddExistingReservations.php index 38d113dd9456..00bc5afa8fe6 100644 --- a/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddExistingReservations.php +++ b/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddExistingReservations.php @@ -8,7 +8,9 @@ namespace Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\Validation\ValidationException; use Magento\InventoryReservationCli\Model\ResourceModel\GetReservationsList; +use Magento\InventoryReservationsApi\Model\ReservationBuilderInterface; /** * Add existing reservations @@ -25,21 +27,30 @@ class AddExistingReservations */ private $serializer; + /** + * @var ReservationBuilderInterface + */ + private $reservationBuilder; + /** * @param GetReservationsList $getReservationsList * @param SerializerInterface $serializer + * @param ReservationBuilderInterface $reservationBuilder */ public function __construct( GetReservationsList $getReservationsList, - SerializerInterface $serializer + SerializerInterface $serializer, + ReservationBuilderInterface $reservationBuilder ) { $this->getReservationsList = $getReservationsList; $this->serializer = $serializer; + $this->reservationBuilder = $reservationBuilder; } /** * Add existing reservations * @param Collector $collector + * @throws ValidationException */ public function execute(Collector $collector): void { @@ -47,16 +58,20 @@ public function execute(Collector $collector): void foreach ($reservationList as $reservation) { /** @var array $metadata */ $metadata = $this->serializer->unserialize($reservation['metadata']); - $objectId = (int)$metadata['object_id']; - $sku = $reservation['sku']; - $quantity = (float)$reservation['quantity']; $orderType = $metadata['object_type']; if ($orderType !== 'order') { continue; } - $collector->add($objectId, $sku, $quantity); + $reservation = $this->reservationBuilder + ->setMetadata($reservation['metadata']) + ->setStockId((int)$reservation['stock_id']) + ->setSku($reservation['sku']) + ->setQuantity((float)$reservation['quantity']) + ->build(); + + $collector->addReservation($reservation); } } } diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddExpectedReservations.php b/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddExpectedReservations.php index 69966b9bf4f8..a2ff6fb4bffc 100644 --- a/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddExpectedReservations.php +++ b/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddExpectedReservations.php @@ -7,7 +7,11 @@ namespace Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\Validation\ValidationException; use Magento\InventoryReservationCli\Model\GetOrdersInNotFinalState; +use Magento\InventoryReservationsApi\Model\ReservationBuilderInterface; +use Magento\InventorySalesApi\Model\StockByWebsiteIdResolverInterface; /** * Add expected reservations by current incomplete orders @@ -19,24 +23,60 @@ class AddExpectedReservations */ private $getOrdersInNotFinalState; + /** + * @var ReservationBuilderInterface + */ + private $reservationBuilder; + + /** + * @var StockByWebsiteIdResolverInterface + */ + private $stockByWebsiteIdResolver; + + /** + * @var SerializerInterface + */ + private $serializer; + /** * @param GetOrdersInNotFinalState $getOrdersInNotFinalState + * @param ReservationBuilderInterface $reservationBuilder + * @param StockByWebsiteIdResolverInterface $stockByWebsiteIdResolver + * @param SerializerInterface $serializer */ public function __construct( - GetOrdersInNotFinalState $getOrdersInNotFinalState + GetOrdersInNotFinalState $getOrdersInNotFinalState, + ReservationBuilderInterface $reservationBuilder, + StockByWebsiteIdResolverInterface $stockByWebsiteIdResolver, + SerializerInterface $serializer ) { $this->getOrdersInNotFinalState = $getOrdersInNotFinalState; + $this->reservationBuilder = $reservationBuilder; + $this->stockByWebsiteIdResolver = $stockByWebsiteIdResolver; + $this->serializer = $serializer; } /** * Add expected reservations by current incomplete orders * @param Collector $collector + * @throws ValidationException */ public function execute(Collector $collector): void { foreach ($this->getOrdersInNotFinalState->execute() as $order) { + $websiteId = (int)$order->getStore()->getWebsiteId(); + $stockId = (int)$this->stockByWebsiteIdResolver->execute((int)$websiteId)->getStockId(); + foreach ($order->getItems() as $item) { - $collector->add((int)$order->getEntityId(), $item->getSku(), (float)$item->getQtyOrdered(), $order); + $reservation = $this->reservationBuilder + ->setSku($item->getSku()) + ->setQuantity((float)$item->getQtyOrdered()) + ->setStockId($stockId) + ->setMetadata($this->serializer->serialize(['object_id' => (int)$order->getEntityId()])) + ->build(); + + $collector->addReservation($reservation); + $collector->addOrder($order); } } } diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency/Collector.php b/InventoryReservationCli/Model/SaleableQuantityInconsistency/Collector.php index a5c9d181b9fb..ab5de176d3c4 100644 --- a/InventoryReservationCli/Model/SaleableQuantityInconsistency/Collector.php +++ b/InventoryReservationCli/Model/SaleableQuantityInconsistency/Collector.php @@ -7,8 +7,11 @@ namespace Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; +use Magento\Framework\Serialize\SerializerInterface; use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistencyFactory; +use Magento\InventoryReservationsApi\Model\ReservationInterface; +use Magento\InventorySalesApi\Model\StockByWebsiteIdResolverInterface; use Magento\Sales\Api\Data\OrderInterface; /** @@ -19,56 +22,87 @@ class Collector /** * @var SaleableQuantityInconsistency[] */ - private $inconsistencies = []; + private $items = []; /** * @var \Magento\InventoryReservationCli\Model\SaleableQuantityInconsistencyFactory */ private $saleableQuantityInconsistencyFactory; + /** + * @var SerializerInterface + */ + private $serializer; + + /** + * @var StockByWebsiteIdResolverInterface + */ + private $stockByWebsiteIdResolver; + /** * @param SaleableQuantityInconsistencyFactory $saleableQuantityInconsistencyFactory + * @param SerializerInterface $serializer + * @param StockByWebsiteIdResolverInterface $stockByWebsiteIdResolver */ public function __construct( - SaleableQuantityInconsistencyFactory $saleableQuantityInconsistencyFactory + SaleableQuantityInconsistencyFactory $saleableQuantityInconsistencyFactory, + SerializerInterface $serializer, + StockByWebsiteIdResolverInterface $stockByWebsiteIdResolver ) { $this->saleableQuantityInconsistencyFactory = $saleableQuantityInconsistencyFactory; + $this->serializer = $serializer; + $this->stockByWebsiteIdResolver = $stockByWebsiteIdResolver; } /** - * @param int $objectId - * @param string $sku - * @param float $quantity - * @param OrderInterface|null $order + * @param ReservationInterface $reservation */ - public function add(int $objectId, string $sku, float $quantity, ?OrderInterface $order = null): void + public function addReservation(ReservationInterface $reservation): void { - if (!isset($this->inconsistencies[$objectId])) { - $this->inconsistencies[$objectId] = $this->saleableQuantityInconsistencyFactory->create(); + $metadata = $this->serializer->unserialize($reservation->getMetadata()); + $objectId = $metadata['object_id']; + $stockId = $reservation->getStockId(); + $key = $objectId . '-' . $stockId; + + if (!isset($this->items[$key])) { + $this->items[$key] = $this->saleableQuantityInconsistencyFactory->create(); } - $this->inconsistencies[$objectId]->setObjectId($objectId); + $this->items[$key]->setObjectId((int)$objectId); + $this->items[$key]->setStockId((int)$stockId); + $this->items[$key]->addItemQty($reservation->getSku(), $reservation->getQuantity()); + } + + /** + * @param OrderInterface $order + */ + public function addOrder(OrderInterface $order): void + { + $objectId = $order->getEntityId(); + $websiteId = (int)$order->getStore()->getWebsiteId(); + $stockId = (int)$this->stockByWebsiteIdResolver->execute((int)$websiteId)->getStockId(); + $key = $objectId . '-' . $stockId; - if ($order) { - $this->inconsistencies[$objectId]->setOrder($order); + if (!isset($this->items[$key])) { + $this->items[$key] = $this->saleableQuantityInconsistencyFactory->create(); } - $this->inconsistencies[$objectId]->addItemQty($sku, $quantity); + $this->items[$key]->setOrder($order); } /** * @return SaleableQuantityInconsistency[] */ - public function getInconsistencies(): array + public function getItems(): array { - return $this->inconsistencies; + return $this->items; } /** - * @param SaleableQuantityInconsistency[] $inconsistencies + * @param SaleableQuantityInconsistency[] $items */ - public function setInconsistencies(array $inconsistencies) + public function setItems(array $items): void { - $this->inconsistencies = $inconsistencies; + $this->items = $items; } } diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterCompleteOrders.php b/InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterCompleteOrders.php new file mode 100644 index 000000000000..fc2261244b55 --- /dev/null +++ b/InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterCompleteOrders.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; + +use Magento\InventoryReservationCli\Model\GetCompleteOrderStatusList; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; + +/** + * Remove all reservations with complete state + */ +class FilterCompleteOrders +{ + /** + * @var GetCompleteOrderStatusList + */ + private $getCompleteOrderStatusList; + + /** + * @param GetCompleteOrderStatusList $getCompleteOrderStatusList + */ + public function __construct( + GetCompleteOrderStatusList $getCompleteOrderStatusList + ) { + $this->getCompleteOrderStatusList = $getCompleteOrderStatusList; + } + + /** + * Remove all reservations with complete state + * + * @param SaleableQuantityInconsistency[] $inconsistencies + * @return SaleableQuantityInconsistency[] + */ + public function execute(array $inconsistencies): array + { + return array_filter( + $inconsistencies, + function (SaleableQuantityInconsistency $inconsistency) { + return in_array($inconsistency->getOrder()->getStatus(), $this->getCompleteOrderStatusList->execute()); + } + ); + } +} diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency/RemoveReservationsWithoutRelevantOrder.php b/InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterExistingOrders.php similarity index 55% rename from InventoryReservationCli/Model/SaleableQuantityInconsistency/RemoveReservationsWithoutRelevantOrder.php rename to InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterExistingOrders.php index a116231c8068..3a39c6fca932 100644 --- a/InventoryReservationCli/Model/SaleableQuantityInconsistency/RemoveReservationsWithoutRelevantOrder.php +++ b/InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterExistingOrders.php @@ -10,21 +10,23 @@ use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; /** - * Remove all entries without order + * Remove all reservations without matching order */ -class RemoveReservationsWithoutRelevantOrder +class FilterExistingOrders { /** - * Remove all entries without order - * @param Collector $collector + * Remove all reservations without matching order + * + * @param SaleableQuantityInconsistency[] $inconsistencies + * @return SaleableQuantityInconsistency[] */ - public function execute(Collector $collector): void + public function execute(array $inconsistencies): array { - $collector->setInconsistencies(array_filter( - $collector->getInconsistencies(), + return array_filter( + $inconsistencies, function (SaleableQuantityInconsistency $inconsistency) { return (bool)$inconsistency->getOrder(); } - )); + ); } } diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterIncompleteOrders.php b/InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterIncompleteOrders.php new file mode 100644 index 000000000000..16e4742f8541 --- /dev/null +++ b/InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterIncompleteOrders.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; + +use Magento\InventoryReservationCli\Model\GetCompleteOrderStatusList; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; + +/** + * Remove all reservations with incomplete state + */ +class FilterIncompleteOrders +{ + /** + * @var GetCompleteOrderStatusList + */ + private $getCompleteOrderStatusList; + + /** + * @param GetCompleteOrderStatusList $getCompleteOrderStatusList + */ + public function __construct( + GetCompleteOrderStatusList $getCompleteOrderStatusList + ) { + $this->getCompleteOrderStatusList = $getCompleteOrderStatusList; + } + + /** + * Remove all reservations with incomplete state + * + * @param SaleableQuantityInconsistency[] $inconsistencies + * @return SaleableQuantityInconsistency[] + */ + public function execute(array $inconsistencies): array + { + return array_filter( + $inconsistencies, + function (SaleableQuantityInconsistency $inconsistency) { + return !in_array($inconsistency->getOrder()->getStatus(), $this->getCompleteOrderStatusList->execute()); + } + ); + } +} diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency/RemoveResolvedReservations.php b/InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterUnresolvedReservations.php similarity index 66% rename from InventoryReservationCli/Model/SaleableQuantityInconsistency/RemoveResolvedReservations.php rename to InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterUnresolvedReservations.php index 3e361b796e31..3cf32377dfc2 100644 --- a/InventoryReservationCli/Model/SaleableQuantityInconsistency/RemoveResolvedReservations.php +++ b/InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterUnresolvedReservations.php @@ -12,23 +12,24 @@ /** * Remove all compensated reservations */ -class RemoveResolvedReservations +class FilterUnresolvedReservations { /** * Remove all compensated reservations - * @param Collector $collector + * @param SaleableQuantityInconsistency[] $inconsistencies + * @return SaleableQuantityInconsistency[] */ - public function execute(Collector $collector): void + public function execute(array $inconsistencies): array { - foreach ($collector->getInconsistencies() as $inconsistency) { + foreach ($inconsistencies as $inconsistency) { $inconsistency->setItems(array_filter($inconsistency->getItems())); } - $collector->setInconsistencies(array_filter( - $collector->getInconsistencies(), + return array_filter( + $inconsistencies, function (SaleableQuantityInconsistency $inconsistency) { return count($inconsistency->getItems()) > 0; } - )); + ); } } diff --git a/InventoryReservationCli/Test/Integration/Model/GetListMissingReservationsForIncompleteOrdersTest.php b/InventoryReservationCli/Test/Integration/Model/GetListMissingReservationsForIncompleteOrdersTest.php deleted file mode 100644 index 5f77017717f7..000000000000 --- a/InventoryReservationCli/Test/Integration/Model/GetListMissingReservationsForIncompleteOrdersTest.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryReservationCli\Test\Integration\Model; - -use Magento\InventoryReservationCli\Model\GetOrdersInNotFinalState; -use Magento\InventoryReservationCli\Model\GetOrdersWithMissingInitialReservations; -use PHPUnit\Framework\TestCase; -use Magento\TestFramework\Helper\Bootstrap; - -class GetListMissingReservationsForIncompleteOrdersTest extends TestCase -{ - /** - * @var GetOrdersWithMissingInitialReservations - */ - private $getOrdersWithMissingInitialReservations; - - /** - * @var GetOrdersInNotFinalState - */ - private $getOrdersInNotFinalState; - - /** - * Initialize test dependencies - */ - protected function setUp() - { - $this->getOrdersWithMissingInitialReservations - = Bootstrap::getObjectManager()->get(GetOrdersWithMissingInitialReservations::class); - $this->getOrdersInNotFinalState - = Bootstrap::getObjectManager()->get(GetOrdersInNotFinalState::class); - } - - /** - * @magentoDataFixture ../../../../app/code/Magento/InventoryReservationCli/Test/Integration/_fixtures/create_incomplete_order_with_reservation.php - */ - public function testShouldNotFindAnyInconsistency(): void - { - $incompleteOrders = $this->getOrdersInNotFinalState->execute(); - $missingReservations = $this->getOrdersWithMissingInitialReservations->execute($incompleteOrders); - self::assertSame([], $missingReservations); - } - - /** - * @magentoDataFixture Magento/Sales/_files/order.php - */ - public function testShouldFindOneInconsistency(): void - { - $incompleteOrders = $this->getOrdersInNotFinalState->execute(); - $missingReservations = $this->getOrdersWithMissingInitialReservations->execute($incompleteOrders); - self::assertCount(1, $missingReservations); - } -} diff --git a/InventoryReservationCli/Test/Integration/Model/GetListReservationsTotOrdersTest.php b/InventoryReservationCli/Test/Integration/Model/GetListReservationsTotOrdersTest.php deleted file mode 100644 index a0e5ed8debd5..000000000000 --- a/InventoryReservationCli/Test/Integration/Model/GetListReservationsTotOrdersTest.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryReservationCli\Test\Integration\Model; - -use Magento\InventoryReservationCli\Model\GetOrdersInFinalState; -use Magento\InventoryReservationCli\Model\GetOrdersWithNotCompensatedReservations; -use Magento\Sales\Api\Data\OrderInterface; -use PHPUnit\Framework\TestCase; -use Magento\TestFramework\Helper\Bootstrap; - -class GetListReservationsTotOrdersTest extends TestCase -{ - - /** - * @magentoDataFixture ../../../../app/code/Magento/InventoryReservationCli/Test/Integration/_fixtures/order_with_reservation.php - */ - public function testShouldNotFindAnyInconsistency(): void - { - $objectManager = Bootstrap::getObjectManager(); - - /** @var GetOrdersWithNotCompensatedReservations $getOrdersWithNotCompensatedReservations */ - $getOrdersWithNotCompensatedReservations = $objectManager->get(GetOrdersWithNotCompensatedReservations::class); - - /** @var GetOrdersInFinalState $getOrderInFinalState */ - $getOrderInFinalState = $objectManager->get(GetOrdersInFinalState::class); - - /** @var array $itemsNotCompensated */ - $itemsNotCompensated = $getOrdersWithNotCompensatedReservations->execute(); - - /** @var OrderInterface[] $orders */ - $orders = $getOrderInFinalState->execute(array_keys($itemsNotCompensated)); - - self::assertSame([], $orders); - } - - /** - * @magentoDataFixture Magento/Sales/_files/order_with_shipping_and_invoice.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryReservationCli/Test/Integration/_fixtures/broken_reservation.php - * @magentoDbIsolation enabled - */ - public function testShouldReturnOneReservationInconsistency(): void - { - $objectManager = Bootstrap::getObjectManager(); - - /** @var GetOrdersWithNotCompensatedReservations $getOrdersWithNotCompensatedReservations */ - $getOrdersWithNotCompensatedReservations = $objectManager->get(GetOrdersWithNotCompensatedReservations::class); - - /** @var GetOrdersInFinalState $getOrderInFinalState */ - $getOrderInFinalState = $objectManager->get(GetOrdersInFinalState::class); - - /** @var array $itemsNotCompensated */ - $itemsNotCompensated = $getOrdersWithNotCompensatedReservations->execute(); - - /** @var OrderInterface[] $orders */ - $orders = $getOrderInFinalState->execute(array_keys($itemsNotCompensated)); - - self::assertCount(1, $orders); - } -} diff --git a/InventoryReservationCli/Test/Integration/Model/GetSaleableQuantityInconsistenciesTest.php b/InventoryReservationCli/Test/Integration/Model/GetSaleableQuantityInconsistenciesTest.php new file mode 100644 index 000000000000..8a465130a0d0 --- /dev/null +++ b/InventoryReservationCli/Test/Integration/Model/GetSaleableQuantityInconsistenciesTest.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservationCli\Test\Integration\Model; + +use Magento\InventoryReservationCli\Model\GetSaleableQuantityInconsistencies; +use PHPUnit\Framework\TestCase; +use Magento\TestFramework\Helper\Bootstrap; + +class GetSaleableQuantityInconsistenciesTest extends TestCase +{ + /** + * @var GetSaleableQuantityInconsistencies + */ + private $getSaleableQuantityInconsistencies; + + /** + * Initialize test dependencies + */ + protected function setUp() + { + $this->getSaleableQuantityInconsistencies + = Bootstrap::getObjectManager()->get(GetSaleableQuantityInconsistencies::class); + } + + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryReservationCli/Test/Integration/_fixtures/create_incomplete_order_with_reservation.php + * @throws \Magento\Framework\Validation\ValidationException + */ + public function testIncompleteOrderWithExistingReservation(): void + { + $inconsistencies = $this->getSaleableQuantityInconsistencies->execute(); + self::assertSame([], $inconsistencies); + } + + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryReservationCli/Test/Integration/_fixtures/create_incomplete_order_without_reservation.php + * @throws \Magento\Framework\Validation\ValidationException + */ + public function testIncompleteOrderWithoutReservation(): void + { + $inconsistencies = $this->getSaleableQuantityInconsistencies->execute(); + self::assertCount(1, $inconsistencies); + } + + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryReservationCli/Test/Integration/_fixtures/order_with_reservation.php + * @throws \Magento\Framework\Validation\ValidationException + */ + public function testCompletedOrderWithReservations(): void + { + $inconsistencies = $this->getSaleableQuantityInconsistencies->execute(); + self::assertSame([], $inconsistencies); + } + + /** + * @magentoDataFixture Magento/Sales/_files/order_with_shipping_and_invoice.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryReservationCli/Test/Integration/_fixtures/broken_reservation.php + * @throws \Magento\Framework\Validation\ValidationException + */ + public function testCompletedOrderWithMissingReservations(): void + { + $inconsistencies = $this->getSaleableQuantityInconsistencies->execute(); + self::assertCount(1, $inconsistencies); + } +} diff --git a/InventoryReservationCli/Test/Integration/_fixtures/create_incomplete_order_without_reservation.php b/InventoryReservationCli/Test/Integration/_fixtures/create_incomplete_order_without_reservation.php new file mode 100644 index 000000000000..08ed72584637 --- /dev/null +++ b/InventoryReservationCli/Test/Integration/_fixtures/create_incomplete_order_without_reservation.php @@ -0,0 +1,71 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Address as OrderAddress; +use Magento\Sales\Model\Order\Item as OrderItem; +use Magento\Sales\Model\Order\Payment; +use Magento\Store\Model\StoreManagerInterface; + +require __DIR__ . '/../../../../../../../dev/tests/integration/testsuite/Magento/Sales/_files/default_rollback.php'; +require __DIR__ . '/../../../../../../../dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple.php'; +/** @var \Magento\Catalog\Model\Product $product */ + +$addressData = include __DIR__ . '/../../../../../../../dev/tests/integration/testsuite/Magento/Sales/_files/address_data.php'; + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +$billingAddress = $objectManager->create(OrderAddress::class, ['data' => $addressData]); +$billingAddress->setAddressType('billing'); + +$shippingAddress = clone $billingAddress; +$shippingAddress->setId(null)->setAddressType('shipping'); + +/** @var Payment $payment */ +$payment = $objectManager->create(Payment::class); +$payment->setMethod('checkmo') + ->setAdditionalInformation('last_trans_id', '11122') + ->setAdditionalInformation( + 'metadata', + [ + 'type' => 'free', + 'fraudulent' => false, + ] + ); + +/** @var OrderItem $orderItem */ +$orderItem = $objectManager->create(OrderItem::class); +$orderItem->setProductId($product->getId()) + ->setQtyOrdered(2) + ->setSku($product->getSku()) + ->setBasePrice($product->getPrice()) + ->setPrice($product->getPrice()) + ->setRowTotal($product->getPrice()) + ->setProductType('simple') + ->setName($product->getName()); + +/** @var Order $order */ +$order = $objectManager->create(Order::class); +$order->setIncrementId('100000001') + ->setState(Order::STATE_PROCESSING) + ->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_PROCESSING)) + ->setSubtotal(100) + ->setGrandTotal(100) + ->setBaseSubtotal(100) + ->setBaseGrandTotal(100) + ->setCustomerIsGuest(true) + ->setCustomerEmail('customer@null.com') + ->setBillingAddress($billingAddress) + ->setShippingAddress($shippingAddress) + ->setStoreId($objectManager->get(StoreManagerInterface::class)->getStore()->getId()) + ->addItem($orderItem) + ->setPayment($payment); + +/** @var OrderRepositoryInterface $orderRepository */ +$orderRepository = $objectManager->create(OrderRepositoryInterface::class); +$orderRepository->save($order); diff --git a/InventoryReservationCli/etc/di.xml b/InventoryReservationCli/etc/di.xml index 3521731e511a..bbdc73ebbe35 100644 --- a/InventoryReservationCli/etc/di.xml +++ b/InventoryReservationCli/etc/di.xml @@ -10,7 +10,10 @@ <arguments> <argument name="commands" xsi:type="array"> <item name="inventory_saleable_quantity_inconsistency" xsi:type="object"> - Magento\InventoryReservationCli\Command\ShowSaleableQuantityInconsistencies + Magento\InventoryReservationCli\Command\ShowInconsistencies + </item> + <item name="inventory_saleable_quantity_compensations" xsi:type="object"> + Magento\InventoryReservationCli\Command\CreateCompensations </item> </argument> </arguments> From c0f71adb0b7ac24cc55629f5a342bb04827909c8 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Tue, 16 Apr 2019 13:45:25 +0300 Subject: [PATCH 152/231] MSI-2043: fixed back button on shipping step test --- .../Block/Shipment/BackButtonUrlOnNewShipmentPagePlugin.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/InventoryShippingAdminUi/Plugin/Sales/Block/Shipment/BackButtonUrlOnNewShipmentPagePlugin.php b/InventoryShippingAdminUi/Plugin/Sales/Block/Shipment/BackButtonUrlOnNewShipmentPagePlugin.php index ca4a117718ad..86fdcff46781 100644 --- a/InventoryShippingAdminUi/Plugin/Sales/Block/Shipment/BackButtonUrlOnNewShipmentPagePlugin.php +++ b/InventoryShippingAdminUi/Plugin/Sales/Block/Shipment/BackButtonUrlOnNewShipmentPagePlugin.php @@ -50,12 +50,11 @@ public function __construct( public function afterGetBackUrl(Create $subject, $result) { $shipment = $subject->getShipment(); - $order = $shipment->getOrder(); - if (empty($shipment) || !$this->isOrderSourceManageable->execute($order)) { + if (empty($shipment) || !$this->isOrderSourceManageable->execute($shipment->getOrder())) { return $result; } - $websiteId = (int)$order->getStore()->getWebsiteId(); + $websiteId = (int)$shipment->getOrder()->getStore()->getWebsiteId(); if ($this->isWebsiteInMultiSourceMode->execute($websiteId)) { return $subject->getUrl( 'inventoryshipping/SourceSelection/index', From e5ea4811c6708411e28cce79e2c7f909b121f45f Mon Sep 17 00:00:00 2001 From: Vadim Justus <v.justus@techdivision.com> Date: Tue, 16 Apr 2019 13:47:52 +0200 Subject: [PATCH 153/231] magento-engcom/msi#2170: Remove not required use statement --- InventoryReservationCli/Command/CreateCompensations.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/InventoryReservationCli/Command/CreateCompensations.php b/InventoryReservationCli/Command/CreateCompensations.php index 0a12cf41d137..0a92a24132f7 100644 --- a/InventoryReservationCli/Command/CreateCompensations.php +++ b/InventoryReservationCli/Command/CreateCompensations.php @@ -17,7 +17,6 @@ use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterIncompleteOrders; use Magento\InventoryReservationsApi\Model\AppendReservationsInterface; use Magento\InventoryReservationsApi\Model\ReservationInterface; -use Magento\Sales\Model\Order; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -124,7 +123,6 @@ private function prettyOutput(OutputInterface $output, array $compensations): vo { $output->writeln('<info>Following reservations were created:</info>'); - /** @var Order $order */ foreach ($compensations as $reservation) { $output->writeln( sprintf( @@ -145,7 +143,6 @@ private function prettyOutput(OutputInterface $output, array $compensations): vo */ private function rawOutput(OutputInterface $output, array $compensations): void { - /** @var Order $order */ foreach ($compensations as $reservation) { $output->writeln( sprintf( From a257bdb496c48d908fd62772e88fbcf7a389d729 Mon Sep 17 00:00:00 2001 From: Vadim Justus <v.justus@techdivision.com> Date: Tue, 16 Apr 2019 17:06:07 +0200 Subject: [PATCH 154/231] magento-engcom/msi#2170: Fix static test issues (missing depencies) --- InventoryReservationCli/composer.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/InventoryReservationCli/composer.json b/InventoryReservationCli/composer.json index 9a93ee19e42b..58766108702d 100644 --- a/InventoryReservationCli/composer.json +++ b/InventoryReservationCli/composer.json @@ -4,7 +4,9 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", - "magento/module-sales": "*" + "magento/module-sales": "*", + "magento/module-inventory-reservations-api": "*", + "magento/module-inventory-sales-api": "*" }, "type": "magento2-module", "license": [ From 66094d7f7df79c787aa4c25ef9fcc5871456d7ca Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Tue, 16 Apr 2019 20:14:42 +0300 Subject: [PATCH 155/231] MSI-2043: removed object manager from plugin and fixed class annotation --- .../Shipment/BackButtonUrlOnNewShipmentPagePlugin.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/InventoryShippingAdminUi/Plugin/Sales/Block/Shipment/BackButtonUrlOnNewShipmentPagePlugin.php b/InventoryShippingAdminUi/Plugin/Sales/Block/Shipment/BackButtonUrlOnNewShipmentPagePlugin.php index 86fdcff46781..2291f5a6d59d 100644 --- a/InventoryShippingAdminUi/Plugin/Sales/Block/Shipment/BackButtonUrlOnNewShipmentPagePlugin.php +++ b/InventoryShippingAdminUi/Plugin/Sales/Block/Shipment/BackButtonUrlOnNewShipmentPagePlugin.php @@ -7,13 +7,12 @@ namespace Magento\InventoryShippingAdminUi\Plugin\Sales\Block\Shipment; -use Magento\Framework\App\ObjectManager; use Magento\InventoryShippingAdminUi\Model\IsOrderSourceManageable; use Magento\Shipping\Block\Adminhtml\Create; use Magento\InventoryShippingAdminUi\Model\IsWebsiteInMultiSourceMode; /** - * Class BackButtonUrlOnNewShipmentPagePlugin + * Modify back button URL on the shipment page in multi source mode */ class BackButtonUrlOnNewShipmentPagePlugin { @@ -33,11 +32,10 @@ class BackButtonUrlOnNewShipmentPagePlugin */ public function __construct( IsWebsiteInMultiSourceMode $isWebsiteInMultiSourceMode, - IsOrderSourceManageable $isOrderSourceManageable = null + IsOrderSourceManageable $isOrderSourceManageable ) { $this->isWebsiteInMultiSourceMode = $isWebsiteInMultiSourceMode; - $this->isOrderSourceManageable = $isOrderSourceManageable ?? - ObjectManager::getInstance()->get(IsOrderSourceManageable::class); + $this->isOrderSourceManageable = $isOrderSourceManageable; } /** From b81dc36479ebd05528c98aacc6f56d5e3b4fb398 Mon Sep 17 00:00:00 2001 From: Tetiana Blindaruk <t.blindaruk@gmail.com> Date: Sat, 13 Apr 2019 18:38:14 +0300 Subject: [PATCH 156/231] [store pickup] code improvement Co-Authored-By: Maksym Novik <novik.kor@gmail.com> --- .../Model/Order/IsFulfillable.php | 23 +++++++---- .../Adminhtml/Order/View/ReadyForPickup.php | 34 +++++++-------- .../Adminhtml/Order/NotifyPickup.php | 41 +++++++++---------- .../Model/IsDisplayReadyForPickupButton.php | 7 +++- 4 files changed, 57 insertions(+), 48 deletions(-) diff --git a/InventoryInStorePickup/Model/Order/IsFulfillable.php b/InventoryInStorePickup/Model/Order/IsFulfillable.php index 0bf3fce86e84..7053817c188f 100644 --- a/InventoryInStorePickup/Model/Order/IsFulfillable.php +++ b/InventoryInStorePickup/Model/Order/IsFulfillable.php @@ -47,18 +47,23 @@ public function __construct( */ public function execute(OrderInterface $order): bool { - if ($order->getExtensionAttributes() && $order->getExtensionAttributes()->getPickupLocationCode()) { - $sourceCode = $order->getExtensionAttributes()->getPickupLocationCode(); - foreach ($order->getItems() as $item) { - if (!$this->isItemFulfillable($item->getSku(), $sourceCode, (float)$item->getQtyOrdered())) { - return false; - } - } + $extensionAttributes = $order->getExtensionAttributes(); + if (!$extensionAttributes) { + return false; + } - return true; + $sourceCode = $extensionAttributes->getPickupLocationCode(); + if (!$sourceCode) { + return false; } - return false; + foreach ($order->getItems() as $item) { + if (!$this->isItemFulfillable($item->getSku(), $sourceCode, (float)$item->getQtyOrdered())) { + return false; + } + } + + return true; } /** diff --git a/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php b/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php index 80d20a828148..15656b9002e8 100644 --- a/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php +++ b/InventoryInStorePickupAdminUi/Block/Adminhtml/Order/View/ReadyForPickup.php @@ -66,23 +66,25 @@ protected function _construct() $this->_controller = 'adminhtml_order'; $this->_mode = 'view'; - if ($this->isDisplayButton()) { - $message = __( - 'Are you sure you want to notify the customer that order is ready for pickup and create shipment?' - ); - $this->addButton( - 'ready_for_pickup', - [ - 'label' => __('Notify Order is Ready for Pickup'), - 'class' => 'action-default ready-for-pickup', - 'onclick' => sprintf( - "confirmSetLocation('%s', '%s')", - $message, - $this->viewBlock->getUrl('sales/*/notifyPickup') - ) - ] - ); + if (!$this->isDisplayButton()) { + return; } + + $message = __( + 'Are you sure you want to notify the customer that order is ready for pickup and create shipment?' + ); + $this->addButton( + 'ready_for_pickup', + [ + 'label' => __('Notify Order is Ready for Pickup'), + 'class' => 'action-default ready-for-pickup', + 'onclick' => sprintf( + "confirmSetLocation('%s', '%s')", + $message, + $this->viewBlock->getUrl('sales/*/notifyPickup') + ) + ] + ); } /** diff --git a/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php b/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php index 8274df1d1180..7adbace31397 100644 --- a/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php +++ b/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php @@ -69,34 +69,31 @@ public function __construct( * Notify customer by email * * @return ResultInterface - * @throws InputException - * @throws LocalizedException - * @throws NoSuchEntityException */ public function execute(): ResultInterface { - $order = $this->initOrder(); - - if ($order) { - try { - $this->notifyOrderIsReadyForPickup->execute((int)$order->getEntityId()); - $this->messageManager->addSuccessMessage(__('The customer have been notified and shipment created.')); - } catch (LocalizedException $e) { - $this->messageManager->addErrorMessage($e->getMessage()); - } catch (Exception $e) { - $this->messageManager->addErrorMessage(__('We can\'t notify the customer right now.')); - $this->logger->critical($e); - } + try { + $order = $this->initOrder(); + } catch (LocalizedException $e) { + return $this->resultRedirectFactory->create()->setPath('sales/*/'); + } - return $this->resultRedirectFactory->create()->setPath( - 'sales/order/view', - [ - 'order_id' => $order->getEntityId() - ] - ); + try { + $this->notifyOrderIsReadyForPickup->execute((int)$order->getEntityId()); + $this->messageManager->addSuccessMessage(__('The customer have been notified and shipment created.')); + } catch (LocalizedException $e) { + $this->messageManager->addErrorMessage($e->getMessage()); + } catch (Exception $e) { + $this->messageManager->addErrorMessage(__('We can\'t notify the customer right now.')); + $this->logger->critical($e); } - return $this->resultRedirectFactory->create()->setPath('sales/*/'); + return $this->resultRedirectFactory->create()->setPath( + 'sales/order/view', + [ + 'order_id' => $order->getEntityId(), + ] + ); } /** diff --git a/InventoryInStorePickupAdminUi/Model/IsDisplayReadyForPickupButton.php b/InventoryInStorePickupAdminUi/Model/IsDisplayReadyForPickupButton.php index 9adf9d602183..9538f16f3c10 100644 --- a/InventoryInStorePickupAdminUi/Model/IsDisplayReadyForPickupButton.php +++ b/InventoryInStorePickupAdminUi/Model/IsDisplayReadyForPickupButton.php @@ -21,7 +21,12 @@ class IsDisplayReadyForPickupButton */ public function execute(Order $order): bool { - return $order->getExtensionAttributes()->getPickupLocationCode() + $extensionAttributes = $order->getExtensionAttributes(); + if ($extensionAttributes === null) { + return false; + } + + return $extensionAttributes->getPickupLocationCode() && $order->canShip(); } } From 77bef85fd1677a7f400f1f1aea5a0fd9136410a4 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Tue, 16 Apr 2019 14:41:19 -0500 Subject: [PATCH 157/231] Update MFTF tests according Manual HIP-TEST --- .../Test/Mftf/Data/MsiProductData.xml | 11 ++ ...leProductOnCustomStockFromHomepageTest.xml | 126 +++--------------- ...eProductOnDefaultStockFromHomepageTest.xml | 114 ++-------------- 3 files changed, 45 insertions(+), 206 deletions(-) diff --git a/InventoryAdminUi/Test/Mftf/Data/MsiProductData.xml b/InventoryAdminUi/Test/Mftf/Data/MsiProductData.xml index dd342c1b585e..026c2cef478d 100644 --- a/InventoryAdminUi/Test/Mftf/Data/MsiProductData.xml +++ b/InventoryAdminUi/Test/Mftf/Data/MsiProductData.xml @@ -53,4 +53,15 @@ <data key="attribute_set_id">4</data> <data key="visibility">4</data> </entity> + <entity name="MsiDownloadableProduct" type="product"> + <data key="name" unique="suffix">Downloadable MSI Product </data> + <data key="sku" unique="suffix">Downloadable-MSI-Product-</data> + <data key="type_id">downloadable</data> + <data key="price">100</data> + <data key="quantity">100</data> + <data key="urlKey" unique="suffix">Downloadable-MSI-Product-</data> + <data key="attribute_set_id">4</data> + <data key="visibility">4</data> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> + </entity> </entities> diff --git a/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnCustomStockFromHomepageTest.xml b/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnCustomStockFromHomepageTest.xml index 89b88bb92201..f14c53721bec 100644 --- a/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnCustomStockFromHomepageTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnCustomStockFromHomepageTest.xml @@ -20,102 +20,28 @@ </annotations> <before> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> <createData entity="FullSource1" stepKey="createSource"/> <createData entity="BasicMsiStockWithMainWebsite1" stepKey="createStock"/> - <createData entity="SimpleSubCategory" stepKey="createCategory"/> - <createData entity="SourceStockLinked1" stepKey="linkSourceStock"> <requiredEntity createDataKey="createStock"/> <requiredEntity createDataKey="createSource"/> </createData> - - <magentoCLI command="indexer:reindex" stepKey="magentoCli"/> - + <createData entity="MsiDownloadableProduct" stepKey="downloadableProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="downloadableLink1" stepKey="addDownloadableLink"> + <requiredEntity createDataKey="downloadableProduct"/> + </createData> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - - <!-- Create Configurable Product --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToAdminProductGrid"/> - <waitForPageLoad time="30" stepKey="waitForProductGridLoad"/> - <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickOnAddProductToggle"/> - <click selector="{{AdminProductGridActionSection.addTypeProduct('configurable')}}" - stepKey="addConfigurableProduct"/> - <waitForPageLoad time="30" stepKey="waitForConfigurableProductNewPageLoad"/> - - <fillField userInput="{{ConfigurableMsiProduct.name}}" selector="{{AdminProductFormSection.productName}}" - stepKey="fillProductName"/> - <fillField userInput="{{ConfigurableMsiProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" - stepKey="fillProductPrice"/> - <fillField userInput="{{ConfigurableMsiProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" - stepKey="fillProductSku"/> - <fillField userInput="{{ConfigurableMsiProduct.quantity}}" - selector="{{AdminConfigurableProductFormSection.productQuantity}}" - stepKey="fillProductQuantity"/> - <fillField userInput="{{ConfigurableMsiProduct.weight}}" - selector="{{AdminConfigurableProductFormSection.productWeight}}" stepKey="fillProductWeight"/> - - <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" - parameterArray="[$$createCategory.name$$]" stepKey="searchAndSelectCategory"/> - <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" - stepKey="clickOnTheCreateConfigurationsButton"/> - <waitForElementVisible selector="{{AdminConfigurableProductSelectAttributesSlideOut.grid}}" time="30" - stepKey="waitForGridPresents"/> - - <click selector="{{AdminGridRow.checkboxByValue('color')}}" stepKey="selectColorAttribute"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="navigateToSecondStep"/> - - <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="addNewColorWhite"/> - <fillField userInput="{{colorProductAttribute1.name}}" - selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="setNameWhite"/> - <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="saveWhiteColor"/> - - <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="addNewColorRed"/> - <fillField userInput="{{colorProductAttribute2.name}}" - selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="setNameRed"/> - <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="saveRedColor"/> - - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="navigateToThirdStep"/> - - <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" - stepKey="clickOnApplySingleQuantityToEachSku"/> - - <click selector="{{AdminConfigurableProductAssignSourcesSlideOut.assignSources}}" - stepKey="openSelectSourcesModalWindow"/> - <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" - dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" - stepKey="clearSourcesFilter"/> - <actionGroup ref="searchAdminDataGridByKeyword" stepKey="searchCustomByNameForAssignment"> - <argument name="keyword" value="$$createSource.source[name]$$"/> + <amOnPage url="{{AdminProductEditPage.url($$downloadableProduct.id$$)}}" stepKey="openProductEditPage" /> + <actionGroup ref="AssignSourceToProductActionGroup" stepKey="assignCustomSource"> + <argument name="sourceCode" value="$$createStock.source_code$$" /> </actionGroup> - <click selector="{{AdminGridRow.checkboxByValue($$createSource.source[name]$$)}}" - stepKey="selectDefaultSource"/> - <click selector="{{AdminConfigurableProductAssignSourcesSlideOut.done}}" stepKey="doneAssignSources"/> - <fillField selector="{{AdminConfigurableProductAssignSourcesSlideOut.quantityPerSource('0')}}" - userInput="100" stepKey="fillQuantityForCustomSource"/> - - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="navigateToFourthStep"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" - stepKey="doneGeneratingConfigurableVariations"/> - - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveConfigurableProduct"/> - <conditionalClick selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" - dependentSelector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" visible="true" - stepKey="confirmDefaultAttributeSetForConfigurableProduct"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="checkProductSavedMessage"/> - - <seeNumberOfElements selector="{{AdminProductFormConfigurationsSection.currentVariationsRows}}" - userInput="2" stepKey="checkConfigurableMatrix"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" - userInput="{{colorProductAttribute1.name}}" stepKey="checkWhiteAttributeVariationName"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" - userInput="{{colorProductAttribute2.name}}" stepKey="checkRedAttributeVariationName"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsSkuCells}}" - userInput="{{colorProductAttribute1.name}}" stepKey="checkWhiteAttributeVariationSku"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsSkuCells}}" - userInput="{{colorProductAttribute2.name}}" stepKey="checkRedAttributeVariationSku"/> - <see selector="{{AdminConfigurableProductFormSection.currentVariationsQuantityCells}}" userInput="100" - stepKey="checkQtyIsCorrectForCustomSource"/> - - <actionGroup ref="AdminFormSaveAndClose" stepKey="saveAndClose"/> + <!--<scrollTo stepKey="scrollToSources" selector="[data-index='sources']" />--> + <fillField selector="{{AdminProductSourcesGrid.rowQty('0')}}" userInput="100" stepKey="fillDefaultSourceQtyField"/> + <fillField selector="{{AdminProductSourcesGrid.rowQty('1')}}" userInput="100" stepKey="fillCustomSourceQtyField"/> + <actionGroup ref="AdminFormSaveAndClose" stepKey="saveAndCloseProduct"/> </before> <after> <!-- Assign Sales Channel to Default Stock --> @@ -130,31 +56,19 @@ <selectOption selector="{{AdminEditStockSalesChannelsSection.websites}}" userInput="Main Website" stepKey="selectWebsiteAsSalesChannel"/> <click selector="{{AdminGridMainControls.saveAndContinue}}" stepKey="saveDefaultStock"/> - + <actionGroup ref="DisableSourceActionGroup" stepKey="disableSource"> <argument name="sourceCode" value="$$createSource.source[source_code]$$"/> </actionGroup> <actionGroup ref="logout" stepKey="logoutOfAdmin"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="downloadableProduct" stepKey="deleteProduct"/> </after> - <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="assertProductInStorefront"> - <argument name="product" value="ConfigurableMsiProduct"/> - </actionGroup> - - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategory"/> - <waitForPageLoad time="30" stepKey="waitForCategoryPageLoad"/> - <click selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo(ConfigurableMsiProduct.name)}}" - stepKey="openProductPage"/> - <waitForAjaxLoad stepKey="waitForImageLoader"/> - <selectOption selector="{{StorefrontConfigurableProductPage.productAttributeDropDown}}" - userInput="{{colorProductAttribute1.name}}" stepKey="selectWhiteVariation"/> - <seeOptionIsSelected selector="{{StorefrontConfigurableProductPage.productAttributeDropDown}}" - userInput="{{colorProductAttribute1.name}}" stepKey="checkWhiteVariationIsSelected"/> - <fillField selector="{{StorefrontProductPageSection.qtyInput}}" userInput="5" stepKey="fillQuantity"/> + <amOnPage url="{{StorefrontProductPage.url($$downloadableProduct.custom_attributes[url_key]$$)}}" stepKey="openProductPage"/> + <waitForPageLoad stepKey="waitForImageLoader"/> <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="addToCart"/> - <waitForElementVisible selector="{{StorefrontProductPageSection.successMsg}}" time="30" - stepKey="waitForProductAdded"/> + <waitForElementVisible selector="{{StorefrontProductPageSection.successMsg}}" time="30" stepKey="waitForProductAdded"/> <!-- Place Order --> <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> @@ -190,7 +104,7 @@ <actionGroup ref="AdminGoToProductGridFilterResultsByInput" stepKey="goToProductGridFilterResultsByInput"> <argument name="filter_selector" value="AdminProductGridFilterSection.skuFilter"/> - <argument name="filter_value" value="ConfigurableMsiProduct.sku"/> + <argument name="filter_value" value="$$downloadableProduct.product[sku]$$"/> </actionGroup> <see selector="{{AdminGridRow.rowOne}}" userInput="{{colorProductAttribute1.name}}" diff --git a/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnDefaultStockFromHomepageTest.xml b/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnDefaultStockFromHomepageTest.xml index 76f982c946d7..d5844cb6e06f 100644 --- a/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnDefaultStockFromHomepageTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnDefaultStockFromHomepageTest.xml @@ -44,89 +44,15 @@ stepKey="selectWebsiteAsSalesChannel"/> <click selector="{{AdminGridMainControls.saveAndContinue}}" stepKey="saveDefaultStock"/> - <!-- Create Configurable Product --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToAdminProductGrid"/> - <waitForPageLoad time="30" stepKey="waitForProductGridLoad"/> - <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickOnAddProductToggle"/> - <click selector="{{AdminProductGridActionSection.addTypeProduct('configurable')}}" - stepKey="addConfigurableProduct"/> - <waitForPageLoad time="30" stepKey="waitForConfigurableProductNewPageLoad"/> - - <fillField userInput="{{ConfigurableMsiProduct.name}}" selector="{{AdminProductFormSection.productName}}" - stepKey="fillProductName"/> - <fillField userInput="{{ConfigurableMsiProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" - stepKey="fillProductPrice"/> - <fillField userInput="{{ConfigurableMsiProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" - stepKey="fillProductSku"/> - <fillField userInput="{{ConfigurableMsiProduct.quantity}}" - selector="{{AdminConfigurableProductFormSection.productQuantity}}" - stepKey="fillProductQuantity"/> - <fillField userInput="{{ConfigurableMsiProduct.weight}}" - selector="{{AdminConfigurableProductFormSection.productWeight}}" stepKey="fillProductWeight"/> - - <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" - parameterArray="[$$createCategory.name$$]" stepKey="searchAndSelectCategory"/> - <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" - stepKey="clickOnTheCreateConfigurationsButton"/> - <waitForElementVisible selector="{{AdminConfigurableProductSelectAttributesSlideOut.grid}}" time="30" - stepKey="waitForGridPresents"/> - - <click selector="{{AdminGridRow.checkboxByValue('color')}}" stepKey="selectColorAttribute"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="navigateToSecondStep"/> - - <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="addNewColorWhite"/> - <fillField userInput="{{colorProductAttribute1.name}}" - selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="setNameWhite"/> - <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="saveWhiteColor"/> - - <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="addNewColorRed"/> - <fillField userInput="{{colorProductAttribute2.name}}" - selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="setNameRed"/> - <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="saveRedColor"/> - - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="navigateToThirdStep"/> - - <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" - stepKey="clickOnApplySingleQuantityToEachSku"/> - - <click selector="{{AdminConfigurableProductAssignSourcesSlideOut.assignSources}}" - stepKey="openSelectSourcesModalWindow"/> - <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" - dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" - stepKey="clearSourcesFilter"/> - <actionGroup ref="searchAdminDataGridByKeyword" stepKey="searchDefaultByNameForAssignment"> - <argument name="keyword" value="_defaultSource.name"/> - </actionGroup> - <click selector="{{AdminGridRow.checkboxByValue(_defaultSource.name)}}" - stepKey="selectDefaultSource"/> - <click selector="{{AdminConfigurableProductAssignSourcesSlideOut.done}}" stepKey="doneAssignSources"/> - <fillField selector="{{AdminConfigurableProductAssignSourcesSlideOut.quantityPerSource('0')}}" - userInput="100" stepKey="fillQuantityForCustomSource"/> - - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="navigateToFourthStep"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" - stepKey="doneGeneratingConfigurableVariations"/> - - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveConfigurableProduct"/> - <conditionalClick selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" - dependentSelector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" visible="true" - stepKey="confirmDefaultAttributeSetForConfigurableProduct"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="checkProductSavedMessage"/> - - <seeNumberOfElements selector="{{AdminProductFormConfigurationsSection.currentVariationsRows}}" - userInput="2" stepKey="checkConfigurableMatrix"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" - userInput="{{colorProductAttribute1.name}}" stepKey="checkWhiteAttributeVariationName"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" - userInput="{{colorProductAttribute2.name}}" stepKey="checkRedAttributeVariationName"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsSkuCells}}" - userInput="{{colorProductAttribute1.name}}" stepKey="checkWhiteAttributeVariationSku"/> - <see selector="{{AdminProductFormConfigurationsSection.currentVariationsSkuCells}}" - userInput="{{colorProductAttribute2.name}}" stepKey="checkRedAttributeVariationSku"/> - <see selector="{{AdminConfigurableProductFormSection.currentVariationsQuantityCells}}" userInput="100" - stepKey="checkQtyIsCorrectForCustomSource"/> - - <actionGroup ref="AdminFormSaveAndClose" stepKey="saveAndClose"/> + <createData entity="MsiDownloadableProduct" stepKey="downloadableProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="downloadableLink1" stepKey="addDownloadableLink"> + <requiredEntity createDataKey="downloadableProduct"/> + </createData> + <amOnPage url="{{AdminProductEditPage.url($$downloadableProduct.id$$)}}" stepKey="openProductEditPage" /> + <fillField selector="{{AdminProductSourcesGrid.rowQty('0')}}" userInput="100" stepKey="fillDefaultSourceQtyField"/> + <actionGroup ref="AdminFormSaveAndClose" stepKey="saveAndCloseProduct"/> </before> <after> <actionGroup ref="DisableSourceActionGroup" stepKey="disableSource"> @@ -134,25 +60,13 @@ </actionGroup> <actionGroup ref="logout" stepKey="logoutOfAdmin"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="downloadableProduct" stepKey="deleteProduct"/> </after> - <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="assertProductInStorefront"> - <argument name="product" value="ConfigurableMsiProduct"/> - </actionGroup> - - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategory"/> - <waitForPageLoad time="30" stepKey="waitForCategoryPageLoad"/> - <click selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo(ConfigurableMsiProduct.name)}}" - stepKey="openProductPage"/> - <waitForAjaxLoad stepKey="waitForImageLoader"/> - <selectOption selector="{{StorefrontConfigurableProductPage.productAttributeDropDown}}" - userInput="{{colorProductAttribute1.name}}" stepKey="selectWhiteVariation"/> - <seeOptionIsSelected selector="{{StorefrontConfigurableProductPage.productAttributeDropDown}}" - userInput="{{colorProductAttribute1.name}}" stepKey="checkWhiteVariationIsSelected"/> - <fillField selector="{{StorefrontProductPageSection.qtyInput}}" userInput="5" stepKey="fillQuantity"/> + <amOnPage url="{{StorefrontProductPage.url($$downloadableProduct.custom_attributes[url_key]$$)}}" stepKey="openProductPage"/> + <waitForPageLoad stepKey="waitForImageLoader"/> <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="addToCart"/> - <waitForElementVisible selector="{{StorefrontProductPageSection.successMsg}}" time="30" - stepKey="waitForProductAdded"/> + <waitForElementVisible selector="{{StorefrontProductPageSection.successMsg}}" time="30" stepKey="waitForProductAdded"/> <!-- Place Order --> <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> @@ -188,7 +102,7 @@ <actionGroup ref="AdminGoToProductGridFilterResultsByInput" stepKey="goToProductGridFilterResultsByInput"> <argument name="filter_selector" value="AdminProductGridFilterSection.skuFilter"/> - <argument name="filter_value" value="ConfigurableMsiProduct.sku"/> + <argument name="filter_value" value="$$downloadableProduct.product[sku]$$"/> </actionGroup> <see selector="{{AdminGridRow.rowOne}}" userInput="{{colorProductAttribute1.name}}" From 8f5e62cbf61b4dfdae67e64374a61dceb9057922 Mon Sep 17 00:00:00 2001 From: Tetiana Blindaruk <t.blindaruk@gmail.com> Date: Wed, 17 Apr 2019 00:54:12 +0300 Subject: [PATCH 158/231] [store pickup] code improvement Clean up exception in `initOrder` --- .../Controller/Adminhtml/Order/NotifyPickup.php | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php b/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php index 7adbace31397..90d41d705389 100644 --- a/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php +++ b/InventoryInStorePickupAdminUi/Controller/Adminhtml/Order/NotifyPickup.php @@ -101,7 +101,6 @@ public function execute(): ResultInterface * * @return OrderInterface * @throws InputException - * @throws LocalizedException * @throws NoSuchEntityException * @see \Magento\Sales\Controller\Adminhtml\Order::_initOrder */ @@ -110,11 +109,9 @@ private function initOrder(): OrderInterface $id = $this->getRequest()->getParam('order_id'); try { $order = $this->orderRepository->get($id); - } catch (LocalizedException $e) { - if ($e instanceof NoSuchEntityException || $e instanceof InputException) { - $this->messageManager->addErrorMessage(__('This order no longer exists.')); - $this->_actionFlag->set('', self::FLAG_NO_DISPATCH, true); - } + } catch (NoSuchEntityException|InputException $e) { + $this->messageManager->addErrorMessage(__('This order no longer exists.')); + $this->_actionFlag->set('', self::FLAG_NO_DISPATCH, true); throw $e; } From d9baa4135f000aefaee4bb8ad47ffad00196e2a1 Mon Sep 17 00:00:00 2001 From: Tetiana Blindaruk <t.blindaruk@gmail.com> Date: Wed, 17 Apr 2019 01:14:29 +0300 Subject: [PATCH 159/231] [store pickup] code improvement - update app/code/Magento/InventoryInStorePickupAdminUi/Ui/Component/Listing/Column/IsPickupLocationActive.php --- .../Listing/Column/IsPickupLocationActive.php | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/InventoryInStorePickupAdminUi/Ui/Component/Listing/Column/IsPickupLocationActive.php b/InventoryInStorePickupAdminUi/Ui/Component/Listing/Column/IsPickupLocationActive.php index 03d504fe47df..4a608b739b57 100644 --- a/InventoryInStorePickupAdminUi/Ui/Component/Listing/Column/IsPickupLocationActive.php +++ b/InventoryInStorePickupAdminUi/Ui/Component/Listing/Column/IsPickupLocationActive.php @@ -21,14 +21,26 @@ class IsPickupLocationActive extends Column */ public function prepareDataSource(array $dataSource):array { - if (isset($dataSource['data']['totalRecords']) - && $dataSource['data']['totalRecords'] > 0 - ) { - foreach ($dataSource['data']['items'] as &$row) { - $row[PickupLocationInterface::IS_PICKUP_LOCATION_ACTIVE] = - $row[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY] - [PickupLocationInterface::IS_PICKUP_LOCATION_ACTIVE] ?? ''; - } + if (!isset($dataSource['data']['totalRecords'])) { + return $dataSource; + } + + if ((int)$dataSource['data']['totalRecords'] === 0) { + return $dataSource; + } + + return $this->normalizeData($dataSource); + } + + /** + * @param array $dataSource + * @return array + */ + private function normalizeData(array $dataSource):array + { + foreach ($dataSource['data']['items'] as &$row) { + $row[PickupLocationInterface::IS_PICKUP_LOCATION_ACTIVE] = + $row[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY][PickupLocationInterface::IS_PICKUP_LOCATION_ACTIVE] ?? ''; } return $dataSource; From d1a012be25239a92b0215711a4ca1f22c14b2995 Mon Sep 17 00:00:00 2001 From: Sergey Mutaf <seruymt@gmail.com> Date: Wed, 17 Apr 2019 09:45:31 +0300 Subject: [PATCH 160/231] MSI-1947:Source items deduction during Credit memo creation for unshipped order items - using bccomp to check if qtyToDeduct less than zero --- .../GetSourceDeductionRequestFromSourceSelection.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/InventorySales/Model/ReturnProcessor/GetSourceDeductionRequestFromSourceSelection.php b/InventorySales/Model/ReturnProcessor/GetSourceDeductionRequestFromSourceSelection.php index 4da3c608e059..4d4c11147423 100644 --- a/InventorySales/Model/ReturnProcessor/GetSourceDeductionRequestFromSourceSelection.php +++ b/InventorySales/Model/ReturnProcessor/GetSourceDeductionRequestFromSourceSelection.php @@ -117,9 +117,10 @@ private function getItemsPerSource(array $sourceSelectionItems): array { $itemsPerSource = []; foreach ($sourceSelectionItems as $sourceSelectionItem) { - if ($sourceSelectionItem->getQtyToDeduct() < 0.000001) { + if (bccomp((string)$sourceSelectionItem->getQtyToDeduct(), '0.000001', 6) === -1) { continue; } + if (!isset($itemsPerSource[$sourceSelectionItem->getSourceCode()])) { $itemsPerSource[$sourceSelectionItem->getSourceCode()] = []; } From 353ac908824046487708e07fd87fa887a0ae2d74 Mon Sep 17 00:00:00 2001 From: Vadim Justus <v.justus@techdivision.com> Date: Wed, 17 Apr 2019 12:11:47 +0200 Subject: [PATCH 161/231] magento-engcom/msi#2170: Implement additional filter for stock managed items during collecting inconsistencies --- .../GetSaleableQuantityInconsistencies.php | 14 +++- .../FilterManagedStockProducts.php | 70 +++++++++++++++++++ InventoryReservationCli/composer.json | 3 +- 3 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterManagedStockProducts.php diff --git a/InventoryReservationCli/Model/GetSaleableQuantityInconsistencies.php b/InventoryReservationCli/Model/GetSaleableQuantityInconsistencies.php index b6b6f42fab83..f0769a4fb3cf 100644 --- a/InventoryReservationCli/Model/GetSaleableQuantityInconsistencies.php +++ b/InventoryReservationCli/Model/GetSaleableQuantityInconsistencies.php @@ -14,6 +14,7 @@ use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\Collector; use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\CollectorFactory; use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterExistingOrders; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterManagedStockProducts; use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterUnresolvedReservations; /** @@ -51,13 +52,19 @@ class GetSaleableQuantityInconsistencies */ private $filterUnresolvedReservations; + /** + * @var FilterManagedStockProducts + */ + private $filterManagedStockProducts; + /** * @param CollectorFactory $collectorFactory * @param AddExpectedReservations $addExpectedReservations * @param AddExistingReservations $addExistingReservations * @param AddCompletedOrdersToForUnresolvedReservations $addCompletedOrdersToUnresolved - * @param FilterExistingOrders $filterExistingOrder + * @param FilterExistingOrders $filterExistingOrders * @param FilterUnresolvedReservations $filterUnresolvedReservations + * @param FilterManagedStockProducts $filterManagedStockProducts */ public function __construct( CollectorFactory $collectorFactory, @@ -65,7 +72,8 @@ public function __construct( AddExistingReservations $addExistingReservations, AddCompletedOrdersToForUnresolvedReservations $addCompletedOrdersToUnresolved, FilterExistingOrders $filterExistingOrders, - FilterUnresolvedReservations $filterUnresolvedReservations + FilterUnresolvedReservations $filterUnresolvedReservations, + FilterManagedStockProducts $filterManagedStockProducts ) { $this->collectorFactory = $collectorFactory; $this->addExpectedReservations = $addExpectedReservations; @@ -73,6 +81,7 @@ public function __construct( $this->addCompletedOrdersToUnresolved = $addCompletedOrdersToUnresolved; $this->filterExistingOrders = $filterExistingOrders; $this->filterUnresolvedReservations = $filterUnresolvedReservations; + $this->filterManagedStockProducts = $filterManagedStockProducts; } /** @@ -89,6 +98,7 @@ public function execute(): array $this->addCompletedOrdersToUnresolved->execute($collector); $items = $collector->getItems(); + $items = $this->filterManagedStockProducts->execute($items); $items = $this->filterUnresolvedReservations->execute($items); $items = $this->filterExistingOrders->execute($items); diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterManagedStockProducts.php b/InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterManagedStockProducts.php new file mode 100644 index 000000000000..ebd239d82106 --- /dev/null +++ b/InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterManagedStockProducts.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; + +use Magento\Framework\Exception\LocalizedException; +use Magento\InventoryApi\Model\IsProductAssignedToStockInterface; +use Magento\InventoryConfigurationApi\Api\GetStockItemConfigurationInterface; +use Magento\InventoryConfigurationApi\Exception\SkuIsNotAssignedToStockException; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; + +/** + * Remove all reservations with incomplete state + */ +class FilterManagedStockProducts +{ + /** + * @var GetStockItemConfigurationInterface + */ + private $getStockItemConfiguration; + + /** + * @var IsProductAssignedToStockInterface + */ + private $isProductAssignedToStock; + + /** + * @param GetStockItemConfigurationInterface $getStockItemConfiguration + * @param IsProductAssignedToStockInterface $isProductAssignedToStock + */ + public function __construct( + GetStockItemConfigurationInterface $getStockItemConfiguration, + IsProductAssignedToStockInterface $isProductAssignedToStock + ) { + $this->getStockItemConfiguration = $getStockItemConfiguration; + $this->isProductAssignedToStock = $isProductAssignedToStock; + } + + /** + * Remove all reservations with incomplete state + * + * @param SaleableQuantityInconsistency[] $inconsistencies + * @return SaleableQuantityInconsistency[] + * @throws LocalizedException + * @throws SkuIsNotAssignedToStockException + */ + public function execute(array $inconsistencies): array + { + foreach ($inconsistencies as $inconsistency) { + $filteredItems = []; + foreach ($inconsistency->getItems() as $sku => $qty) { + if (false === $this->isProductAssignedToStock->execute($sku, $inconsistency->getStockId())) { + continue; + } + + $stockConfiguration = $this->getStockItemConfiguration->execute($sku, $inconsistency->getStockId()); + if ($stockConfiguration->isManageStock()) { + $filteredItems[$sku] = $qty; + } + } + $inconsistency->setItems($filteredItems); + } + + return $inconsistencies; + } +} diff --git a/InventoryReservationCli/composer.json b/InventoryReservationCli/composer.json index 58766108702d..80cf92c84ac1 100644 --- a/InventoryReservationCli/composer.json +++ b/InventoryReservationCli/composer.json @@ -6,7 +6,8 @@ "magento/framework": "*", "magento/module-sales": "*", "magento/module-inventory-reservations-api": "*", - "magento/module-inventory-sales-api": "*" + "magento/module-inventory-sales-api": "*", + "magento/module-inventory-configuration-api": "*" }, "type": "magento2-module", "license": [ From 8c938b930d1414889aae1b1ef375c8f92ddd09c0 Mon Sep 17 00:00:00 2001 From: Vadim Justus <v.justus@techdivision.com> Date: Wed, 17 Apr 2019 12:37:43 +0200 Subject: [PATCH 162/231] magento-engcom/msi#2170: Fix static test issues --- InventoryReservationCli/composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/InventoryReservationCli/composer.json b/InventoryReservationCli/composer.json index 80cf92c84ac1..de3446e85422 100644 --- a/InventoryReservationCli/composer.json +++ b/InventoryReservationCli/composer.json @@ -5,6 +5,7 @@ "php": "~7.1.3||~7.2.0", "magento/framework": "*", "magento/module-sales": "*", + "magento/module-inventory-api": "*", "magento/module-inventory-reservations-api": "*", "magento/module-inventory-sales-api": "*", "magento/module-inventory-configuration-api": "*" From 5f80a6d68dc7e8b6c4f27ef5ace7ffdd9d678935 Mon Sep 17 00:00:00 2001 From: Vadim Justus <v.justus@techdivision.com> Date: Wed, 17 Apr 2019 22:08:57 +0200 Subject: [PATCH 163/231] magento-engcom/msi#2170: Fix install for automated tests --- .../Command/CreateCompensations.php | 10 +++++----- .../Command/ShowInconsistencies.php | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/InventoryReservationCli/Command/CreateCompensations.php b/InventoryReservationCli/Command/CreateCompensations.php index 0a92a24132f7..4a3073e15de4 100644 --- a/InventoryReservationCli/Command/CreateCompensations.php +++ b/InventoryReservationCli/Command/CreateCompensations.php @@ -10,12 +10,12 @@ use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\InputException; use Magento\Framework\Validation\ValidationException; -use Magento\InventoryReservationCli\Model\GetSaleableQuantityCompensations; -use Magento\InventoryReservationCli\Model\GetSaleableQuantityInconsistencies; +use Magento\InventoryReservationCli\Model\GetSaleableQuantityCompensations\Proxy as GetSaleableQuantityCompensations; +use Magento\InventoryReservationCli\Model\GetSaleableQuantityInconsistencies\Proxy as GetSaleableQuantityInconsistencies; use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterCompleteOrders; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterIncompleteOrders; -use Magento\InventoryReservationsApi\Model\AppendReservationsInterface; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterCompleteOrders\Proxy as FilterCompleteOrders; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterIncompleteOrders\Proxy as FilterIncompleteOrders; +use Magento\InventoryReservationsApi\Model\AppendReservationsInterface\Proxy as AppendReservationsInterface; use Magento\InventoryReservationsApi\Model\ReservationInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; diff --git a/InventoryReservationCli/Command/ShowInconsistencies.php b/InventoryReservationCli/Command/ShowInconsistencies.php index 5ee935689be5..151a1c5eecd5 100644 --- a/InventoryReservationCli/Command/ShowInconsistencies.php +++ b/InventoryReservationCli/Command/ShowInconsistencies.php @@ -8,10 +8,10 @@ namespace Magento\InventoryReservationCli\Command; use Magento\Framework\Validation\ValidationException; -use Magento\InventoryReservationCli\Model\GetSaleableQuantityInconsistencies; +use Magento\InventoryReservationCli\Model\GetSaleableQuantityInconsistencies\Proxy as GetSaleableQuantityInconsistencies; use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterCompleteOrders; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterIncompleteOrders; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterCompleteOrders\Proxy as FilterCompleteOrders; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterIncompleteOrders\Proxy as FilterIncompleteOrders; use Magento\Sales\Model\Order; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; From 8ba9418f841d976f573f897642f9fb86bf6b229c Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Thu, 18 Apr 2019 01:16:16 +0300 Subject: [PATCH 164/231] MSI-2062: Inventory Export Stock: refactoring, addad is_salable field --- .../Model/ExportStockIndexData.php | 30 ++++++++++++++--- .../Model/GetQtyForNotManageStock.php | 10 +++--- .../Model/PreciseExportStockProcessor.php | 33 +++++++++++++++---- .../ResourceModel/StockIndexDumpProcessor.php | 33 ++++++++++++++----- InventoryExportStock/README.md | 16 +++++++++ InventoryExportStock/etc/di.xml | 7 +--- .../Api/ExportStockIndexDataInterface.php | 4 +-- InventoryExportStockApi/README.md | 16 +++++++++ 8 files changed, 117 insertions(+), 32 deletions(-) diff --git a/InventoryExportStock/Model/ExportStockIndexData.php b/InventoryExportStock/Model/ExportStockIndexData.php index 72cfa60194d6..0cb0c81284ee 100644 --- a/InventoryExportStock/Model/ExportStockIndexData.php +++ b/InventoryExportStock/Model/ExportStockIndexData.php @@ -11,6 +11,9 @@ use Magento\Framework\Exception\LocalizedException; use Magento\InventoryExportStock\Model\ResourceModel\StockIndexDumpProcessor; use Magento\InventoryExportStockApi\Api\ExportStockIndexDataInterface; +use Magento\InventorySalesApi\Api\Data\SalesChannelInterface; +use Magento\InventorySalesApi\Api\StockResolverInterface; +use Psr\Log\LoggerInterface; /** * Class ExportStockIndexData @@ -22,31 +25,50 @@ class ExportStockIndexData implements ExportStockIndexDataInterface */ private $stockIndexDumpProcessor; + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @var StockResolverInterface + */ + private $stockResolver; + /** * ExportStockIndexData constructor * * @param StockIndexDumpProcessor $stockIndexDumpProcessor + * @param StockResolverInterface $stockResolver + * @param LoggerInterface $logger */ public function __construct( - StockIndexDumpProcessor $stockIndexDumpProcessor + StockIndexDumpProcessor $stockIndexDumpProcessor, + StockResolverInterface $stockResolver, + LoggerInterface $logger ) { $this->stockIndexDumpProcessor = $stockIndexDumpProcessor; + $this->stockResolver = $stockResolver; + $this->logger = $logger; } /** * Provides stock index export from inventory_stock_% table * - * @param int $stockId + * @param string $websiteCode * @return array * @throws LocalizedException */ public function execute( - int $stockId + string $websiteCode ): array { try { + $stockId = $this->stockResolver + ->execute(SalesChannelInterface::TYPE_WEBSITE, $websiteCode)->getStockId(); $items = $this->stockIndexDumpProcessor->execute($stockId); } catch (Exception $e) { - throw new LocalizedException(__($e->getMessage())); + $this->logger->critical($e->getMessage(), $e->getTrace()); + throw new LocalizedException(_('Something went wrong. Export couldn\'t be executed, See log files for error details')); } return $items; diff --git a/InventoryExportStock/Model/GetQtyForNotManageStock.php b/InventoryExportStock/Model/GetQtyForNotManageStock.php index b39ea9b95571..4b2ac88b102a 100644 --- a/InventoryExportStock/Model/GetQtyForNotManageStock.php +++ b/InventoryExportStock/Model/GetQtyForNotManageStock.php @@ -13,17 +13,17 @@ class GetQtyForNotManageStock { /** - * @var int + * @var float|null */ private $qtyForNotManageStock; /** * GetQtyForNotManageStock constructor * - * @param int $qtyForNotManageStock + * @param float|null $qtyForNotManageStock */ public function __construct( - int $qtyForNotManageStock + ?float $qtyForNotManageStock ) { $this->qtyForNotManageStock = $qtyForNotManageStock; } @@ -31,9 +31,9 @@ public function __construct( /** * Provides qtyForNotManageStock from di configuration * - * @return int + * @return float|null */ - public function execute(): int + public function execute(): ?float { return $this->qtyForNotManageStock; } diff --git a/InventoryExportStock/Model/PreciseExportStockProcessor.php b/InventoryExportStock/Model/PreciseExportStockProcessor.php index d04e154a83f2..691107b7acd6 100644 --- a/InventoryExportStock/Model/PreciseExportStockProcessor.php +++ b/InventoryExportStock/Model/PreciseExportStockProcessor.php @@ -10,13 +10,15 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\LocalizedException; +use Magento\InventoryApi\Model\IsProductAssignedToStockInterface; use Magento\InventoryConfigurationApi\Api\GetStockItemConfigurationInterface; use Magento\InventoryConfigurationApi\Exception\SkuIsNotAssignedToStockException; use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForSkuInterface; use Magento\InventorySalesApi\Api\GetProductSalableQtyInterface; +use Magento\InventorySalesApi\Api\IsProductSalableInterface; /** - * Class Provides precise method of getting stock data + * Class Provides stock data with reservation taken into in account */ class PreciseExportStockProcessor { @@ -39,25 +41,37 @@ class PreciseExportStockProcessor * @var GetQtyForNotManageStock */ private $getQtyForNotManageStock; + /** + * @var IsProductAssignedToStockInterface + */ + private $isProductAssignedToStock; + /** + * @var IsProductSalableInterface + */ + private $isProductSalable; /** - * PreciseExportStockProcessor constructor - * * @param IsSourceItemManagementAllowedForSkuInterface $isSourceItemManagementAllowedForSku * @param GetProductSalableQtyInterface $getProductSalableQty * @param GetStockItemConfigurationInterface $getStockItemConfiguration * @param GetQtyForNotManageStock $getQtyForNotManageStock + * @param IsProductAssignedToStockInterface $isProductAssignedToStock + * @param IsProductSalableInterface $isProductSalable */ public function __construct( IsSourceItemManagementAllowedForSkuInterface $isSourceItemManagementAllowedForSku, GetProductSalableQtyInterface $getProductSalableQty, GetStockItemConfigurationInterface $getStockItemConfiguration, - GetQtyForNotManageStock $getQtyForNotManageStock + GetQtyForNotManageStock $getQtyForNotManageStock, + IsProductAssignedToStockInterface $isProductAssignedToStock, + IsProductSalableInterface $isProductSalable ) { $this->isSourceItemManagementAllowedForSku = $isSourceItemManagementAllowedForSku; $this->getProductSalableQty = $getProductSalableQty; $this->getStockItemConfiguration = $getStockItemConfiguration; $this->getQtyForNotManageStock = $getQtyForNotManageStock; + $this->isProductAssignedToStock = $isProductAssignedToStock; + $this->isProductSalable = $isProductSalable; } /** @@ -76,13 +90,15 @@ public function execute(array $products, int $stockId): array foreach ($skus as $sku) { try { $qty = $this->getProductSalableQtyByStock($sku, $stockId); + $isSalable = $this->isProductSalable->execute($sku, $stockId); } catch (SkuIsNotAssignedToStockException $e) { - $qty = 0.00; + continue; } $items[] = [ 'sku' => $sku, - 'qty' => $qty + 'qty' => $qty, + 'is_salable' => $isSalable ]; } @@ -111,13 +127,16 @@ private function getProductSkus(array $products): array * * @param string $sku * @param int $stockId - * @return float + * @return float|null * @throws InputException * @throws LocalizedException * @throws SkuIsNotAssignedToStockException */ private function getProductSalableQtyByStock(string $sku, int $stockId): ?float { + if (!$this->isProductAssignedToStock->execute($sku, $stockId)) { + throw new SkuIsNotAssignedToStockException(__('The requested sku is not assigned to given stock.')); + } if (!$this->getStockItemConfiguration->execute($sku, $stockId)->isManageStock()) { return (float)$this->getQtyForNotManageStock->execute(); } diff --git a/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php b/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php index e2a6ab852e65..82ed06011ebb 100644 --- a/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php +++ b/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php @@ -7,14 +7,17 @@ namespace Magento\InventoryExportStock\Model\ResourceModel; +use Exception; use Magento\Framework\App\ResourceConnection; use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\DB\Select; +use Magento\Framework\Exception\LocalizedException; use Magento\InventoryExportStock\Model\GetQtyForNotManageStock; use Magento\InventoryIndexer\Model\StockIndexTableNameResolverInterface; use Magento\InventorySales\Model\ResourceModel\IsStockItemSalableCondition\ManageStockCondition; +use Psr\Log\LoggerInterface; +use Zend\Db\Sql\Expression; use Zend_Db_Expr; -use Zend_Db_Select_Exception; /** * Class GetStockIndexDump provides sku and qty of products dumping them from stock index table @@ -45,6 +48,10 @@ class StockIndexDumpProcessor * @var GetQtyForNotManageStock */ private $getQtyForNotManageStock; + /** + * @var LoggerInterface + */ + private $logger; /** * GetStockIndexDump constructor @@ -53,17 +60,20 @@ class StockIndexDumpProcessor * @param ResourceConnection $resourceConnection * @param ManageStockCondition $manageStockCondition * @param GetQtyForNotManageStock $getQtyForNotManageStock + * @param LoggerInterface $logger */ public function __construct( StockIndexTableNameResolverInterface $stockIndexTableNameResolver, ResourceConnection $resourceConnection, ManageStockCondition $manageStockCondition, - GetQtyForNotManageStock $getQtyForNotManageStock + GetQtyForNotManageStock $getQtyForNotManageStock, + LoggerInterface $logger ) { $this->stockIndexTableNameResolver = $stockIndexTableNameResolver; $this->resourceConnection = $resourceConnection; $this->manageStockCondition = $manageStockCondition; $this->getQtyForNotManageStock = $getQtyForNotManageStock; + $this->logger = $logger; } /** @@ -71,16 +81,21 @@ public function __construct( * * @param int $stockId * @return array - * @throws Zend_Db_Select_Exception + * @throws LocalizedException */ public function execute(int $stockId): array { $this->connection = $this->resourceConnection->getConnection(); $select = $this->connection->select(); - $select->union([ - $this->getStockItemSelect($stockId), - $this->getStockIndexSelect($stockId) - ]); + try { + $select->union([ + $this->getStockItemSelect($stockId), + $this->getStockIndexSelect($stockId) + ]); + } catch (Exception $e) { + $this->logger->critical($e->getMessage(), $e->getTrace()); + throw new LocalizedException(_('Something went wrong. Export couldn\'t be executed, See log files for error details')); + } return $this->connection->fetchAll($select); } @@ -101,6 +116,7 @@ private function getStockIndexSelect(int $stockId): Select $stockIndexTableName, [ 'qty' => 'quantity', + 'is_salable' => 'is_salable', 'sku' => 'sku' ] ); @@ -122,7 +138,8 @@ private function getStockItemSelect(int $stockId): Select $select->from( ['legacy_stock_item' => $legacyStockItemTable], - new Zend_Db_Expr('"' . $this->getQtyForNotManageStock->execute() . '" as qty') + new Expression($this->getQtyForNotManageStock->execute() . ' as qty'), + new Expression('1 as is_salable') )->join( ['product_entity' => $productEntityTable], 'legacy_stock_item.product_id = product_entity.entity_id', diff --git a/InventoryExportStock/README.md b/InventoryExportStock/README.md index e69de29bb2d1..c2974cb89cde 100644 --- a/InventoryExportStock/README.md +++ b/InventoryExportStock/README.md @@ -0,0 +1,16 @@ +# InventoryExportStock module + +The `InventoryExportStock` module provides stock export functionality. + +This module is part of the new inventory infrastructure. The +[Inventory Management overview](https://devdocs.magento.com/guides/v2.3/inventory/index.html) +describes the MSI (Multi-Source Inventory) project in more detail. + +## Installation details + +This module is installed as part of Magento Open Source. It may be disabled if the Inventory Management UI +is provided by a 3rd-party system or if you run a headless version of Magento. + +## Extension points and service contracts + +There are no extension points or service contracts for this module. diff --git a/InventoryExportStock/etc/di.xml b/InventoryExportStock/etc/di.xml index 1470e7d4dd30..6b2184189352 100644 --- a/InventoryExportStock/etc/di.xml +++ b/InventoryExportStock/etc/di.xml @@ -13,14 +13,9 @@ type="Magento\InventoryExportStock\Model\ExportStockSalableQty"/> <preference for="Magento\InventoryExportStockApi\Api\ExportStockIndexDataInterface" type="Magento\InventoryExportStock\Model\ExportStockIndexData"/> - <type name="Magento\InventoryExportStock\Model\ExportStockProcessor\PreciseExportStockProcessor"> - <arguments> - <argument name="qtyForNotManageStock" xsi:type="number">1</argument> - </arguments> - </type> <type name="Magento\InventoryExportStock\Model\GetQtyForNotManageStock"> <arguments> - <argument name="qtyForNotManageStock" xsi:type="number">1</argument> + <argument name="qtyForNotManageStock" xsi:type="null"/> </arguments> </type> </config> diff --git a/InventoryExportStockApi/Api/ExportStockIndexDataInterface.php b/InventoryExportStockApi/Api/ExportStockIndexDataInterface.php index f23bb610f30d..72fe8918f146 100644 --- a/InventoryExportStockApi/Api/ExportStockIndexDataInterface.php +++ b/InventoryExportStockApi/Api/ExportStockIndexDataInterface.php @@ -16,10 +16,10 @@ interface ExportStockIndexDataInterface /** * Provides stock index export from inventory_stock_% table * - * @param int $stockId + * @param string $websiteCode * @return array */ public function execute( - int $stockId + string $websiteCode ): array; } diff --git a/InventoryExportStockApi/README.md b/InventoryExportStockApi/README.md index e69de29bb2d1..42c97793168b 100644 --- a/InventoryExportStockApi/README.md +++ b/InventoryExportStockApi/README.md @@ -0,0 +1,16 @@ +# InventoryExportStockApi module + +The `InventoryExportStockApi` module provides stock export functionality api. + +This module is part of the new inventory infrastructure. The +[Inventory Management overview](https://devdocs.magento.com/guides/v2.3/inventory/index.html) +describes the MSI (Multi-Source Inventory) project in more detail. + +## Installation details + +This module is installed as part of Magento Open Source. It may be disabled if the Inventory Management UI +is provided by a 3rd-party system or if you run a headless version of Magento. + +## Extension points and service contracts + +There are no extension points or service contracts for this module. From 5eb4d0e2733bee38fe81e5cc48e9d8b0d2dd4165 Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Thu, 18 Apr 2019 01:32:17 +0300 Subject: [PATCH 165/231] MSI-2062: Inventory Export Stock: refactoring --- .../Model/ResourceModel/StockIndexDumpProcessor.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php b/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php index 82ed06011ebb..90edf4f6437b 100644 --- a/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php +++ b/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php @@ -16,7 +16,6 @@ use Magento\InventoryIndexer\Model\StockIndexTableNameResolverInterface; use Magento\InventorySales\Model\ResourceModel\IsStockItemSalableCondition\ManageStockCondition; use Psr\Log\LoggerInterface; -use Zend\Db\Sql\Expression; use Zend_Db_Expr; /** @@ -94,7 +93,7 @@ public function execute(int $stockId): array ]); } catch (Exception $e) { $this->logger->critical($e->getMessage(), $e->getTrace()); - throw new LocalizedException(_('Something went wrong. Export couldn\'t be executed, See log files for error details')); + throw new LocalizedException(__('Something went wrong. Export couldn\'t be executed, See log files for error details')); } return $this->connection->fetchAll($select); @@ -138,8 +137,8 @@ private function getStockItemSelect(int $stockId): Select $select->from( ['legacy_stock_item' => $legacyStockItemTable], - new Expression($this->getQtyForNotManageStock->execute() . ' as qty'), - new Expression('1 as is_salable') + [new Zend_Db_Expr($this->getQtyForNotManageStock->execute() . ' as qty'), + new Zend_Db_Expr('1 as is_salable')] )->join( ['product_entity' => $productEntityTable], 'legacy_stock_item.product_id = product_entity.entity_id', From 5325c21c0539820b7bc318c472ad1fdc2e63b9bc Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Thu, 18 Apr 2019 01:32:43 +0300 Subject: [PATCH 166/231] MSI-2062: Inventory Export Stock: refactoring --- InventoryExportStock/Model/ExportStockIndexData.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InventoryExportStock/Model/ExportStockIndexData.php b/InventoryExportStock/Model/ExportStockIndexData.php index 0cb0c81284ee..3cf3afd77a54 100644 --- a/InventoryExportStock/Model/ExportStockIndexData.php +++ b/InventoryExportStock/Model/ExportStockIndexData.php @@ -68,7 +68,7 @@ public function execute( $items = $this->stockIndexDumpProcessor->execute($stockId); } catch (Exception $e) { $this->logger->critical($e->getMessage(), $e->getTrace()); - throw new LocalizedException(_('Something went wrong. Export couldn\'t be executed, See log files for error details')); + throw new LocalizedException(__('Something went wrong. Export couldn\'t be executed, See log files for error details')); } return $items; From 5294d8d25bc130ab39d92537cd172d64644ad5c0 Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Thu, 18 Apr 2019 10:06:14 -0500 Subject: [PATCH 167/231] Added AdminCreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipment Test for msi-1974 issue --- ...tockAfterFullInvoiceAndPartialShipment.xml | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipment.xml diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipment.xml b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipment.xml new file mode 100644 index 000000000000..4518eb46599c --- /dev/null +++ b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipment.xml @@ -0,0 +1,160 @@ +<?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="CreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipmentTest"> + <annotations> + <stories value="MSI Credit Memo"/> + <title value="Credit Memo created with full refund with Simple product on Default stock after full invoice and partial shipment"/> + <description value="Credit Memo created with full refund with Simple product on Default stock after full invoice and partial shipment"/> + <testCaseId value="MSI-1974"/> + <severity value="BLOCKER"/> + <group value="msi"/> + <group value="multi_mode"/> + </annotations> + + <before> + <createData entity="MsiCustomer1" stepKey="createCustomer"/> + <createData entity="BasicMsiStock1" stepKey="createStock"/> + <createData entity="FullSource1" stepKey="createSource"/> + <createData entity="SourceStockLinked1" stepKey="linkSourceStock"> + <requiredEntity createDataKey="createStock"/> + <requiredEntity createDataKey="createSource"/> + </createData> + <createData entity="SimpleSubCategory" stepKey="simpleCategory"/> + <createData entity="SimpleProduct" stepKey="simpleProduct"> + <field key="qty">100.00</field> + <requiredEntity createDataKey="simpleCategory"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <waitForPageLoad stepKey="waitForDashboardLoad"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createStock" stepKey="deleteStock"/> + <deleteData createDataKey="simpleCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> + </after> + + <!--Login To storefront as Customer--> + <comment userInput="Login to storefront as customer." stepKey="loginToStorefrontComment"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefront"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!--Purchase product once logged in--> + <comment userInput="Purchase 5 simple product" stepKey="purchaseSimpleProductComment"/> + <amOnPage url="{{StorefrontCategoryPage.url($$simpleCategory.name$$)}}" stepKey="navigateToCategoryPage"/> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($$simpleProduct.name$$)}}" stepKey="moveMouseOverProduct" /> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$simpleProduct.name$$)}}" stepKey="clickAddToCart" /> + <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart($$simpleProduct.name$$)}}" time="30" stepKey="assertMessage"/> + <waitForText userInput="1" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> + <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> + <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForViewAndEditCartVisible"/> + <clearField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct.name$$)}}" stepKey="clearField"/> + <fillField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct.name$$)}}" userInput="5" stepKey="setProductQtyToFiftyInMiniCart"/> + <click selector="{{StorefrontMinicartSection.itemQuantityUpdate($$simpleProduct.name$$)}}" stepKey="updateQtyInMiniCart"/> + <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForPaymentSelectionPageLoad"/> + <click selector=".continue" stepKey="clickOnNextCheckout"/> + <waitForPageLoad stepKey="waitForPageLoadCheckout"/> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyOrderPayment"/> + <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButtonVisible"/> + <see selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{US_Address_TX.street[0]}}" stepKey="chooseBillingAddress"/> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="placeOrder"/> + <waitForPageLoad stepKey="waitUntilOrderPlaced"/> + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> + <see selector="{{CheckoutSuccessMainSection.success}}" userInput="Your order number is:" stepKey="checkOrderPlaceSuccessMessage"/> + + <!--Admin Area Check ordered quantity--> + <comment userInput="Admin - Check ordered quantity" stepKey="AdminCheckOrderedQuantity"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderListPage"/> + <waitForLoadingMaskToDisappear stepKey="waitOrderListPageLoad"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrder"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearch1"/> + <waitForLoadingMaskToDisappear stepKey="waitFilteredOrderListPageLoad"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="navigateToOrderViewPage"/> + <waitForElementVisible selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" stepKey="waitForViewOrderedQuantity"/> + <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Ordered 5" stepKey="orderedQuantity"/> + + <!--Admin Area Check source quantity and salable quantity--> + <comment userInput="Admin - Check Source quantity and salable quantity after order placed" stepKey="AdminCheckQuantityAfterOrderPlaced"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterPlaceOrder"/> + <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterPlaceOrder"> + <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> + <argument name="value" value="$$simpleProduct.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="100" stepKey="checkProductSourceQtyAfterPlaceOrder"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="95" stepKey="checkSalableQtyAfterPlaceOrder"/> + + <!--Admin Area Process Full Invoice--> + <comment userInput="Admin - Process invoice for full order" stepKey="InvoiceFullOrder"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToProcessInvoice"/> + <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMaskInvoice"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNumInvoice"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchInvoice"/> + <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMaskGridForInvoice"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRowInvoice"/> + <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoice"/> + <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> + <waitForPageLoad time="60" stepKey="waitForPageLoadSubmitInvoice"/> + <scrollToTopOfPage stepKey="scrollToTopMessage"/> + <waitForPageLoad stepKey="waitForPageLoadSuccessMessage"/> + <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="checkSuccessMessage"/> + + <!--Admin Area Process Partial Shipping--> + <comment userInput="Admin - Ship partial order" stepKey="AdminShipPartialOrder"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToCreateShipment"/> + <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMask"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNum"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchShipping"/> + <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMask"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> + <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShip"/> + <waitForLoadingMaskToDisappear stepKey="waitForShipLoadingMask"/> + <fillField selector="{{AdminShipmentItemsSection.itemQtyToShip('1')}}" userInput="3" stepKey="shipPartialQuantity3"/> + <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" stepKey="submitShipment"/> + <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The shipment has been created." stepKey="seeShipmentCreateSuccess"/> + + <!--Admin Area Check source quantity and salable quantity after partial shipment--> + <comment userInput="Admin - Check Source quantity and salable quantity after partial shipment" stepKey="AdminCheckQuantityAfterPartialShipment"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterPartialShipment"/> + <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterPartialShipment"> + <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> + <argument name="value" value="$$simpleProduct.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="97" stepKey="checkProductSourceQtyAfterPartialShipment"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="90" stepKey="checkSalableQtyAfterPartialShipment"/> + + <!--Admin Area Create Full Credit Memo--> + <comment userInput="Admin - Create credit memo for full order" stepKey="AdminCreateCreditMemoFullOrder"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToCreateCreditMemo"/> + <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMaskCreditMemo"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNumCreditMemo"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchCreditMemo"/> + <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMaskCreditMemo"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRowCreditMemo"/> + <click selector="{{AdminOrderDetailsMainActionsSection.creditMemo}}" stepKey="clickCreditMemo"/> + <click selector="{{AdminCreditMemoItemsSection.itemReturnToStock('1')}}" stepKey="returnToStockCheckbox"/> + <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickSubmit"/> + + + <!--Admin Area Check quantities after Credit Memo--> + <comment userInput="Admin - Check Source quantity and salable quantity after credit memo" stepKey="AdminCheckQuantityAfterCreditMemo"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterCreditMemo"/> + <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterCreditMemo"> + <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> + <argument name="value" value="$$simpleProduct.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="100" stepKey="checkProductSourceQtyAfterCreditMemo"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="100" stepKey="checkSalableQtyAfterCreditMemo"/> + + </test> +</tests> \ No newline at end of file From 1745d71d5c288feb0f6366f79a7d200adab8a20c Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Thu, 18 Apr 2019 12:49:21 -0500 Subject: [PATCH 168/231] Update MFTF tests according Manual HIP-TEST - minor fixes --- ...leProductOnCustomStockFromHomepageTest.xml | 28 +++++++++---------- ...eProductOnDefaultStockFromHomepageTest.xml | 27 +++++++++--------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnCustomStockFromHomepageTest.xml b/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnCustomStockFromHomepageTest.xml index f14c53721bec..39e4a6b019b3 100644 --- a/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnCustomStockFromHomepageTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnCustomStockFromHomepageTest.xml @@ -20,30 +20,32 @@ </annotations> <before> - <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <magentoCLI stepKey="enableGuestCheckoutForDownloadable" command="config:set catalog/downloadable/disable_guest_checkout 0" /> <createData entity="FullSource1" stepKey="createSource"/> <createData entity="BasicMsiStockWithMainWebsite1" stepKey="createStock"/> <createData entity="SourceStockLinked1" stepKey="linkSourceStock"> <requiredEntity createDataKey="createStock"/> <requiredEntity createDataKey="createSource"/> </createData> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> <createData entity="MsiDownloadableProduct" stepKey="downloadableProduct"> <requiredEntity createDataKey="createCategory"/> </createData> <createData entity="downloadableLink1" stepKey="addDownloadableLink"> + <field key="is_shareable">1</field> <requiredEntity createDataKey="downloadableProduct"/> </createData> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <amOnPage url="{{AdminProductEditPage.url($$downloadableProduct.id$$)}}" stepKey="openProductEditPage" /> <actionGroup ref="AssignSourceToProductActionGroup" stepKey="assignCustomSource"> - <argument name="sourceCode" value="$$createStock.source_code$$" /> + <argument name="sourceCode" value="$$createSource.source[source_code]$$" /> </actionGroup> - <!--<scrollTo stepKey="scrollToSources" selector="[data-index='sources']" />--> <fillField selector="{{AdminProductSourcesGrid.rowQty('0')}}" userInput="100" stepKey="fillDefaultSourceQtyField"/> <fillField selector="{{AdminProductSourcesGrid.rowQty('1')}}" userInput="100" stepKey="fillCustomSourceQtyField"/> <actionGroup ref="AdminFormSaveAndClose" stepKey="saveAndCloseProduct"/> </before> <after> + <magentoCLI stepKey="disableGuestCheckoutForDownloadable" command="config:set catalog/downloadable/disable_guest_checkout 1" /> <!-- Assign Sales Channel to Default Stock --> <amOnPage url="{{AdminManageStockPage.url}}" stepKey="amOnTheStockGridPage"/> <waitForPageLoad time="30" stepKey="waitForStockGridPageLoad"/> @@ -74,15 +76,15 @@ <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> <fillField selector="{{CheckoutShippingSection.email}}" userInput="{{MsiCustomer1.email}}" stepKey="enterEmail"/> - <fillField selector="#shipping-new-address-form input[name=firstname]" userInput="{{MsiCustomer1.firstname}}" stepKey="enterFirstName"/> - <fillField selector="#shipping-new-address-form input[name=lastname]" userInput="{{MsiCustomer1.lastname}}" stepKey="enterLastName"/> - <fillField selector="#shipping-new-address-form input[name='street[0]']" userInput="{{CustomerAddressSimple.street[0]}}" stepKey="enterStreet"/> - <fillField selector="#shipping-new-address-form input[name=city]" userInput="{{CustomerAddressSimple.city}}" stepKey="enterCity"/> - <selectOption selector="#shipping-new-address-form select[name=region_id]" userInput="{{CustomerAddressSimple.state}}" stepKey="selectRegion"/> - <fillField selector="#shipping-new-address-form input[name=postcode]" userInput="{{CustomerAddressSimple.postcode}}" stepKey="enterPostcode"/> - <fillField selector="#shipping-new-address-form input[name=telephone]" userInput="{{CustomerAddressSimple.telephone}}" stepKey="enterTelephone"/> + <fillField selector="{{CheckoutPaymentSection.guestFirstName}}" userInput="{{MsiCustomer1.firstname}}" stepKey="enterFirstName"/> + <fillField selector="{{CheckoutPaymentSection.guestLastName}}" userInput="{{MsiCustomer1.lastname}}" stepKey="enterLastName"/> + <fillField selector="{{CheckoutPaymentSection.guestStreet}}" userInput="{{CustomerAddressSimple.street[0]}}" stepKey="enterStreet"/> + <fillField selector="{{CheckoutPaymentSection.guestCity}}" userInput="{{CustomerAddressSimple.city}}" stepKey="enterCity"/> + <selectOption selector="{{CheckoutPaymentSection.guestRegion}}" userInput="{{CustomerAddressSimple.state}}" stepKey="selectRegion"/> + <fillField selector="{{CheckoutPaymentSection.guestPostcode}}" userInput="{{CustomerAddressSimple.postcode}}" stepKey="enterPostcode"/> + <fillField selector="{{CheckoutPaymentSection.guestTelephone}}" userInput="{{CustomerAddressSimple.telephone}}" stepKey="enterTelephone"/> - <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> + <click selector="{{CheckoutPaymentSection.update}}" stepKey="clickUpdate"/> <waitForPageLoad stepKey="waitForPageLoad"/> <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> @@ -107,12 +109,10 @@ <argument name="filter_value" value="$$downloadableProduct.product[sku]$$"/> </actionGroup> - <see selector="{{AdminGridRow.rowOne}}" userInput="{{colorProductAttribute1.name}}" - stepKey="seeProductNameInGrid"/> <see selector="{{AdminGridRow.rowOne}}" userInput="$100.00" stepKey="seeProductPriceInGrid"/> <see selector="{{AdminGridRow.rowOne}}" userInput="$$createSource.source[name]$$: 100" stepKey="seeProductQuantityInGrid"/> - <see selector="{{AdminGridRow.rowOne}}" userInput="$$createStock.stock[name]$$: 95" + <see selector="{{AdminGridRow.rowOne}}" userInput="$$createStock.stock[name]$$: 99" stepKey="seeProductSalableQuantityInGrid"/> <see selector="{{AdminGridRow.rowOne}}" userInput="Enabled" stepKey="seeProductStatusInGrid"/> <see selector="{{AdminGridRow.rowOne}}" userInput="Main Website" stepKey="seeProductWebsiteInGrid"/> diff --git a/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnDefaultStockFromHomepageTest.xml b/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnDefaultStockFromHomepageTest.xml index d5844cb6e06f..c9e18758e3f0 100644 --- a/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnDefaultStockFromHomepageTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnDefaultStockFromHomepageTest.xml @@ -20,6 +20,7 @@ </annotations> <before> + <magentoCLI stepKey="enableGuestCheckoutForDownloadable" command="config:set catalog/downloadable/disable_guest_checkout 0" /> <createData entity="FullSource1" stepKey="createSource"/> <createData entity="BasicMsiStockWithMainWebsite1" stepKey="createStock"/> <createData entity="SimpleSubCategory" stepKey="createCategory"/> @@ -48,6 +49,7 @@ <requiredEntity createDataKey="createCategory"/> </createData> <createData entity="downloadableLink1" stepKey="addDownloadableLink"> + <field key="is_shareable">1</field> <requiredEntity createDataKey="downloadableProduct"/> </createData> <amOnPage url="{{AdminProductEditPage.url($$downloadableProduct.id$$)}}" stepKey="openProductEditPage" /> @@ -55,12 +57,13 @@ <actionGroup ref="AdminFormSaveAndClose" stepKey="saveAndCloseProduct"/> </before> <after> + <magentoCLI stepKey="disableGuestCheckoutForDownloadable" command="config:set catalog/downloadable/disable_guest_checkout 1" /> <actionGroup ref="DisableSourceActionGroup" stepKey="disableSource"> <argument name="sourceCode" value="$$createSource.source[source_code]$$"/> </actionGroup> - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="downloadableProduct" stepKey="deleteProduct"/> + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> </after> <amOnPage url="{{StorefrontProductPage.url($$downloadableProduct.custom_attributes[url_key]$$)}}" stepKey="openProductPage"/> @@ -72,15 +75,15 @@ <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> <fillField selector="{{CheckoutShippingSection.email}}" userInput="{{MsiCustomer1.email}}" stepKey="enterEmail"/> - <fillField selector="#shipping-new-address-form input[name=firstname]" userInput="{{MsiCustomer1.firstname}}" stepKey="enterFirstName"/> - <fillField selector="#shipping-new-address-form input[name=lastname]" userInput="{{MsiCustomer1.lastname}}" stepKey="enterLastName"/> - <fillField selector="#shipping-new-address-form input[name='street[0]']" userInput="{{CustomerAddressSimple.street[0]}}" stepKey="enterStreet"/> - <fillField selector="#shipping-new-address-form input[name=city]" userInput="{{CustomerAddressSimple.city}}" stepKey="enterCity"/> - <selectOption selector="#shipping-new-address-form select[name=region_id]" userInput="{{CustomerAddressSimple.state}}" stepKey="selectRegion"/> - <fillField selector="#shipping-new-address-form input[name=postcode]" userInput="{{CustomerAddressSimple.postcode}}" stepKey="enterPostcode"/> - <fillField selector="#shipping-new-address-form input[name=telephone]" userInput="{{CustomerAddressSimple.telephone}}" stepKey="enterTelephone"/> - - <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> + <fillField selector="{{CheckoutPaymentSection.guestFirstName}}" userInput="{{MsiCustomer1.firstname}}" stepKey="enterFirstName"/> + <fillField selector="{{CheckoutPaymentSection.guestLastName}}" userInput="{{MsiCustomer1.lastname}}" stepKey="enterLastName"/> + <fillField selector="{{CheckoutPaymentSection.guestStreet}}" userInput="{{CustomerAddressSimple.street[0]}}" stepKey="enterStreet"/> + <fillField selector="{{CheckoutPaymentSection.guestCity}}" userInput="{{CustomerAddressSimple.city}}" stepKey="enterCity"/> + <selectOption selector="{{CheckoutPaymentSection.guestRegion}}" userInput="{{CustomerAddressSimple.state}}" stepKey="selectRegion"/> + <fillField selector="{{CheckoutPaymentSection.guestPostcode}}" userInput="{{CustomerAddressSimple.postcode}}" stepKey="enterPostcode"/> + <fillField selector="{{CheckoutPaymentSection.guestTelephone}}" userInput="{{CustomerAddressSimple.telephone}}" stepKey="enterTelephone"/> + + <click selector="{{CheckoutPaymentSection.update}}" stepKey="clickUpdate"/> <waitForPageLoad stepKey="waitForPageLoad"/> <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> @@ -105,12 +108,10 @@ <argument name="filter_value" value="$$downloadableProduct.product[sku]$$"/> </actionGroup> - <see selector="{{AdminGridRow.rowOne}}" userInput="{{colorProductAttribute1.name}}" - stepKey="seeProductNameInGrid"/> <see selector="{{AdminGridRow.rowOne}}" userInput="$100.00" stepKey="seeProductPriceInGrid"/> <see selector="{{AdminGridRow.rowOne}}" userInput="{{_defaultSource.name}}: 100" stepKey="seeProductQuantityInGrid"/> - <see selector="{{AdminGridRow.rowOne}}" userInput="{{_defaultStock.name}}: 95" + <see selector="{{AdminGridRow.rowOne}}" userInput="{{_defaultStock.name}}: 99" stepKey="seeProductSalableQuantityInGrid"/> <see selector="{{AdminGridRow.rowOne}}" userInput="Enabled" stepKey="seeProductStatusInGrid"/> <see selector="{{AdminGridRow.rowOne}}" userInput="Main Website" stepKey="seeProductWebsiteInGrid"/> From f14de975682a3ab737525137f2b979e1e89157dc Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Thu, 18 Apr 2019 14:36:03 -0500 Subject: [PATCH 169/231] magento-engcom/msi#2184: Configure proxies with DI --- .../Command/CreateCompensations.php | 10 +++--- .../Command/ShowInconsistencies.php | 6 ++-- InventoryReservationCli/etc/di.xml | 32 +++++++++++++++++++ 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/InventoryReservationCli/Command/CreateCompensations.php b/InventoryReservationCli/Command/CreateCompensations.php index 4a3073e15de4..0a92a24132f7 100644 --- a/InventoryReservationCli/Command/CreateCompensations.php +++ b/InventoryReservationCli/Command/CreateCompensations.php @@ -10,12 +10,12 @@ use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\InputException; use Magento\Framework\Validation\ValidationException; -use Magento\InventoryReservationCli\Model\GetSaleableQuantityCompensations\Proxy as GetSaleableQuantityCompensations; -use Magento\InventoryReservationCli\Model\GetSaleableQuantityInconsistencies\Proxy as GetSaleableQuantityInconsistencies; +use Magento\InventoryReservationCli\Model\GetSaleableQuantityCompensations; +use Magento\InventoryReservationCli\Model\GetSaleableQuantityInconsistencies; use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterCompleteOrders\Proxy as FilterCompleteOrders; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterIncompleteOrders\Proxy as FilterIncompleteOrders; -use Magento\InventoryReservationsApi\Model\AppendReservationsInterface\Proxy as AppendReservationsInterface; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterCompleteOrders; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterIncompleteOrders; +use Magento\InventoryReservationsApi\Model\AppendReservationsInterface; use Magento\InventoryReservationsApi\Model\ReservationInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; diff --git a/InventoryReservationCli/Command/ShowInconsistencies.php b/InventoryReservationCli/Command/ShowInconsistencies.php index 151a1c5eecd5..5ee935689be5 100644 --- a/InventoryReservationCli/Command/ShowInconsistencies.php +++ b/InventoryReservationCli/Command/ShowInconsistencies.php @@ -8,10 +8,10 @@ namespace Magento\InventoryReservationCli\Command; use Magento\Framework\Validation\ValidationException; -use Magento\InventoryReservationCli\Model\GetSaleableQuantityInconsistencies\Proxy as GetSaleableQuantityInconsistencies; +use Magento\InventoryReservationCli\Model\GetSaleableQuantityInconsistencies; use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterCompleteOrders\Proxy as FilterCompleteOrders; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterIncompleteOrders\Proxy as FilterIncompleteOrders; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterCompleteOrders; +use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterIncompleteOrders; use Magento\Sales\Model\Order; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; diff --git a/InventoryReservationCli/etc/di.xml b/InventoryReservationCli/etc/di.xml index bbdc73ebbe35..11ae659ee6b3 100644 --- a/InventoryReservationCli/etc/di.xml +++ b/InventoryReservationCli/etc/di.xml @@ -18,4 +18,36 @@ </argument> </arguments> </type> + <type name="Magento\InventoryReservationCli\Command\ShowInconsistencies"> + <arguments> + <argument name="getSaleableQuantityInconsistencies" xsi:type="object"> + Magento\InventoryReservationCli\Model\GetSaleableQuantityInconsistencies\Proxy + </argument> + <argument name="filterCompleteOrders" xsi:type="object"> + Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterCompleteOrders\Proxy + </argument> + <argument name="filterIncompleteOrders" xsi:type="object"> + Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterIncompleteOrders\Proxy + </argument> + </arguments> + </type> + <type name="Magento\InventoryReservationCli\Command\CreateCompensations"> + <arguments> + <argument name="getSaleableQuantityInconsistencies" xsi:type="object"> + Magento\InventoryReservationCli\Model\GetSaleableQuantityInconsistencies\Proxy + </argument> + <argument name="getSaleableQuantityCompensations" xsi:type="object"> + Magento\InventoryReservationCli\Model\GetSaleableQuantityCompensations\Proxy + </argument> + <argument name="appendReservations" xsi:type="object"> + Magento\InventoryReservationsApi\Model\AppendReservationsInterface\Proxy + </argument> + <argument name="filterCompleteOrders" xsi:type="object"> + Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterCompleteOrders\Proxy + </argument> + <argument name="filterIncompleteOrders" xsi:type="object"> + Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterIncompleteOrders\Proxy + </argument> + </arguments> + </type> </config> From f446f3d5a85eae9dd50fcece8a86db1be08c1fde Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@magento.com> Date: Thu, 18 Apr 2019 14:36:44 -0500 Subject: [PATCH 170/231] Update AdminCreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipment.xml Corrected expected value --- ...roductOnDefaultStockAfterFullInvoiceAndPartialShipment.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipment.xml b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipment.xml index 4518eb46599c..d688873741b3 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipment.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithFullRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipment.xml @@ -131,7 +131,7 @@ <argument name="value" value="$$simpleProduct.sku$$"/> </actionGroup> <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="97" stepKey="checkProductSourceQtyAfterPartialShipment"/> - <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="90" stepKey="checkSalableQtyAfterPartialShipment"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="95" stepKey="checkSalableQtyAfterPartialShipment"/> <!--Admin Area Create Full Credit Memo--> <comment userInput="Admin - Create credit memo for full order" stepKey="AdminCreateCreditMemoFullOrder"/> @@ -157,4 +157,4 @@ <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="100" stepKey="checkSalableQtyAfterCreditMemo"/> </test> -</tests> \ No newline at end of file +</tests> From 889617f6ebcb13c238fea28e1a56d80734561e00 Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@magento.com> Date: Thu, 18 Apr 2019 15:21:28 -0500 Subject: [PATCH 171/231] Update LoggedInCustomerCreatedOrderWithSimpleProductOnTestStockFromHomepageTest.xml Editting before for freeShipping enable/disable --- ...CreatedOrderWithSimpleProductOnTestStockFromHomepageTest.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/InventoryAdminUi/Test/Mftf/Test/LoggedInCustomerCreatedOrderWithSimpleProductOnTestStockFromHomepageTest.xml b/InventoryAdminUi/Test/Mftf/Test/LoggedInCustomerCreatedOrderWithSimpleProductOnTestStockFromHomepageTest.xml index bf98d3123754..00626118902a 100644 --- a/InventoryAdminUi/Test/Mftf/Test/LoggedInCustomerCreatedOrderWithSimpleProductOnTestStockFromHomepageTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/LoggedInCustomerCreatedOrderWithSimpleProductOnTestStockFromHomepageTest.xml @@ -14,6 +14,8 @@ </annotations> <before> + <magentoCLI stepKey="enableFreeShipping" command="config:set carriers/freeshipping/active 1" /> + <magentoCLI stepKey="disableFreeShipping" command="config:set carriers/freeshipping/active 0"/> <createData entity="MsiCustomer1" stepKey="createCustomer"/> <createData entity="BasicMsiStockWithMainWebsite1" stepKey="createStock"/> <createData entity="FullSource1" stepKey="createSource"/> From 45c15226814ec3a6d65cb751ac825901ba1a68a2 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Fri, 19 Apr 2019 09:35:20 -0500 Subject: [PATCH 172/231] MFTF test fixes - select Stock Status to In Stock to make sure product is in stock --- ...lyXLeftTresholdForVirtualProductWithDefaultSourceTest.xml | 1 + .../Test/AdminCreateVirtualProductWithDefaultSourceTest.xml | 1 + .../AdminCreateDownloadableProductWithDefaultSourceTest.xml | 3 ++- ...ansferInventoryToSourceForDifferentTypeOfProductsTest.xml | 1 + .../Mftf/Test/AdminUserSetStatusForEachSourceItemTest.xml | 5 +++++ ...eredDownloadableProductOnDefaultStockFromHomepageTest.xml | 1 + 6 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Inventory/Test/Mftf/Test/AdminCanSetOnlyXLeftTresholdForVirtualProductWithDefaultSourceTest.xml b/Inventory/Test/Mftf/Test/AdminCanSetOnlyXLeftTresholdForVirtualProductWithDefaultSourceTest.xml index 0ff74c9ae70c..864bfd5202c2 100644 --- a/Inventory/Test/Mftf/Test/AdminCanSetOnlyXLeftTresholdForVirtualProductWithDefaultSourceTest.xml +++ b/Inventory/Test/Mftf/Test/AdminCanSetOnlyXLeftTresholdForVirtualProductWithDefaultSourceTest.xml @@ -64,6 +64,7 @@ <argument name="filter_value" value="$$virtualProduct1.product[sku]$$"/> </actionGroup> + <selectOption selector="{{AdminProductSourcesGrid.rowStatus('0')}}" userInput="In Stock" stepKey="selectStockStatus" /> <fillField selector="{{AdminProductSourcesGrid.rowQty('0')}}" userInput="10" stepKey="fillSourceQuantityField1"/> <scrollToTopOfPage stepKey="scrollToTopOfPage1"/> diff --git a/Inventory/Test/Mftf/Test/AdminCreateVirtualProductWithDefaultSourceTest.xml b/Inventory/Test/Mftf/Test/AdminCreateVirtualProductWithDefaultSourceTest.xml index d03e88037a5a..fdd548945105 100644 --- a/Inventory/Test/Mftf/Test/AdminCreateVirtualProductWithDefaultSourceTest.xml +++ b/Inventory/Test/Mftf/Test/AdminCreateVirtualProductWithDefaultSourceTest.xml @@ -58,6 +58,7 @@ <click selector="{{AdminGridColumnsControls.reset}}" stepKey="clickOnResetToRestoreDefaultColumns1"/> <click selector="{{AdminProductGridSection.productGridXRowYColumnButton('1', '2')}}" stepKey="openProductEditPage1"/> + <selectOption selector="{{AdminProductSourcesGrid.rowStatus('0')}}" userInput="In Stock" stepKey="selectStockStatus" /> <fillField selector="{{AdminProductSourcesGrid.rowQty('0')}}" userInput="100" stepKey="fillSourceQuantityField1"/> <scrollToTopOfPage stepKey="scrollToTopOfPage1"/> diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSourceTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSourceTest.xml index 0499f40fd1a9..e926e3fb5e60 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSourceTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSourceTest.xml @@ -61,7 +61,8 @@ <comment userInput="Assign category to product." stepKey="assignCategoryComment"/> <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$simpleCategory1.name$$]" requiredAction="true" stepKey="searchAndSelectCategory"/> - + <selectOption selector="{{AdminProductSourcesGrid.rowStatus('0')}}" userInput="In Stock" stepKey="selectStockStatus" /> + <fillField selector="{{AdminProductSourcesGrid.rowQty('0')}}" userInput="100" stepKey="fillDefaultSourceQtyField"/> <click selector="{{AdminProductDownloadableSection.sectionHeader}}" stepKey="openDownloadableSection1"/> <checkOption selector="{{AdminProductDownloadableSection.isDownloadableProduct}}" stepKey="checkIsDownloadable1"/> <fillField userInput="{{downloadableData.link_title}}" selector="{{AdminProductDownloadableSection.linksTitleInput}}" stepKey="fillDownloadableLinkTitle1"/> diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminMassActionTransferInventoryToSourceForDifferentTypeOfProductsTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminMassActionTransferInventoryToSourceForDifferentTypeOfProductsTest.xml index a24a312df07a..ef951e10f6f8 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminMassActionTransferInventoryToSourceForDifferentTypeOfProductsTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminMassActionTransferInventoryToSourceForDifferentTypeOfProductsTest.xml @@ -61,6 +61,7 @@ <click selector="{{AdminGridRow.editByValue($$createDownloadableProduct.product[sku]$$)}}" stepKey="clickOnEditDownloadableProductForCheckInStock"/> <comment userInput="Assign category to product." stepKey="assignCategoryComment"/> <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" requiredAction="true" stepKey="searchAndSelectCategory"/> + <selectOption selector="{{AdminProductSourcesGrid.rowStatus('0')}}" userInput="In Stock" stepKey="selectStockStatus" /> <fillField selector="{{AdminProductSourcesGrid.rowQty('0')}}" userInput="1000" stepKey="fillSourceQuantityField"/> <comment userInput="Add downloadable links to product." stepKey="addDownloadableLinks"/> <click selector="{{AdminProductDownloadableSection.sectionHeader}}" stepKey="openDownloadableSection"/> diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminUserSetStatusForEachSourceItemTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminUserSetStatusForEachSourceItemTest.xml index 83fafd56ee12..e1276dd462a4 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminUserSetStatusForEachSourceItemTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminUserSetStatusForEachSourceItemTest.xml @@ -83,10 +83,15 @@ <scrollToTopOfPage stepKey="scrollToTopOfPage1"/> <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$simpleCategory1.name$$]" requiredAction="true" stepKey="searchAndSelectCategory1"/> + <selectOption selector="{{AdminProductSourcesGrid.rowStatus('0')}}" userInput="In Stock" stepKey="selectStockStatusSource1" /> <fillField selector="{{AdminProductSourcesGrid.rowQty('0')}}" userInput="{{SimpleMsiProduct.quantity}}" stepKey="fillCustomSource1QtyField"/> + <selectOption selector="{{AdminProductSourcesGrid.rowStatus('1')}}" userInput="In Stock" stepKey="selectStockStatusSource2" /> <fillField selector="{{AdminProductSourcesGrid.rowQty('1')}}" userInput="{{SimpleMsiProduct.quantity}}" stepKey="fillCustomSource2QtyField"/> + <selectOption selector="{{AdminProductSourcesGrid.rowStatus('2')}}" userInput="In Stock" stepKey="selectStockStatusSource3" /> <fillField selector="{{AdminProductSourcesGrid.rowQty('2')}}" userInput="{{SimpleMsiProduct.quantity}}" stepKey="fillCustomSource3QtyField"/> + <selectOption selector="{{AdminProductSourcesGrid.rowStatus('3')}}" userInput="In Stock" stepKey="selectStockStatusSource4" /> <fillField selector="{{AdminProductSourcesGrid.rowQty('3')}}" userInput="{{SimpleMsiProduct.quantity}}" stepKey="fillCustomSource4QtyField"/> + <selectOption selector="{{AdminProductSourcesGrid.rowStatus('4')}}" userInput="In Stock" stepKey="selectStockStatusSource5" /> <fillField selector="{{AdminProductSourcesGrid.rowQty('4')}}" userInput="{{SimpleMsiProduct.quantity}}" stepKey="fillCustomSource5QtyField"/> <actionGroup ref="AdminFormSaveAndClose" stepKey="saveAndCloseSimpleProduct"/> diff --git a/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnDefaultStockFromHomepageTest.xml b/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnDefaultStockFromHomepageTest.xml index c9e18758e3f0..3cd040dd6dbc 100644 --- a/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnDefaultStockFromHomepageTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnDefaultStockFromHomepageTest.xml @@ -53,6 +53,7 @@ <requiredEntity createDataKey="downloadableProduct"/> </createData> <amOnPage url="{{AdminProductEditPage.url($$downloadableProduct.id$$)}}" stepKey="openProductEditPage" /> + <selectOption selector="{{AdminProductSourcesGrid.rowStatus('0')}}" userInput="In Stock" stepKey="selectStockStatus" /> <fillField selector="{{AdminProductSourcesGrid.rowQty('0')}}" userInput="100" stepKey="fillDefaultSourceQtyField"/> <actionGroup ref="AdminFormSaveAndClose" stepKey="saveAndCloseProduct"/> </before> From 12a43225f9781fb137b1d5e20e7bdfebe89e03d2 Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Fri, 19 Apr 2019 16:26:30 +0200 Subject: [PATCH 173/231] Removed GetAvailableSourceItemsBySkusAndSortedSourceInterface from API --- ...ilableSourceItemsBySkusAndSortedSource.php | 57 ---------------- ...leSourceItemsDataBySkusAndSortedSource.php | 53 --------------- Inventory/etc/di.xml | 1 - ...rceItemsBySkusAndSortedSourceInterface.php | 27 -------- .../Result/GetDefaultSortedSourcesResult.php | 26 +++----- ...ilableSourceItemsBySkusAndSortedSource.php | 66 +++++++++++++++++++ .../GetDefaultSortedSourcesResultTest.php | 17 +++++ InventorySourceSelectionApi/etc/di.xml | 9 +++ 8 files changed, 102 insertions(+), 154 deletions(-) delete mode 100644 Inventory/Model/GetAvailableSourceItemsBySkusAndSortedSource.php delete mode 100644 Inventory/Model/ResourceModel/GetAvailableSourceItemsDataBySkusAndSortedSource.php delete mode 100644 InventoryApi/Api/GetAvailableSourceItemsBySkusAndSortedSourceInterface.php create mode 100644 InventorySourceSelectionApi/Model/GetAvailableSourceItemsBySkusAndSortedSource.php diff --git a/Inventory/Model/GetAvailableSourceItemsBySkusAndSortedSource.php b/Inventory/Model/GetAvailableSourceItemsBySkusAndSortedSource.php deleted file mode 100644 index c8b698669bb8..000000000000 --- a/Inventory/Model/GetAvailableSourceItemsBySkusAndSortedSource.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Inventory\Model; - -use Magento\Inventory\Model\ResourceModel\GetAvailableSourceItemsDataBySkusAndSortedSource; -use Magento\InventoryApi\Api\Data\SourceItemInterfaceFactory; -use Magento\InventoryApi\Api\GetAvailableSourceItemsBySkusAndSortedSourceInterface; - -/** - * @inheritdoc - */ -class GetAvailableSourceItemsBySkusAndSortedSource implements GetAvailableSourceItemsBySkusAndSortedSourceInterface -{ - /** - * @var GetAvailableSourceItemsDataBySkusAndSortedSource - */ - private $getSourceItemsDataBySkusAndSortedSource; - - /** - * @var SourceItemInterfaceFactory - */ - private $sourceItemInterfaceFactory; - - /** - * GetSourceItemsBySkusAndSortedSource constructor. - * @param GetAvailableSourceItemsDataBySkusAndSortedSource $getSourceItemsDataBySkusAndSortedSource - * @param SourceItemInterfaceFactory $sourceItemInterfaceFactory - * @SuppressWarnings(PHPMD.LongVariable) - */ - public function __construct( - GetAvailableSourceItemsDataBySkusAndSortedSource $getSourceItemsDataBySkusAndSortedSource, - SourceItemInterfaceFactory $sourceItemInterfaceFactory - ) { - $this->getSourceItemsDataBySkusAndSortedSource = $getSourceItemsDataBySkusAndSortedSource; - $this->sourceItemInterfaceFactory = $sourceItemInterfaceFactory; - } - - /** - * @inheritdoc - */ - public function execute(array $skus, array $sortedSourceCodes): array - { - $res = []; - $sourceItemsData = $this->getSourceItemsDataBySkusAndSortedSource->execute($skus, $sortedSourceCodes); - - foreach ($sourceItemsData as $sourceItemData) { - $res[] = $this->sourceItemInterfaceFactory->create(['data' => $sourceItemData]); - } - - return $res; - } -} diff --git a/Inventory/Model/ResourceModel/GetAvailableSourceItemsDataBySkusAndSortedSource.php b/Inventory/Model/ResourceModel/GetAvailableSourceItemsDataBySkusAndSortedSource.php deleted file mode 100644 index dbf0fe2b1eb6..000000000000 --- a/Inventory/Model/ResourceModel/GetAvailableSourceItemsDataBySkusAndSortedSource.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Inventory\Model\ResourceModel; - -use Magento\Framework\App\ResourceConnection; -use Magento\InventoryApi\Api\Data\SourceItemInterface; - -/** - * Retrieve source items for a defined set of skus and sorted source codes - */ -class GetAvailableSourceItemsDataBySkusAndSortedSource -{ - /** - * @var ResourceConnection - */ - private $resourceConnection; - - /** - * @param ResourceConnection $resourceConnection - */ - public function __construct( - ResourceConnection $resourceConnection - ) { - $this->resourceConnection = $resourceConnection; - } - - /** - * @param array $skus - * @param array $sourceCodes - * @return array[] - */ - public function execute(array $skus, array $sourceCodes): array - { - $tableName = $this->resourceConnection->getTableName(SourceItem::TABLE_NAME_SOURCE_ITEM); - $connection = $this->resourceConnection->getConnection(); - - $qry = $connection - ->select() - ->from($tableName) - ->where(SourceItemInterface::SKU . ' IN (?)', $skus) - ->where(SourceItemInterface::SOURCE_CODE . ' IN (?)', $sourceCodes) - ->where(SourceItemInterface::QUANTITY . ' > 0') - ->where(SourceItemInterface::STATUS . ' = ?', SourceItemInterface::STATUS_IN_STOCK) - ->order($connection->quoteInto('FIELD(' . SourceItemInterface::SOURCE_CODE . ', ?)', $sourceCodes)); - - return $connection->fetchAll($qry) ?? []; - } -} diff --git a/Inventory/etc/di.xml b/Inventory/etc/di.xml index 71d238e5e5d5..889a57da39d2 100644 --- a/Inventory/etc/di.xml +++ b/Inventory/etc/di.xml @@ -90,5 +90,4 @@ </arguments> </type> <preference for="Magento\InventoryApi\Model\IsProductAssignedToStockInterface" type="Magento\Inventory\Model\ResourceModel\IsProductAssignedToStock"/> - <preference for="Magento\InventoryApi\Api\GetAvailableSourceItemsBySkusAndSortedSourceInterface" type="Magento\Inventory\Model\GetAvailableSourceItemsBySkusAndSortedSource" /> </config> diff --git a/InventoryApi/Api/GetAvailableSourceItemsBySkusAndSortedSourceInterface.php b/InventoryApi/Api/GetAvailableSourceItemsBySkusAndSortedSourceInterface.php deleted file mode 100644 index f48356d448c8..000000000000 --- a/InventoryApi/Api/GetAvailableSourceItemsBySkusAndSortedSourceInterface.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryApi\Api; - -/** - * Retrieve source items for a defined set of skus and sorted source codes - * - * Useful for determining availability and source selection - * - * @api - */ -interface GetAvailableSourceItemsBySkusAndSortedSourceInterface -{ - /** - * Get Source items assigned toa set of sorted source codes and return respecting the source codes order - * - * @param array $skus - * @param array $sortedSourceCodes - * @return \Magento\InventoryApi\Api\Data\SourceItemInterface[] - */ - public function execute(array $skus, array $sortedSourceCodes): array; -} diff --git a/InventorySourceSelectionApi/Model/Algorithms/Result/GetDefaultSortedSourcesResult.php b/InventorySourceSelectionApi/Model/Algorithms/Result/GetDefaultSortedSourcesResult.php index 35e53b92f108..dd0ba8af6f5b 100644 --- a/InventorySourceSelectionApi/Model/Algorithms/Result/GetDefaultSortedSourcesResult.php +++ b/InventorySourceSelectionApi/Model/Algorithms/Result/GetDefaultSortedSourcesResult.php @@ -9,14 +9,11 @@ use Magento\Framework\App\ObjectManager; use Magento\InventoryApi\Api\Data\SourceInterface; -use Magento\InventoryApi\Api\Data\SourceItemInterface; -use Magento\InventoryApi\Api\GetAvailableSourceItemsBySkusAndSortedSourceInterface; -use Magento\InventoryApi\Api\SourceItemRepositoryInterface; +use Magento\InventorySourceSelectionApi\Model\GetAvailableSourceItemsBySkusAndSortedSource; use Magento\InventorySourceSelectionApi\Api\Data\InventoryRequestInterface; use Magento\InventorySourceSelectionApi\Api\Data\SourceSelectionResultInterface; use Magento\InventorySourceSelectionApi\Api\Data\SourceSelectionItemInterfaceFactory; use Magento\InventorySourceSelectionApi\Api\Data\SourceSelectionResultInterfaceFactory; -use Magento\Framework\Api\SearchCriteriaBuilder; /** * Return a default response for sorted source algorithms @@ -34,31 +31,29 @@ class GetDefaultSortedSourcesResult private $sourceSelectionResultFactory; /** - * @var GetAvailableSourceItemsBySkusAndSortedSourceInterface + * @var GetAvailableSourceItemsBySkusAndSortedSource */ private $getAvailableSourceItemsBySkusAndSortedSource; /** - * GetDefaultSortedSourcesResult constructor. - * * @param SourceSelectionItemInterfaceFactory $sourceSelectionItemFactory * @param SourceSelectionResultInterfaceFactory $sourceSelectionResultFactory - * @param SearchCriteriaBuilder $searchCriteriaBuilder @deprecated - * @param SourceItemRepositoryInterface $sourceItemRepository @deprecated - * @param GetAvailableSourceItemsBySkusAndSortedSourceInterface $getAvailableSourceItemsBySkusAndSortedSource = null + * @param null $searchCriteriaBuilder @deprecated + * @param null $sourceItemRepository @deprecated + * @param GetAvailableSourceItemsBySkusAndSortedSource $getAvailableSourceItemsBySkusAndSortedSource = null * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( SourceSelectionItemInterfaceFactory $sourceSelectionItemFactory, SourceSelectionResultInterfaceFactory $sourceSelectionResultFactory, - SearchCriteriaBuilder $searchCriteriaBuilder, - SourceItemRepositoryInterface $sourceItemRepository, - GetAvailableSourceItemsBySkusAndSortedSourceInterface $getAvailableSourceItemsBySkusAndSortedSource = null + $searchCriteriaBuilder, + $sourceItemRepository, + GetAvailableSourceItemsBySkusAndSortedSource $getAvailableSourceItemsBySkusAndSortedSource = null ) { $this->sourceSelectionItemFactory = $sourceSelectionItemFactory; $this->sourceSelectionResultFactory = $sourceSelectionResultFactory; $this->getAvailableSourceItemsBySkusAndSortedSource = $getAvailableSourceItemsBySkusAndSortedSource ?: - ObjectManager::getInstance()->get(GetAvailableSourceItemsBySkusAndSortedSourceInterface::class); + ObjectManager::getInstance()->get(GetAvailableSourceItemsBySkusAndSortedSource::class); } /** @@ -84,12 +79,11 @@ public function execute( InventoryRequestInterface $inventoryRequest, array $sortedSources ): SourceSelectionResultInterface { - $isShippable = true; $sourceItemSelections = []; $itemsTdDeliver = []; foreach ($inventoryRequest->getItems() as $item) { - $itemsTdDeliver[$item->getSku()] = (float) $item->getQty(); + $itemsTdDeliver[$item->getSku()] = $item->getQty(); } $sortedSourceCodes = []; diff --git a/InventorySourceSelectionApi/Model/GetAvailableSourceItemsBySkusAndSortedSource.php b/InventorySourceSelectionApi/Model/GetAvailableSourceItemsBySkusAndSortedSource.php new file mode 100644 index 000000000000..2bd1f0856826 --- /dev/null +++ b/InventorySourceSelectionApi/Model/GetAvailableSourceItemsBySkusAndSortedSource.php @@ -0,0 +1,66 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventorySourceSelectionApi\Model; + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryApi\Api\SourceItemRepositoryInterface; + +/** + * Retrieve source items for a defined set of skus and sorted source codes + * + * Useful for determining availability and source selection + */ +class GetAvailableSourceItemsBySkusAndSortedSource +{ + /** + * @var SourceItemRepositoryInterface + */ + private $sourceItemRepository; + + /** + * @var SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + + /** + * @param SourceItemRepositoryInterface $sourceItemRepository + * @param SearchCriteriaBuilder $searchCriteriaBuilder + * @SuppressWarnings(PHPMD.LongVariable) + */ + public function __construct( + SourceItemRepositoryInterface $sourceItemRepository, + SearchCriteriaBuilder $searchCriteriaBuilder + ) { + $this->sourceItemRepository = $sourceItemRepository; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + } + + /** + * @inheritdoc + */ + public function execute(array $skus, array $sortedSourceCodes): array + { + $searchCriteria = $this->searchCriteriaBuilder + ->addFilter(SourceItemInterface::SKU, $skus, 'in') + ->addFilter(SourceItemInterface::SOURCE_CODE, $sortedSourceCodes, 'in') + ->addFilter(SourceItemInterface::QUANTITY, 0, 'gt') + ->addFilter(SourceItemInterface::STATUS, SourceItemInterface::STATUS_IN_STOCK) + ->create(); + + $items = $this->sourceItemRepository->getList($searchCriteria)->getItems(); + + $itemsSorting = []; + foreach ($items as $item) { + $itemsSorting[] = array_search($item->getSourceCode(), $sortedSourceCodes, true); + } + + array_multisort($itemsSorting, SORT_NUMERIC, SORT_ASC, $items); + return $items; + } +} diff --git a/InventorySourceSelectionApi/Test/Integration/GetDefaultSortedSourcesResultTest.php b/InventorySourceSelectionApi/Test/Integration/GetDefaultSortedSourcesResultTest.php index 5b4469a07f9e..6532c1497a9b 100644 --- a/InventorySourceSelectionApi/Test/Integration/GetDefaultSortedSourcesResultTest.php +++ b/InventorySourceSelectionApi/Test/Integration/GetDefaultSortedSourcesResultTest.php @@ -102,6 +102,23 @@ public function shouldReturnDefaultResultsDataProvider(): array ], false ], + [ + 10, + [ + ['sku' => 'SKU-1', 'qty' => 5], + ['sku' => 'SKU-2', 'qty' => 3], + ], + [ + 'eu-3', + 'eu-2', + 'eu-1', + ], + [ + 'eu-1/SKU-1' => ['deduct' => 2, 'avail' => 5.5], + 'eu-2/SKU-1' => ['deduct' => 3, 'avail' => 3], + ], + false + ], [ 20, [ diff --git a/InventorySourceSelectionApi/etc/di.xml b/InventorySourceSelectionApi/etc/di.xml index 80c5ed83b490..ddf9ef02e038 100644 --- a/InventorySourceSelectionApi/etc/di.xml +++ b/InventorySourceSelectionApi/etc/di.xml @@ -23,4 +23,13 @@ </argument> </arguments> </type> + + <type name="Magento\InventorySourceSelectionApi\Model\Algorithms\Result\GetDefaultSortedSourcesResult"> + <arguments> + <!-- Argument searchCriteriaBuilder is deprecated and it should not be used anymore --> + <argument name="searchCriteriaBuilder" xsi:type="null" /> + <!-- Argument sourceItemRepository is deprecated and it should not be used anymore --> + <argument name="sourceItemRepository" xsi:type="null" /> + </arguments> + </type> </config> From 9b7afa0dba6f884dd9d806ed87f92238fc859b9d Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Fri, 19 Apr 2019 14:37:53 -0500 Subject: [PATCH 174/231] Adding scrollTo filtergrid in ActionGroup and Adding disableSource to test after --- .../AdminGoToProductGridFilterResultsByInputActionGroup.xml | 1 + ...reatedOrderWithSimpleProductOnTestStockFromHomepageTest.xml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/InventoryAdminUi/Test/Mftf/ActionGroup/AdminGoToProductGridFilterResultsByInputActionGroup.xml b/InventoryAdminUi/Test/Mftf/ActionGroup/AdminGoToProductGridFilterResultsByInputActionGroup.xml index f378e73d9b68..f5736e4d9429 100644 --- a/InventoryAdminUi/Test/Mftf/ActionGroup/AdminGoToProductGridFilterResultsByInputActionGroup.xml +++ b/InventoryAdminUi/Test/Mftf/ActionGroup/AdminGoToProductGridFilterResultsByInputActionGroup.xml @@ -53,6 +53,7 @@ <conditionalClick selector="{{AdminGridFilterControls.clearAll}}" dependentSelector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .admin__data-grid-filters-current._show" visible="true" stepKey="clearTheFiltersIfPresent"/> <waitForPageLoad stepKey="waitForPageLoad" time="5"/> + <scrollTo selector="{{AdminGridFilterControls.filters}}"/> <click selector="{{AdminGridFilterControls.filters}}" stepKey="clickOnFilters1"/> <fillField userInput="{{filter_value}}" selector="{{filter_selector}}" stepKey="fillCodeField2"/> <click selector="{{AdminGridFilterControls.applyFilters}}" stepKey="clickOnApplyFilters1"/> diff --git a/InventoryAdminUi/Test/Mftf/Test/LoggedInCustomerCreatedOrderWithSimpleProductOnTestStockFromHomepageTest.xml b/InventoryAdminUi/Test/Mftf/Test/LoggedInCustomerCreatedOrderWithSimpleProductOnTestStockFromHomepageTest.xml index bf98d3123754..8a69dcf212c6 100644 --- a/InventoryAdminUi/Test/Mftf/Test/LoggedInCustomerCreatedOrderWithSimpleProductOnTestStockFromHomepageTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/LoggedInCustomerCreatedOrderWithSimpleProductOnTestStockFromHomepageTest.xml @@ -30,6 +30,9 @@ <waitForPageLoad stepKey="waitForDashboardLoad"/> </before> <after> + <actionGroup ref="DisableSourceActionGroup" stepKey="disableThirdCreatedSource"> + <argument name="sourceCode" value="$$createSource.source[source_code]$$"/> + </actionGroup> <actionGroup ref="logout" stepKey="logoutOfAdmin"/> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="simpleCategory" stepKey="deleteCategory"/> From 357e1d131c40647d64782a483e11d203fc145cf6 Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@magento.com> Date: Fri, 19 Apr 2019 15:26:25 -0500 Subject: [PATCH 175/231] Update AdminGoToProductGridFilterResultsByInputActionGroup.xml --- .../AdminGoToProductGridFilterResultsByInputActionGroup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InventoryAdminUi/Test/Mftf/ActionGroup/AdminGoToProductGridFilterResultsByInputActionGroup.xml b/InventoryAdminUi/Test/Mftf/ActionGroup/AdminGoToProductGridFilterResultsByInputActionGroup.xml index f5736e4d9429..3baf8f42448a 100644 --- a/InventoryAdminUi/Test/Mftf/ActionGroup/AdminGoToProductGridFilterResultsByInputActionGroup.xml +++ b/InventoryAdminUi/Test/Mftf/ActionGroup/AdminGoToProductGridFilterResultsByInputActionGroup.xml @@ -53,7 +53,7 @@ <conditionalClick selector="{{AdminGridFilterControls.clearAll}}" dependentSelector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .admin__data-grid-filters-current._show" visible="true" stepKey="clearTheFiltersIfPresent"/> <waitForPageLoad stepKey="waitForPageLoad" time="5"/> - <scrollTo selector="{{AdminGridFilterControls.filters}}"/> + <scrollTo selector="{{AdminGridFilterControls.filters}} stepKey="scrollToGridFilterControls"/> <click selector="{{AdminGridFilterControls.filters}}" stepKey="clickOnFilters1"/> <fillField userInput="{{filter_value}}" selector="{{filter_selector}}" stepKey="fillCodeField2"/> <click selector="{{AdminGridFilterControls.applyFilters}}" stepKey="clickOnApplyFilters1"/> From 49c7501c7530364abe5c2fae56f169be8b018f93 Mon Sep 17 00:00:00 2001 From: "al.kravchuk" <al.kravchuk@ism-ukraine.com> Date: Sat, 20 Apr 2019 10:34:59 +0300 Subject: [PATCH 176/231] MSI-2126 Immutable DTO for Pickup Location. Provide refactoring according to code review reworks. --- .../Model/PickupLocation.php | 4 + .../Model/PickupLocation/Mapper.php | 4 + .../Mapper/CreateFromSource.php | 114 ++++++++++++++---- .../Integration/PickupLocation/MapperTest.php | 30 +++++ .../Api/Data/PickupLocationInterface.php | 7 ++ 5 files changed, 134 insertions(+), 25 deletions(-) diff --git a/InventoryInStorePickup/Model/PickupLocation.php b/InventoryInStorePickup/Model/PickupLocation.php index 114e2d813a6f..00ef0e11df1e 100644 --- a/InventoryInStorePickup/Model/PickupLocation.php +++ b/InventoryInStorePickup/Model/PickupLocation.php @@ -10,6 +10,10 @@ use Magento\InventoryInStorePickupApi\Api\Data\PickupLocationExtensionInterface; use Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface; +/** + * @inheritdoc + * @codeCoverageIgnore + */ class PickupLocation implements PickupLocationInterface { /** diff --git a/InventoryInStorePickup/Model/PickupLocation/Mapper.php b/InventoryInStorePickup/Model/PickupLocation/Mapper.php index cc9b68497b45..f91c75c78d32 100644 --- a/InventoryInStorePickup/Model/PickupLocation/Mapper.php +++ b/InventoryInStorePickup/Model/PickupLocation/Mapper.php @@ -11,6 +11,10 @@ use Magento\InventoryInStorePickup\Model\PickupLocation\Mapper\CreateFromSource; use Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface; +/** + * Create projection of sources on In-Store Pickup context. + * Data transfer from source to projection will be done according to provided fields mapping. + */ class Mapper { /** diff --git a/InventoryInStorePickup/Model/PickupLocation/Mapper/CreateFromSource.php b/InventoryInStorePickup/Model/PickupLocation/Mapper/CreateFromSource.php index 5f7cf137c9ad..0291a61fcace 100644 --- a/InventoryInStorePickup/Model/PickupLocation/Mapper/CreateFromSource.php +++ b/InventoryInStorePickup/Model/PickupLocation/Mapper/CreateFromSource.php @@ -10,29 +10,33 @@ use Magento\Framework\Api\ExtensionAttributesFactory; use Magento\Framework\Api\SimpleDataObjectConverter; use Magento\InventoryApi\Api\Data\SourceInterface; -use Magento\InventoryInStorePickup\Model\PickupLocationFactory; use Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface; +use Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterfaceFactory; +/** + * Create Pickup Location based on Source. + * Transport data from Source to Pickup Location according to provided mapping. + */ class CreateFromSource { /** - * @var \Magento\InventoryInStorePickup\Model\PickupLocationFactory + * @var PickupLocationInterfaceFactory */ private $pickupLocationFactory; /** - * @var \Magento\Framework\Api\ExtensionAttributesFactory + * @var ExtensionAttributesFactory */ private $extensionAttributesFactory; /** * CreateFromSource constructor. * - * @param \Magento\InventoryInStorePickup\Model\PickupLocationFactory $pickupLocationFactory - * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionAttributesFactory + * @param PickupLocationInterfaceFactory $pickupLocationFactory + * @param ExtensionAttributesFactory $extensionAttributesFactory */ public function __construct( - PickupLocationFactory $pickupLocationFactory, + PickupLocationInterfaceFactory $pickupLocationFactory, ExtensionAttributesFactory $extensionAttributesFactory ) { $this->pickupLocationFactory = $pickupLocationFactory; @@ -40,37 +44,97 @@ public function __construct( } /** - * @param \Magento\InventoryApi\Api\Data\SourceInterface $source - * @param array $map + * @param SourceInterface $source + * @param array $map May contains references to fields in extension attributes. + * Please use format 'extension_attributes.field_name' to do so. E.g. + * [ + * "extension_attributes.source_field" => "pickup_location_field" + * "extension_attributes.source_field" => "extension_attributes.pickup_location_extension_field", + * ] + * @throws \InvalidArgumentException + * @return PickupLocationInterface + */ + public function execute(SourceInterface $source, array $map): PickupLocationInterface + { + $mappedData = $this->extractDataFromSource($source, $map); + $data = $this->preparePickupLocationFields($mappedData); + + return $this->pickupLocationFactory->create($data); + } + + /** + * @param array $mappedData * - * @return \Magento\InventoryInStorePickup\Model\PickupLocation + * @return array */ - public function execute(SourceInterface $source, array $map) + private function preparePickupLocationFields(array $mappedData): array { - $data = []; $pickupLocationExtension = $this->extensionAttributesFactory->create(PickupLocationInterface::class); + $pickupLocationMethods = get_class_methods(PickupLocationInterface::class); + $data = [ + 'extensionAttributes' => $pickupLocationExtension + ]; + + foreach ($mappedData as $pickupLocationField => $value) { + if ($this->isExtensionAttributeField($pickupLocationField)) { + $methodName = $this->getSetterMethodName($this->getExtensionAttributeFieldName($pickupLocationField)); + + if (!method_exists($pickupLocationExtension, $methodName)) { + $this->throwException(PickupLocationInterface::class); + } + $pickupLocationExtension->{$methodName}($value); + } else { + $methodName = $this->getGetterMethodName($pickupLocationField); + if (!in_array($methodName, $pickupLocationMethods)) { + $this->throwException(PickupLocationInterface::class); + } + $data[SimpleDataObjectConverter::snakeCaseToCamelCase($pickupLocationField)] = $value; + } + } + return $data; + } + + /** + * Extract values from Source according to the provided map. + * + * @param \Magento\InventoryApi\Api\Data\SourceInterface $source + * @param string[] $map + * + * @return array + */ + private function extractDataFromSource(SourceInterface $source, array $map): array + { + $mappedData = []; foreach ($map as $sourceField => $pickupLocationField) { if ($this->isExtensionAttributeField($sourceField)) { - $fieldValue = $source->getExtensionAttributes()->{$this->getGetterMethodName( - $this->getExtensionAttributeFieldName($sourceField) - )}(); + $methodName = $this->getGetterMethodName($this->getExtensionAttributeFieldName($sourceField)); + $entity = $source->getExtensionAttributes(); } else { - $fieldValue = $source->{$this->getGetterMethodName($sourceField)}(); + $methodName = $this->getGetterMethodName($sourceField); + $entity = $source; } - if ($this->isExtensionAttributeField($pickupLocationField)) { - $pickupLocationExtension->{$this->getSetterMethodName( - $this->getExtensionAttributeFieldName($pickupLocationField) - )}($fieldValue); - } else { - $data[SimpleDataObjectConverter::snakeCaseToCamelCase($pickupLocationField)] = $fieldValue; + if (!method_exists($entity, $methodName)) { + $this->throwException(SourceInterface::class); } + + $mappedData[$pickupLocationField] = $entity->{$methodName}(); } - $data['extensionAttributes'] = $pickupLocationExtension; + return $mappedData; + } - return $this->pickupLocationFactory->create($data); + /** + * Wrapper for throwing Invalid Argument Exception. + * + * @throws \InvalidArgumentException + * @param string $className + * @return void + */ + private function throwException(string $className): void + { + throw new \InvalidArgumentException("Wrong mapping provided for " . $className); } /** @@ -78,7 +142,7 @@ public function execute(SourceInterface $source, array $map) * * @return string */ - private function getExtensionAttributeFieldName($fieldName): string + private function getExtensionAttributeFieldName(string $fieldName): string { $field = explode('.', $fieldName); @@ -92,7 +156,7 @@ private function getExtensionAttributeFieldName($fieldName): string * * @return bool */ - private function isExtensionAttributeField($fieldName): bool + private function isExtensionAttributeField(string $fieldName): bool { return strpos($fieldName, 'extension_attributes.') === 0; } diff --git a/InventoryInStorePickup/Test/Integration/PickupLocation/MapperTest.php b/InventoryInStorePickup/Test/Integration/PickupLocation/MapperTest.php index 633c2a59567e..543c90730d2e 100644 --- a/InventoryInStorePickup/Test/Integration/PickupLocation/MapperTest.php +++ b/InventoryInStorePickup/Test/Integration/PickupLocation/MapperTest.php @@ -39,6 +39,36 @@ protected function setUp() $this->sourceCode = 'source-code-1'; } + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source.php + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Wrong mapping provided for Magento\InventoryApi\Api\Data\SourceInterface + */ + public function testWrongMappingForSource() + { + $source = $this->sourceRepository->get($this->sourceCode); + $map = $this->getMap(); + $map['fail_field'] = 'fail_field'; + /** @var \Magento\InventoryInStorePickup\Model\PickupLocation\Mapper $mapper */ + $mapper = $this->objectManager->create(Mapper::class, ['map' => $map]); + $mapper->map($source); + } + + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source.php + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Wrong mapping provided for Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface + */ + public function testWrongMappingForPickupLocation() + { + $source = $this->sourceRepository->get($this->sourceCode); + $map = $this->getMap(); + $map['name'] = 'fail_field'; + /** @var \Magento\InventoryInStorePickup\Model\PickupLocation\Mapper $mapper */ + $mapper = $this->objectManager->create(Mapper::class, ['map' => $map]); + $mapper->map($source); + } + /** * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source.php */ diff --git a/InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php b/InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php index 96d114190519..b594bbbaa889 100644 --- a/InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php +++ b/InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php @@ -7,6 +7,13 @@ use Magento\Framework\Api\ExtensibleDataInterface; +/** + * Represents sources projection on In-Store Pickup context. + * Realisation must follow immutable DTO concept. + * Partial immutability done according to restriction of current Extension Attributes implementation. + * + * @api + */ interface PickupLocationInterface extends ExtensibleDataInterface { const IS_PICKUP_LOCATION_ACTIVE = 'is_pickup_location_active'; From 9964f7715304982082ab1b6146ebc78076e353fd Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Sat, 20 Apr 2019 19:14:15 +0200 Subject: [PATCH 177/231] FIX tests --- .../GetSourceItemsBySkusAndSortedSourceTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename {Inventory => InventorySourceSelectionApi}/Test/Integration/GetSourceItemsBySkusAndSortedSourceTest.php (90%) diff --git a/Inventory/Test/Integration/GetSourceItemsBySkusAndSortedSourceTest.php b/InventorySourceSelectionApi/Test/Integration/GetSourceItemsBySkusAndSortedSourceTest.php similarity index 90% rename from Inventory/Test/Integration/GetSourceItemsBySkusAndSortedSourceTest.php rename to InventorySourceSelectionApi/Test/Integration/GetSourceItemsBySkusAndSortedSourceTest.php index 5aac2ef011dd..f3c401845a45 100644 --- a/Inventory/Test/Integration/GetSourceItemsBySkusAndSortedSourceTest.php +++ b/InventorySourceSelectionApi/Test/Integration/GetSourceItemsBySkusAndSortedSourceTest.php @@ -5,17 +5,17 @@ */ declare(strict_types=1); -namespace Magento\Inventory\Test\Integration; +namespace Magento\InventorySourceSelectionApi\Test\Integration; use Magento\InventoryApi\Api\Data\SourceItemInterface; -use Magento\InventoryApi\Api\GetAvailableSourceItemsBySkusAndSortedSourceInterface; +use Magento\InventorySourceSelectionApi\Model\GetAvailableSourceItemsBySkusAndSortedSource; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; class GetSourceItemsBySkusAndSortedSourceTest extends TestCase { /** - * @var GetAvailableSourceItemsBySkusAndSortedSourceInterface + * @var GetAvailableSourceItemsBySkusAndSortedSource */ private $subject; @@ -23,7 +23,7 @@ protected function setUp() { parent::setUp(); - $this->subject = Bootstrap::getObjectManager()->get(GetAvailableSourceItemsBySkusAndSortedSourceInterface::class); + $this->subject = Bootstrap::getObjectManager()->get(GetAvailableSourceItemsBySkusAndSortedSource::class); } /** From 92ac5d2d9fca691433bde4eaa84f794884581a8d Mon Sep 17 00:00:00 2001 From: "al.kravchuk" <al.kravchuk@ism-ukraine.com> Date: Sat, 20 Apr 2019 11:25:43 +0300 Subject: [PATCH 178/231] MSI-2126 Immutable DTO for Pickup Location. Expand exception with more details. --- .../Mapper/CreateFromSource.php | 15 +++++++------ .../Integration/PickupLocation/MapperTest.php | 21 ++++++++++++++++--- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/InventoryInStorePickup/Model/PickupLocation/Mapper/CreateFromSource.php b/InventoryInStorePickup/Model/PickupLocation/Mapper/CreateFromSource.php index 0291a61fcace..cf949f4c63d4 100644 --- a/InventoryInStorePickup/Model/PickupLocation/Mapper/CreateFromSource.php +++ b/InventoryInStorePickup/Model/PickupLocation/Mapper/CreateFromSource.php @@ -80,13 +80,13 @@ private function preparePickupLocationFields(array $mappedData): array $methodName = $this->getSetterMethodName($this->getExtensionAttributeFieldName($pickupLocationField)); if (!method_exists($pickupLocationExtension, $methodName)) { - $this->throwException(PickupLocationInterface::class); + $this->throwException(PickupLocationInterface::class, $pickupLocationField); } $pickupLocationExtension->{$methodName}($value); } else { $methodName = $this->getGetterMethodName($pickupLocationField); if (!in_array($methodName, $pickupLocationMethods)) { - $this->throwException(PickupLocationInterface::class); + $this->throwException(PickupLocationInterface::class, $pickupLocationField); } $data[SimpleDataObjectConverter::snakeCaseToCamelCase($pickupLocationField)] = $value; } @@ -116,7 +116,7 @@ private function extractDataFromSource(SourceInterface $source, array $map): arr } if (!method_exists($entity, $methodName)) { - $this->throwException(SourceInterface::class); + $this->throwException(SourceInterface::class, $sourceField); } $mappedData[$pickupLocationField] = $entity->{$methodName}(); @@ -128,13 +128,16 @@ private function extractDataFromSource(SourceInterface $source, array $map): arr /** * Wrapper for throwing Invalid Argument Exception. * - * @throws \InvalidArgumentException * @param string $className + * @param string $fieldName + * * @return void */ - private function throwException(string $className): void + private function throwException(string $className, string $fieldName): void { - throw new \InvalidArgumentException("Wrong mapping provided for " . $className); + $message = "Wrong mapping provided for %s. Field '%s' is not found."; + + throw new \InvalidArgumentException(sprintf($message, $className, $fieldName)); } /** diff --git a/InventoryInStorePickup/Test/Integration/PickupLocation/MapperTest.php b/InventoryInStorePickup/Test/Integration/PickupLocation/MapperTest.php index 543c90730d2e..9671b542b9ce 100644 --- a/InventoryInStorePickup/Test/Integration/PickupLocation/MapperTest.php +++ b/InventoryInStorePickup/Test/Integration/PickupLocation/MapperTest.php @@ -42,13 +42,13 @@ protected function setUp() /** * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source.php * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Wrong mapping provided for Magento\InventoryApi\Api\Data\SourceInterface + * @expectedExceptionMessage Wrong mapping provided for Magento\InventoryApi\Api\Data\SourceInterface. Field 'source_fail_field' is not found. */ public function testWrongMappingForSource() { $source = $this->sourceRepository->get($this->sourceCode); $map = $this->getMap(); - $map['fail_field'] = 'fail_field'; + $map['source_fail_field'] = 'fail_field'; /** @var \Magento\InventoryInStorePickup\Model\PickupLocation\Mapper $mapper */ $mapper = $this->objectManager->create(Mapper::class, ['map' => $map]); $mapper->map($source); @@ -57,7 +57,22 @@ public function testWrongMappingForSource() /** * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source.php * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Wrong mapping provided for Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface + * @expectedExceptionMessage Wrong mapping provided for Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface. Field 'extension_attributes.fail_field' is not found. + */ + public function testWrongMappingForPickupLocationExtensionAttributes() + { + $source = $this->sourceRepository->get($this->sourceCode); + $map = $this->getMap(); + $map['name'] = 'extension_attributes.fail_field'; + /** @var \Magento\InventoryInStorePickup\Model\PickupLocation\Mapper $mapper */ + $mapper = $this->objectManager->create(Mapper::class, ['map' => $map]); + $mapper->map($source); + } + + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source.php + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Wrong mapping provided for Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface. Field 'fail_field' is not found. */ public function testWrongMappingForPickupLocation() { From 5cb6d48eb05a424de1581a4af537bbf57cd3d250 Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@magento.com> Date: Mon, 22 Apr 2019 08:58:53 -0500 Subject: [PATCH 179/231] Update AdminGoToProductGridFilterResultsByInputActionGroup.xml Fix Missing quote on line 56 --- .../AdminGoToProductGridFilterResultsByInputActionGroup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InventoryAdminUi/Test/Mftf/ActionGroup/AdminGoToProductGridFilterResultsByInputActionGroup.xml b/InventoryAdminUi/Test/Mftf/ActionGroup/AdminGoToProductGridFilterResultsByInputActionGroup.xml index 3baf8f42448a..d1b8a75885f8 100644 --- a/InventoryAdminUi/Test/Mftf/ActionGroup/AdminGoToProductGridFilterResultsByInputActionGroup.xml +++ b/InventoryAdminUi/Test/Mftf/ActionGroup/AdminGoToProductGridFilterResultsByInputActionGroup.xml @@ -53,7 +53,7 @@ <conditionalClick selector="{{AdminGridFilterControls.clearAll}}" dependentSelector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .admin__data-grid-filters-current._show" visible="true" stepKey="clearTheFiltersIfPresent"/> <waitForPageLoad stepKey="waitForPageLoad" time="5"/> - <scrollTo selector="{{AdminGridFilterControls.filters}} stepKey="scrollToGridFilterControls"/> + <scrollTo selector="{{AdminGridFilterControls.filters}}" stepKey="scrollToGridFilterControls"/> <click selector="{{AdminGridFilterControls.filters}}" stepKey="clickOnFilters1"/> <fillField userInput="{{filter_value}}" selector="{{filter_selector}}" stepKey="fillCodeField2"/> <click selector="{{AdminGridFilterControls.applyFilters}}" stepKey="clickOnApplyFilters1"/> From 89c600afab4d5457d597d83f42e00dff566e2ae2 Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Mon, 22 Apr 2019 16:07:40 -0500 Subject: [PATCH 180/231] Fixing MSI-1527 in EE/B2B MSI-1527 failing on EE and B2B. Attempting fix by assigning stock to main website upon API creation. --- ...plyOnlyXLeftThresholdForSimpleProductOnDefaultSourceTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminUserApplyOnlyXLeftThresholdForSimpleProductOnDefaultSourceTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminUserApplyOnlyXLeftThresholdForSimpleProductOnDefaultSourceTest.xml index 6fdcf6564232..9db15c688544 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminUserApplyOnlyXLeftThresholdForSimpleProductOnDefaultSourceTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminUserApplyOnlyXLeftThresholdForSimpleProductOnDefaultSourceTest.xml @@ -28,7 +28,7 @@ <magentoCLI command="cache:flush" stepKey="cleanCacheAfterFillThresholdConfig"/> <createData entity="_minimalSource" stepKey="createSource"/> - <createData entity="BasicMsiStock1" stepKey="createStock"/> + <createData entity="BasicMsiStockWithMainWebsite1" stepKey="createStock"/> <createData entity="SourceStockLinked1" stepKey="stockSourceLink"> <requiredEntity createDataKey="createStock"/> From 56991f21d70ae9389c4246327f463355a112f6a7 Mon Sep 17 00:00:00 2001 From: Riccardo Tempesta <riccardo.tempesta@gmail.com> Date: Tue, 23 Apr 2019 19:33:09 +0200 Subject: [PATCH 181/231] Removed qty filtering at MySQL level and increased Test coverage --- .../Result/GetDefaultSortedSourcesResult.php | 16 ++++++++-------- ...tInStockSourceItemsBySkusAndSortedSource.php} | 11 +++++++---- .../GetSourceItemsBySkusAndSortedSourceTest.php | 15 ++++++++++++--- 3 files changed, 27 insertions(+), 15 deletions(-) rename InventorySourceSelectionApi/Model/{GetAvailableSourceItemsBySkusAndSortedSource.php => GetInStockSourceItemsBySkusAndSortedSource.php} (89%) diff --git a/InventorySourceSelectionApi/Model/Algorithms/Result/GetDefaultSortedSourcesResult.php b/InventorySourceSelectionApi/Model/Algorithms/Result/GetDefaultSortedSourcesResult.php index dd0ba8af6f5b..9c8d04c6b5e2 100644 --- a/InventorySourceSelectionApi/Model/Algorithms/Result/GetDefaultSortedSourcesResult.php +++ b/InventorySourceSelectionApi/Model/Algorithms/Result/GetDefaultSortedSourcesResult.php @@ -9,7 +9,7 @@ use Magento\Framework\App\ObjectManager; use Magento\InventoryApi\Api\Data\SourceInterface; -use Magento\InventorySourceSelectionApi\Model\GetAvailableSourceItemsBySkusAndSortedSource; +use Magento\InventorySourceSelectionApi\Model\GetInStockSourceItemsBySkusAndSortedSource; use Magento\InventorySourceSelectionApi\Api\Data\InventoryRequestInterface; use Magento\InventorySourceSelectionApi\Api\Data\SourceSelectionResultInterface; use Magento\InventorySourceSelectionApi\Api\Data\SourceSelectionItemInterfaceFactory; @@ -31,16 +31,16 @@ class GetDefaultSortedSourcesResult private $sourceSelectionResultFactory; /** - * @var GetAvailableSourceItemsBySkusAndSortedSource + * @var GetInStockSourceItemsBySkusAndSortedSource */ - private $getAvailableSourceItemsBySkusAndSortedSource; + private $getInStockSourceItemsBySkusAndSortedSource; /** * @param SourceSelectionItemInterfaceFactory $sourceSelectionItemFactory * @param SourceSelectionResultInterfaceFactory $sourceSelectionResultFactory * @param null $searchCriteriaBuilder @deprecated * @param null $sourceItemRepository @deprecated - * @param GetAvailableSourceItemsBySkusAndSortedSource $getAvailableSourceItemsBySkusAndSortedSource = null + * @param GetInStockSourceItemsBySkusAndSortedSource $getInStockSourceItemsBySkusAndSortedSource = null * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( @@ -48,12 +48,12 @@ public function __construct( SourceSelectionResultInterfaceFactory $sourceSelectionResultFactory, $searchCriteriaBuilder, $sourceItemRepository, - GetAvailableSourceItemsBySkusAndSortedSource $getAvailableSourceItemsBySkusAndSortedSource = null + GetInStockSourceItemsBySkusAndSortedSource $getInStockSourceItemsBySkusAndSortedSource = null ) { $this->sourceSelectionItemFactory = $sourceSelectionItemFactory; $this->sourceSelectionResultFactory = $sourceSelectionResultFactory; - $this->getAvailableSourceItemsBySkusAndSortedSource = $getAvailableSourceItemsBySkusAndSortedSource ?: - ObjectManager::getInstance()->get(GetAvailableSourceItemsBySkusAndSortedSource::class); + $this->getInStockSourceItemsBySkusAndSortedSource = $getInStockSourceItemsBySkusAndSortedSource ?: + ObjectManager::getInstance()->get(GetInStockSourceItemsBySkusAndSortedSource::class); } /** @@ -92,7 +92,7 @@ public function execute( } $sourceItems = - $this->getAvailableSourceItemsBySkusAndSortedSource->execute( + $this->getInStockSourceItemsBySkusAndSortedSource->execute( array_keys($itemsTdDeliver), $sortedSourceCodes ); diff --git a/InventorySourceSelectionApi/Model/GetAvailableSourceItemsBySkusAndSortedSource.php b/InventorySourceSelectionApi/Model/GetInStockSourceItemsBySkusAndSortedSource.php similarity index 89% rename from InventorySourceSelectionApi/Model/GetAvailableSourceItemsBySkusAndSortedSource.php rename to InventorySourceSelectionApi/Model/GetInStockSourceItemsBySkusAndSortedSource.php index 2bd1f0856826..4bd9a18316c7 100644 --- a/InventorySourceSelectionApi/Model/GetAvailableSourceItemsBySkusAndSortedSource.php +++ b/InventorySourceSelectionApi/Model/GetInStockSourceItemsBySkusAndSortedSource.php @@ -14,9 +14,11 @@ /** * Retrieve source items for a defined set of skus and sorted source codes * - * Useful for determining availability and source selection + * Useful for determining presence in stock and source selection + * + * @api */ -class GetAvailableSourceItemsBySkusAndSortedSource +class GetInStockSourceItemsBySkusAndSortedSource { /** * @var SourceItemRepositoryInterface @@ -42,14 +44,15 @@ public function __construct( } /** - * @inheritdoc + * @param array $skus + * @param array $sortedSourceCodes + * @return SourceItemInterface[] */ public function execute(array $skus, array $sortedSourceCodes): array { $searchCriteria = $this->searchCriteriaBuilder ->addFilter(SourceItemInterface::SKU, $skus, 'in') ->addFilter(SourceItemInterface::SOURCE_CODE, $sortedSourceCodes, 'in') - ->addFilter(SourceItemInterface::QUANTITY, 0, 'gt') ->addFilter(SourceItemInterface::STATUS, SourceItemInterface::STATUS_IN_STOCK) ->create(); diff --git a/InventorySourceSelectionApi/Test/Integration/GetSourceItemsBySkusAndSortedSourceTest.php b/InventorySourceSelectionApi/Test/Integration/GetSourceItemsBySkusAndSortedSourceTest.php index f3c401845a45..42a53cd61627 100644 --- a/InventorySourceSelectionApi/Test/Integration/GetSourceItemsBySkusAndSortedSourceTest.php +++ b/InventorySourceSelectionApi/Test/Integration/GetSourceItemsBySkusAndSortedSourceTest.php @@ -8,14 +8,14 @@ namespace Magento\InventorySourceSelectionApi\Test\Integration; use Magento\InventoryApi\Api\Data\SourceItemInterface; -use Magento\InventorySourceSelectionApi\Model\GetAvailableSourceItemsBySkusAndSortedSource; +use Magento\InventorySourceSelectionApi\Model\GetInStockSourceItemsBySkusAndSortedSource; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; class GetSourceItemsBySkusAndSortedSourceTest extends TestCase { /** - * @var GetAvailableSourceItemsBySkusAndSortedSource + * @var GetInStockSourceItemsBySkusAndSortedSource */ private $subject; @@ -23,7 +23,7 @@ protected function setUp() { parent::setUp(); - $this->subject = Bootstrap::getObjectManager()->get(GetAvailableSourceItemsBySkusAndSortedSource::class); + $this->subject = Bootstrap::getObjectManager()->get(GetInStockSourceItemsBySkusAndSortedSource::class); } /** @@ -47,6 +47,15 @@ public function shouldReturnSortedSourceItemsDataProvider(): array 'eu-2/SKU-1' => 3.0, 'eu-1/SKU-1' => 5.5, ] + ], + [ + ['SKU-1', 'SKU-2', 'SKU-3', 'SKU-6'], + ['eu-3', 'eu-2', 'eu-1'], + [ + 'eu-2/SKU-1' => 3.0, + 'eu-1/SKU-1' => 5.5, + 'eu-1/SKU-6' => 0.0 + ] ] ]; } From 3dee3f3f6348be84b23285e0c3bf80f03492e918 Mon Sep 17 00:00:00 2001 From: Vadim Justus <v.justus@techdivision.com> Date: Tue, 23 Apr 2019 20:25:05 +0200 Subject: [PATCH 182/231] magento-engcom/msi#2170: Change naming of 'SaleableQuantity' to 'SalableQuantity' --- .../Command/CreateCompensations.php | 58 ++++++++----------- .../Command/ShowInconsistencies.php | 40 ++++++++----- ...hp => GetSalableQuantityCompensations.php} | 4 +- ... => GetSalableQuantityInconsistencies.php} | 24 ++++---- ...y.php => SalableQuantityInconsistency.php} | 2 +- ...letedOrdersToForUnresolvedReservations.php | 2 +- .../AddExistingReservations.php | 2 +- .../AddExpectedReservations.php | 2 +- .../Collector.php | 26 ++++----- .../FilterCompleteOrders.php | 10 ++-- .../FilterExistingOrders.php | 10 ++-- .../FilterIncompleteOrders.php | 10 ++-- .../FilterManagedStockProducts.php | 8 +-- .../FilterUnresolvedReservations.php | 10 ++-- ...GetSalableQuantityInconsistenciesTest.php} | 20 +++---- 15 files changed, 114 insertions(+), 114 deletions(-) rename InventoryReservationCli/Model/{GetSaleableQuantityCompensations.php => GetSalableQuantityCompensations.php} (95%) rename InventoryReservationCli/Model/{GetSaleableQuantityInconsistencies.php => GetSalableQuantityInconsistencies.php} (74%) rename InventoryReservationCli/Model/{SaleableQuantityInconsistency.php => SalableQuantityInconsistency.php} (98%) rename InventoryReservationCli/Model/{SaleableQuantityInconsistency => SalableQuantityInconsistency}/AddCompletedOrdersToForUnresolvedReservations.php (93%) rename InventoryReservationCli/Model/{SaleableQuantityInconsistency => SalableQuantityInconsistency}/AddExistingReservations.php (96%) rename InventoryReservationCli/Model/{SaleableQuantityInconsistency => SalableQuantityInconsistency}/AddExpectedReservations.php (97%) rename InventoryReservationCli/Model/{SaleableQuantityInconsistency => SalableQuantityInconsistency}/Collector.php (71%) rename InventoryReservationCli/Model/{SaleableQuantityInconsistency => SalableQuantityInconsistency}/FilterCompleteOrders.php (75%) rename InventoryReservationCli/Model/{SaleableQuantityInconsistency => SalableQuantityInconsistency}/FilterExistingOrders.php (60%) rename InventoryReservationCli/Model/{SaleableQuantityInconsistency => SalableQuantityInconsistency}/FilterIncompleteOrders.php (75%) rename InventoryReservationCli/Model/{SaleableQuantityInconsistency => SalableQuantityInconsistency}/FilterManagedStockProducts.php (88%) rename InventoryReservationCli/Model/{SaleableQuantityInconsistency => SalableQuantityInconsistency}/FilterUnresolvedReservations.php (65%) rename InventoryReservationCli/Test/Integration/Model/{GetSaleableQuantityInconsistenciesTest.php => GetSalableQuantityInconsistenciesTest.php} (72%) diff --git a/InventoryReservationCli/Command/CreateCompensations.php b/InventoryReservationCli/Command/CreateCompensations.php index 4a3073e15de4..45a253675070 100644 --- a/InventoryReservationCli/Command/CreateCompensations.php +++ b/InventoryReservationCli/Command/CreateCompensations.php @@ -10,14 +10,15 @@ use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\InputException; use Magento\Framework\Validation\ValidationException; -use Magento\InventoryReservationCli\Model\GetSaleableQuantityCompensations\Proxy as GetSaleableQuantityCompensations; -use Magento\InventoryReservationCli\Model\GetSaleableQuantityInconsistencies\Proxy as GetSaleableQuantityInconsistencies; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterCompleteOrders\Proxy as FilterCompleteOrders; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterIncompleteOrders\Proxy as FilterIncompleteOrders; +use Magento\InventoryReservationCli\Model\GetSalableQuantityCompensations\Proxy as GetSalableQuantityCompensations; +use Magento\InventoryReservationCli\Model\GetSalableQuantityInconsistencies\Proxy as GetSalableQuantityInconsistencies; +use Magento\InventoryReservationCli\Model\SalableQuantityInconsistency; +use Magento\InventoryReservationCli\Model\SalableQuantityInconsistency\FilterCompleteOrders\Proxy as FilterCompleteOrders; +use Magento\InventoryReservationCli\Model\SalableQuantityInconsistency\FilterIncompleteOrders\Proxy as FilterIncompleteOrders; use Magento\InventoryReservationsApi\Model\AppendReservationsInterface\Proxy as AppendReservationsInterface; use Magento\InventoryReservationsApi\Model\ReservationInterface; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -31,9 +32,9 @@ class CreateCompensations extends Command { /** - * @var GetSaleableQuantityInconsistencies + * @var GetSalableQuantityInconsistencies */ - private $getSaleableQuantityInconsistencies; + private $getSalableQuantityInconsistencies; /** * @var FilterCompleteOrders @@ -46,9 +47,9 @@ class CreateCompensations extends Command private $filterIncompleteOrders; /** - * @var GetSaleableQuantityCompensations + * @var GetSalableQuantityCompensations */ - private $getSaleableQuantityCompensations; + private $getSalableQuantityCompensations; /** * @var AppendReservationsInterface @@ -56,24 +57,24 @@ class CreateCompensations extends Command private $appendReservations; /** - * @param GetSaleableQuantityInconsistencies $getSaleableQuantityInconsistencies - * @param GetSaleableQuantityCompensations $getSaleableQuantityCompensations + * @param GetSalableQuantityInconsistencies $getSalableQuantityInconsistencies + * @param GetSalableQuantityCompensations $getSalableQuantityCompensations * @param AppendReservationsInterface $appendReservations * @param FilterCompleteOrders $filterCompleteOrders * @param FilterIncompleteOrders $filterIncompleteOrders */ public function __construct( - GetSaleableQuantityInconsistencies $getSaleableQuantityInconsistencies, - GetSaleableQuantityCompensations $getSaleableQuantityCompensations, + GetSalableQuantityInconsistencies $getSalableQuantityInconsistencies, + GetSalableQuantityCompensations $getSalableQuantityCompensations, AppendReservationsInterface $appendReservations, FilterCompleteOrders $filterCompleteOrders, FilterIncompleteOrders $filterIncompleteOrders ) { parent::__construct(); - $this->getSaleableQuantityInconsistencies = $getSaleableQuantityInconsistencies; + $this->getSalableQuantityInconsistencies = $getSalableQuantityInconsistencies; $this->filterCompleteOrders = $filterCompleteOrders; $this->filterIncompleteOrders = $filterIncompleteOrders; - $this->getSaleableQuantityCompensations = $getSaleableQuantityCompensations; + $this->getSalableQuantityCompensations = $getSalableQuantityCompensations; $this->appendReservations = $appendReservations; } @@ -85,23 +86,10 @@ protected function configure() $this ->setName('inventory:reservation:create-compensations') ->setDescription('Create compensation reservations for detected inconsistencies') - ->addOption( - 'complete-orders', - 'c', - InputOption::VALUE_NONE, - 'Compensate only inconsistencies for completed orders' - ) - ->addOption( - 'incomplete-orders', - 'i', - InputOption::VALUE_NONE, - 'Compensate only inconsistencies for incomplete orders' - ) - ->addOption( - 'dry-run', - 'd', - InputOption::VALUE_NONE, - 'Display result without applying reservations' + ->addArgument( + 'compensations', + InputArgument::IS_ARRAY, + 'List of compensation arguments in format ' ) ->addOption( 'raw', @@ -157,12 +145,12 @@ private function rawOutput(OutputInterface $output, array $compensations): void /** * @param InputInterface $input - * @return SaleableQuantityInconsistency[] + * @return SalableQuantityInconsistency[] * @throws ValidationException */ private function getFilteredInconsistencies(InputInterface $input): array { - $inconsistencies = $this->getSaleableQuantityInconsistencies->execute(); + $inconsistencies = $this->getSalableQuantityInconsistencies->execute(); if ($input->getOption('complete-orders')) { $inconsistencies = $this->filterCompleteOrders->execute($inconsistencies); @@ -186,7 +174,7 @@ private function getFilteredInconsistencies(InputInterface $input): array public function execute(InputInterface $input, OutputInterface $output): int { $inconsistencies = $this->getFilteredInconsistencies($input); - $compensations = $this->getSaleableQuantityCompensations->execute($inconsistencies); + $compensations = $this->getSalableQuantityCompensations->execute($inconsistencies); if (empty($compensations)) { $output->writeln('<info>No required compensations calculated.</info>'); diff --git a/InventoryReservationCli/Command/ShowInconsistencies.php b/InventoryReservationCli/Command/ShowInconsistencies.php index 151a1c5eecd5..19a709d5bc90 100644 --- a/InventoryReservationCli/Command/ShowInconsistencies.php +++ b/InventoryReservationCli/Command/ShowInconsistencies.php @@ -8,10 +8,10 @@ namespace Magento\InventoryReservationCli\Command; use Magento\Framework\Validation\ValidationException; -use Magento\InventoryReservationCli\Model\GetSaleableQuantityInconsistencies\Proxy as GetSaleableQuantityInconsistencies; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterCompleteOrders\Proxy as FilterCompleteOrders; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterIncompleteOrders\Proxy as FilterIncompleteOrders; +use Magento\InventoryReservationCli\Model\GetSalableQuantityInconsistencies\Proxy as GetSalableQuantityInconsistencies; +use Magento\InventoryReservationCli\Model\SalableQuantityInconsistency; +use Magento\InventoryReservationCli\Model\SalableQuantityInconsistency\FilterCompleteOrders\Proxy as FilterCompleteOrders; +use Magento\InventoryReservationCli\Model\SalableQuantityInconsistency\FilterIncompleteOrders\Proxy as FilterIncompleteOrders; use Magento\Sales\Model\Order; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; @@ -27,9 +27,9 @@ class ShowInconsistencies extends Command { /** - * @var GetSaleableQuantityInconsistencies + * @var GetSalableQuantityInconsistencies */ - private $getSaleableQuantityInconsistencies; + private $getSalableQuantityInconsistencies; /** * @var FilterCompleteOrders @@ -42,17 +42,17 @@ class ShowInconsistencies extends Command private $filterIncompleteOrders; /** - * @param GetSaleableQuantityInconsistencies $getSaleableQuantityInconsistencies + * @param GetSalableQuantityInconsistencies $getSalableQuantityInconsistencies * @param FilterCompleteOrders $filterCompleteOrders * @param FilterIncompleteOrders $filterIncompleteOrders */ public function __construct( - GetSaleableQuantityInconsistencies $getSaleableQuantityInconsistencies, + GetSalableQuantityInconsistencies $getSalableQuantityInconsistencies, FilterCompleteOrders $filterCompleteOrders, FilterIncompleteOrders $filterIncompleteOrders ) { parent::__construct(); - $this->getSaleableQuantityInconsistencies = $getSaleableQuantityInconsistencies; + $this->getSalableQuantityInconsistencies = $getSalableQuantityInconsistencies; $this->filterCompleteOrders = $filterCompleteOrders; $this->filterIncompleteOrders = $filterIncompleteOrders; } @@ -64,7 +64,7 @@ protected function configure() { $this ->setName('inventory:reservation:list-inconsistencies') - ->setDescription('Show all orders and products with saleable quantity inconsistencies') + ->setDescription('Show all orders and products with salable quantity inconsistencies') ->addOption( 'complete-orders', 'c', @@ -91,7 +91,7 @@ protected function configure() * Format output * * @param OutputInterface $output - * @param SaleableQuantityInconsistency[] $inconsistencies + * @param SalableQuantityInconsistency[] $inconsistencies */ private function prettyOutput(OutputInterface $output, array $inconsistencies): void { @@ -109,9 +109,11 @@ private function prettyOutput(OutputInterface $output, array $inconsistencies): foreach ($inconsistentItems as $sku => $qty) { $output->writeln( sprintf( - ' - Product <comment>%s</comment> should be compensated by <comment>%+f</comment>', + ' - Product <comment>%s</comment> should be compensated by ' + . '<comment>%+f</comment> for stock <comment>%s</comment>', $sku, - -$qty + -$qty, + $inconsistency->getStockId() ) ); } @@ -122,7 +124,7 @@ private function prettyOutput(OutputInterface $output, array $inconsistencies): * Output without formatting * * @param OutputInterface $output - * @param SaleableQuantityInconsistency[] $inconsistencies + * @param SalableQuantityInconsistency[] $inconsistencies */ private function rawOutput(OutputInterface $output, array $inconsistencies): void { @@ -132,7 +134,13 @@ private function rawOutput(OutputInterface $output, array $inconsistencies): voi foreach ($inconsistentItems as $sku => $qty) { $output->writeln( - sprintf('%s:%s:%f', $inconsistency->getOrder()->getIncrementId(), $sku, -$qty) + sprintf( + '%s:%s:%f:%s', + $inconsistency->getOrder()->getIncrementId(), + $sku, + -$qty, + $inconsistency->getStockId() + ) ); } } @@ -148,7 +156,7 @@ private function rawOutput(OutputInterface $output, array $inconsistencies): voi */ public function execute(InputInterface $input, OutputInterface $output): int { - $inconsistencies = $this->getSaleableQuantityInconsistencies->execute(); + $inconsistencies = $this->getSalableQuantityInconsistencies->execute(); if ($input->getOption('complete-orders')) { $inconsistencies = $this->filterCompleteOrders->execute($inconsistencies); diff --git a/InventoryReservationCli/Model/GetSaleableQuantityCompensations.php b/InventoryReservationCli/Model/GetSalableQuantityCompensations.php similarity index 95% rename from InventoryReservationCli/Model/GetSaleableQuantityCompensations.php rename to InventoryReservationCli/Model/GetSalableQuantityCompensations.php index 3a827987ee64..9d86045def56 100644 --- a/InventoryReservationCli/Model/GetSaleableQuantityCompensations.php +++ b/InventoryReservationCli/Model/GetSalableQuantityCompensations.php @@ -15,7 +15,7 @@ /** * Returns compensation reservations for given inconsistencies */ -class GetSaleableQuantityCompensations +class GetSalableQuantityCompensations { /** * @var ReservationBuilderInterface @@ -50,7 +50,7 @@ public function __construct( /** * Returns compensation reservations for given inconsistencies * - * @param SaleableQuantityInconsistency[] $inconsistencies + * @param SalableQuantityInconsistency[] $inconsistencies * @return ReservationInterface[] * @throws \Magento\Framework\Validation\ValidationException */ diff --git a/InventoryReservationCli/Model/GetSaleableQuantityInconsistencies.php b/InventoryReservationCli/Model/GetSalableQuantityInconsistencies.php similarity index 74% rename from InventoryReservationCli/Model/GetSaleableQuantityInconsistencies.php rename to InventoryReservationCli/Model/GetSalableQuantityInconsistencies.php index f0769a4fb3cf..e7c1e78b76e6 100644 --- a/InventoryReservationCli/Model/GetSaleableQuantityInconsistencies.php +++ b/InventoryReservationCli/Model/GetSalableQuantityInconsistencies.php @@ -7,20 +7,22 @@ namespace Magento\InventoryReservationCli\Model; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Validation\ValidationException; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\AddCompletedOrdersToForUnresolvedReservations; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\AddExistingReservations; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\AddExpectedReservations; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\Collector; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\CollectorFactory; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterExistingOrders; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterManagedStockProducts; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency\FilterUnresolvedReservations; +use Magento\InventoryConfigurationApi\Exception\SkuIsNotAssignedToStockException; +use Magento\InventoryReservationCli\Model\SalableQuantityInconsistency\AddCompletedOrdersToForUnresolvedReservations; +use Magento\InventoryReservationCli\Model\SalableQuantityInconsistency\AddExistingReservations; +use Magento\InventoryReservationCli\Model\SalableQuantityInconsistency\AddExpectedReservations; +use Magento\InventoryReservationCli\Model\SalableQuantityInconsistency\Collector; +use Magento\InventoryReservationCli\Model\SalableQuantityInconsistency\CollectorFactory; +use Magento\InventoryReservationCli\Model\SalableQuantityInconsistency\FilterExistingOrders; +use Magento\InventoryReservationCli\Model\SalableQuantityInconsistency\FilterManagedStockProducts; +use Magento\InventoryReservationCli\Model\SalableQuantityInconsistency\FilterUnresolvedReservations; /** * Filter orders for missing initial reservation */ -class GetSaleableQuantityInconsistencies +class GetSalableQuantityInconsistencies { /** * @var CollectorFactory @@ -86,8 +88,10 @@ public function __construct( /** * Filter orders for missing initial reservation - * @return SaleableQuantityInconsistency[] + * @return SalableQuantityInconsistency[] * @throws ValidationException + * @throws LocalizedException + * @throws SkuIsNotAssignedToStockException */ public function execute(): array { diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency.php b/InventoryReservationCli/Model/SalableQuantityInconsistency.php similarity index 98% rename from InventoryReservationCli/Model/SaleableQuantityInconsistency.php rename to InventoryReservationCli/Model/SalableQuantityInconsistency.php index 5055d4f3714e..e51c84d37f94 100644 --- a/InventoryReservationCli/Model/SaleableQuantityInconsistency.php +++ b/InventoryReservationCli/Model/SalableQuantityInconsistency.php @@ -12,7 +12,7 @@ /** * Filter orders for missing initial reservation */ -class SaleableQuantityInconsistency +class SalableQuantityInconsistency { /** * @var OrderInterface diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddCompletedOrdersToForUnresolvedReservations.php b/InventoryReservationCli/Model/SalableQuantityInconsistency/AddCompletedOrdersToForUnresolvedReservations.php similarity index 93% rename from InventoryReservationCli/Model/SaleableQuantityInconsistency/AddCompletedOrdersToForUnresolvedReservations.php rename to InventoryReservationCli/Model/SalableQuantityInconsistency/AddCompletedOrdersToForUnresolvedReservations.php index a9c215b67fca..384c60bb4e6a 100644 --- a/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddCompletedOrdersToForUnresolvedReservations.php +++ b/InventoryReservationCli/Model/SalableQuantityInconsistency/AddCompletedOrdersToForUnresolvedReservations.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; +namespace Magento\InventoryReservationCli\Model\SalableQuantityInconsistency; use Magento\InventoryReservationCli\Model\GetOrdersInFinalState; diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddExistingReservations.php b/InventoryReservationCli/Model/SalableQuantityInconsistency/AddExistingReservations.php similarity index 96% rename from InventoryReservationCli/Model/SaleableQuantityInconsistency/AddExistingReservations.php rename to InventoryReservationCli/Model/SalableQuantityInconsistency/AddExistingReservations.php index 00bc5afa8fe6..52413dd21217 100644 --- a/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddExistingReservations.php +++ b/InventoryReservationCli/Model/SalableQuantityInconsistency/AddExistingReservations.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; +namespace Magento\InventoryReservationCli\Model\SalableQuantityInconsistency; use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\Validation\ValidationException; diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddExpectedReservations.php b/InventoryReservationCli/Model/SalableQuantityInconsistency/AddExpectedReservations.php similarity index 97% rename from InventoryReservationCli/Model/SaleableQuantityInconsistency/AddExpectedReservations.php rename to InventoryReservationCli/Model/SalableQuantityInconsistency/AddExpectedReservations.php index a2ff6fb4bffc..b1000af9638b 100644 --- a/InventoryReservationCli/Model/SaleableQuantityInconsistency/AddExpectedReservations.php +++ b/InventoryReservationCli/Model/SalableQuantityInconsistency/AddExpectedReservations.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; +namespace Magento\InventoryReservationCli\Model\SalableQuantityInconsistency; use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\Validation\ValidationException; diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency/Collector.php b/InventoryReservationCli/Model/SalableQuantityInconsistency/Collector.php similarity index 71% rename from InventoryReservationCli/Model/SaleableQuantityInconsistency/Collector.php rename to InventoryReservationCli/Model/SalableQuantityInconsistency/Collector.php index ab5de176d3c4..8544647aacf8 100644 --- a/InventoryReservationCli/Model/SaleableQuantityInconsistency/Collector.php +++ b/InventoryReservationCli/Model/SalableQuantityInconsistency/Collector.php @@ -5,11 +5,11 @@ */ declare(strict_types=1); -namespace Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; +namespace Magento\InventoryReservationCli\Model\SalableQuantityInconsistency; use Magento\Framework\Serialize\SerializerInterface; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistencyFactory; +use Magento\InventoryReservationCli\Model\SalableQuantityInconsistency; +use Magento\InventoryReservationCli\Model\SalableQuantityInconsistencyFactory; use Magento\InventoryReservationsApi\Model\ReservationInterface; use Magento\InventorySalesApi\Model\StockByWebsiteIdResolverInterface; use Magento\Sales\Api\Data\OrderInterface; @@ -20,14 +20,14 @@ class Collector { /** - * @var SaleableQuantityInconsistency[] + * @var SalableQuantityInconsistency[] */ private $items = []; /** - * @var \Magento\InventoryReservationCli\Model\SaleableQuantityInconsistencyFactory + * @var \Magento\InventoryReservationCli\Model\SalableQuantityInconsistencyFactory */ - private $saleableQuantityInconsistencyFactory; + private $salableQuantityInconsistencyFactory; /** * @var SerializerInterface @@ -40,16 +40,16 @@ class Collector private $stockByWebsiteIdResolver; /** - * @param SaleableQuantityInconsistencyFactory $saleableQuantityInconsistencyFactory + * @param SalableQuantityInconsistencyFactory $salableQuantityInconsistencyFactory * @param SerializerInterface $serializer * @param StockByWebsiteIdResolverInterface $stockByWebsiteIdResolver */ public function __construct( - SaleableQuantityInconsistencyFactory $saleableQuantityInconsistencyFactory, + SalableQuantityInconsistencyFactory $salableQuantityInconsistencyFactory, SerializerInterface $serializer, StockByWebsiteIdResolverInterface $stockByWebsiteIdResolver ) { - $this->saleableQuantityInconsistencyFactory = $saleableQuantityInconsistencyFactory; + $this->salableQuantityInconsistencyFactory = $salableQuantityInconsistencyFactory; $this->serializer = $serializer; $this->stockByWebsiteIdResolver = $stockByWebsiteIdResolver; } @@ -65,7 +65,7 @@ public function addReservation(ReservationInterface $reservation): void $key = $objectId . '-' . $stockId; if (!isset($this->items[$key])) { - $this->items[$key] = $this->saleableQuantityInconsistencyFactory->create(); + $this->items[$key] = $this->salableQuantityInconsistencyFactory->create(); } $this->items[$key]->setObjectId((int)$objectId); @@ -84,14 +84,14 @@ public function addOrder(OrderInterface $order): void $key = $objectId . '-' . $stockId; if (!isset($this->items[$key])) { - $this->items[$key] = $this->saleableQuantityInconsistencyFactory->create(); + $this->items[$key] = $this->salableQuantityInconsistencyFactory->create(); } $this->items[$key]->setOrder($order); } /** - * @return SaleableQuantityInconsistency[] + * @return SalableQuantityInconsistency[] */ public function getItems(): array { @@ -99,7 +99,7 @@ public function getItems(): array } /** - * @param SaleableQuantityInconsistency[] $items + * @param SalableQuantityInconsistency[] $items */ public function setItems(array $items): void { diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterCompleteOrders.php b/InventoryReservationCli/Model/SalableQuantityInconsistency/FilterCompleteOrders.php similarity index 75% rename from InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterCompleteOrders.php rename to InventoryReservationCli/Model/SalableQuantityInconsistency/FilterCompleteOrders.php index fc2261244b55..10ee9f2d8d70 100644 --- a/InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterCompleteOrders.php +++ b/InventoryReservationCli/Model/SalableQuantityInconsistency/FilterCompleteOrders.php @@ -5,10 +5,10 @@ */ declare(strict_types=1); -namespace Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; +namespace Magento\InventoryReservationCli\Model\SalableQuantityInconsistency; use Magento\InventoryReservationCli\Model\GetCompleteOrderStatusList; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; +use Magento\InventoryReservationCli\Model\SalableQuantityInconsistency; /** * Remove all reservations with complete state @@ -32,14 +32,14 @@ public function __construct( /** * Remove all reservations with complete state * - * @param SaleableQuantityInconsistency[] $inconsistencies - * @return SaleableQuantityInconsistency[] + * @param SalableQuantityInconsistency[] $inconsistencies + * @return SalableQuantityInconsistency[] */ public function execute(array $inconsistencies): array { return array_filter( $inconsistencies, - function (SaleableQuantityInconsistency $inconsistency) { + function (SalableQuantityInconsistency $inconsistency) { return in_array($inconsistency->getOrder()->getStatus(), $this->getCompleteOrderStatusList->execute()); } ); diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterExistingOrders.php b/InventoryReservationCli/Model/SalableQuantityInconsistency/FilterExistingOrders.php similarity index 60% rename from InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterExistingOrders.php rename to InventoryReservationCli/Model/SalableQuantityInconsistency/FilterExistingOrders.php index 3a39c6fca932..9ee78b34fc32 100644 --- a/InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterExistingOrders.php +++ b/InventoryReservationCli/Model/SalableQuantityInconsistency/FilterExistingOrders.php @@ -5,9 +5,9 @@ */ declare(strict_types=1); -namespace Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; +namespace Magento\InventoryReservationCli\Model\SalableQuantityInconsistency; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; +use Magento\InventoryReservationCli\Model\SalableQuantityInconsistency; /** * Remove all reservations without matching order @@ -17,14 +17,14 @@ class FilterExistingOrders /** * Remove all reservations without matching order * - * @param SaleableQuantityInconsistency[] $inconsistencies - * @return SaleableQuantityInconsistency[] + * @param SalableQuantityInconsistency[] $inconsistencies + * @return SalableQuantityInconsistency[] */ public function execute(array $inconsistencies): array { return array_filter( $inconsistencies, - function (SaleableQuantityInconsistency $inconsistency) { + function (SalableQuantityInconsistency $inconsistency) { return (bool)$inconsistency->getOrder(); } ); diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterIncompleteOrders.php b/InventoryReservationCli/Model/SalableQuantityInconsistency/FilterIncompleteOrders.php similarity index 75% rename from InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterIncompleteOrders.php rename to InventoryReservationCli/Model/SalableQuantityInconsistency/FilterIncompleteOrders.php index 16e4742f8541..bf11ef4df681 100644 --- a/InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterIncompleteOrders.php +++ b/InventoryReservationCli/Model/SalableQuantityInconsistency/FilterIncompleteOrders.php @@ -5,10 +5,10 @@ */ declare(strict_types=1); -namespace Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; +namespace Magento\InventoryReservationCli\Model\SalableQuantityInconsistency; use Magento\InventoryReservationCli\Model\GetCompleteOrderStatusList; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; +use Magento\InventoryReservationCli\Model\SalableQuantityInconsistency; /** * Remove all reservations with incomplete state @@ -32,14 +32,14 @@ public function __construct( /** * Remove all reservations with incomplete state * - * @param SaleableQuantityInconsistency[] $inconsistencies - * @return SaleableQuantityInconsistency[] + * @param SalableQuantityInconsistency[] $inconsistencies + * @return SalableQuantityInconsistency[] */ public function execute(array $inconsistencies): array { return array_filter( $inconsistencies, - function (SaleableQuantityInconsistency $inconsistency) { + function (SalableQuantityInconsistency $inconsistency) { return !in_array($inconsistency->getOrder()->getStatus(), $this->getCompleteOrderStatusList->execute()); } ); diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterManagedStockProducts.php b/InventoryReservationCli/Model/SalableQuantityInconsistency/FilterManagedStockProducts.php similarity index 88% rename from InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterManagedStockProducts.php rename to InventoryReservationCli/Model/SalableQuantityInconsistency/FilterManagedStockProducts.php index ebd239d82106..6a6892f4faf0 100644 --- a/InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterManagedStockProducts.php +++ b/InventoryReservationCli/Model/SalableQuantityInconsistency/FilterManagedStockProducts.php @@ -5,13 +5,13 @@ */ declare(strict_types=1); -namespace Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; +namespace Magento\InventoryReservationCli\Model\SalableQuantityInconsistency; use Magento\Framework\Exception\LocalizedException; use Magento\InventoryApi\Model\IsProductAssignedToStockInterface; use Magento\InventoryConfigurationApi\Api\GetStockItemConfigurationInterface; use Magento\InventoryConfigurationApi\Exception\SkuIsNotAssignedToStockException; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; +use Magento\InventoryReservationCli\Model\SalableQuantityInconsistency; /** * Remove all reservations with incomplete state @@ -43,8 +43,8 @@ public function __construct( /** * Remove all reservations with incomplete state * - * @param SaleableQuantityInconsistency[] $inconsistencies - * @return SaleableQuantityInconsistency[] + * @param SalableQuantityInconsistency[] $inconsistencies + * @return SalableQuantityInconsistency[] * @throws LocalizedException * @throws SkuIsNotAssignedToStockException */ diff --git a/InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterUnresolvedReservations.php b/InventoryReservationCli/Model/SalableQuantityInconsistency/FilterUnresolvedReservations.php similarity index 65% rename from InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterUnresolvedReservations.php rename to InventoryReservationCli/Model/SalableQuantityInconsistency/FilterUnresolvedReservations.php index 3cf32377dfc2..933350b80fbb 100644 --- a/InventoryReservationCli/Model/SaleableQuantityInconsistency/FilterUnresolvedReservations.php +++ b/InventoryReservationCli/Model/SalableQuantityInconsistency/FilterUnresolvedReservations.php @@ -5,9 +5,9 @@ */ declare(strict_types=1); -namespace Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; +namespace Magento\InventoryReservationCli\Model\SalableQuantityInconsistency; -use Magento\InventoryReservationCli\Model\SaleableQuantityInconsistency; +use Magento\InventoryReservationCli\Model\SalableQuantityInconsistency; /** * Remove all compensated reservations @@ -16,8 +16,8 @@ class FilterUnresolvedReservations { /** * Remove all compensated reservations - * @param SaleableQuantityInconsistency[] $inconsistencies - * @return SaleableQuantityInconsistency[] + * @param SalableQuantityInconsistency[] $inconsistencies + * @return SalableQuantityInconsistency[] */ public function execute(array $inconsistencies): array { @@ -27,7 +27,7 @@ public function execute(array $inconsistencies): array return array_filter( $inconsistencies, - function (SaleableQuantityInconsistency $inconsistency) { + function (SalableQuantityInconsistency $inconsistency) { return count($inconsistency->getItems()) > 0; } ); diff --git a/InventoryReservationCli/Test/Integration/Model/GetSaleableQuantityInconsistenciesTest.php b/InventoryReservationCli/Test/Integration/Model/GetSalableQuantityInconsistenciesTest.php similarity index 72% rename from InventoryReservationCli/Test/Integration/Model/GetSaleableQuantityInconsistenciesTest.php rename to InventoryReservationCli/Test/Integration/Model/GetSalableQuantityInconsistenciesTest.php index 8a465130a0d0..92ffefe5df8c 100644 --- a/InventoryReservationCli/Test/Integration/Model/GetSaleableQuantityInconsistenciesTest.php +++ b/InventoryReservationCli/Test/Integration/Model/GetSalableQuantityInconsistenciesTest.php @@ -7,24 +7,24 @@ namespace Magento\InventoryReservationCli\Test\Integration\Model; -use Magento\InventoryReservationCli\Model\GetSaleableQuantityInconsistencies; +use Magento\InventoryReservationCli\Model\GetSalableQuantityInconsistencies; use PHPUnit\Framework\TestCase; use Magento\TestFramework\Helper\Bootstrap; -class GetSaleableQuantityInconsistenciesTest extends TestCase +class GetSalableQuantityInconsistenciesTest extends TestCase { /** - * @var GetSaleableQuantityInconsistencies + * @var GetSalableQuantityInconsistencies */ - private $getSaleableQuantityInconsistencies; + private $getSalableQuantityInconsistencies; /** * Initialize test dependencies */ protected function setUp() { - $this->getSaleableQuantityInconsistencies - = Bootstrap::getObjectManager()->get(GetSaleableQuantityInconsistencies::class); + $this->getSalableQuantityInconsistencies + = Bootstrap::getObjectManager()->get(GetSalableQuantityInconsistencies::class); } /** @@ -33,7 +33,7 @@ protected function setUp() */ public function testIncompleteOrderWithExistingReservation(): void { - $inconsistencies = $this->getSaleableQuantityInconsistencies->execute(); + $inconsistencies = $this->getSalableQuantityInconsistencies->execute(); self::assertSame([], $inconsistencies); } @@ -43,7 +43,7 @@ public function testIncompleteOrderWithExistingReservation(): void */ public function testIncompleteOrderWithoutReservation(): void { - $inconsistencies = $this->getSaleableQuantityInconsistencies->execute(); + $inconsistencies = $this->getSalableQuantityInconsistencies->execute(); self::assertCount(1, $inconsistencies); } @@ -53,7 +53,7 @@ public function testIncompleteOrderWithoutReservation(): void */ public function testCompletedOrderWithReservations(): void { - $inconsistencies = $this->getSaleableQuantityInconsistencies->execute(); + $inconsistencies = $this->getSalableQuantityInconsistencies->execute(); self::assertSame([], $inconsistencies); } @@ -64,7 +64,7 @@ public function testCompletedOrderWithReservations(): void */ public function testCompletedOrderWithMissingReservations(): void { - $inconsistencies = $this->getSaleableQuantityInconsistencies->execute(); + $inconsistencies = $this->getSalableQuantityInconsistencies->execute(); self::assertCount(1, $inconsistencies); } } From a65106c4ad4a9cc2c2bd37705456bbea3f01376f Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Tue, 23 Apr 2019 21:54:32 +0300 Subject: [PATCH 183/231] MSI-2062: remove from duplicated products, replace argumnt from stockId to website code at stockIndexDump --- .../Model/ExportStockIndexData.php | 20 ++-- .../ResourceModel/ManageStockCondition.php | 49 ++++++++++ .../ResourceModel/StockIndexDumpProcessor.php | 98 +++++++++++++------ .../GetWebsiteIdByWebsiteCode.php | 44 +++++++++ 4 files changed, 172 insertions(+), 39 deletions(-) create mode 100644 InventoryExportStock/Model/ResourceModel/ManageStockCondition.php create mode 100644 InventorySales/Model/ResourceModel/GetWebsiteIdByWebsiteCode.php diff --git a/InventoryExportStock/Model/ExportStockIndexData.php b/InventoryExportStock/Model/ExportStockIndexData.php index 3cf3afd77a54..00b8208c0342 100644 --- a/InventoryExportStock/Model/ExportStockIndexData.php +++ b/InventoryExportStock/Model/ExportStockIndexData.php @@ -11,8 +11,7 @@ use Magento\Framework\Exception\LocalizedException; use Magento\InventoryExportStock\Model\ResourceModel\StockIndexDumpProcessor; use Magento\InventoryExportStockApi\Api\ExportStockIndexDataInterface; -use Magento\InventorySalesApi\Api\Data\SalesChannelInterface; -use Magento\InventorySalesApi\Api\StockResolverInterface; +use Magento\InventorySales\Model\ResourceModel\GetWebsiteIdByWebsiteCode; use Psr\Log\LoggerInterface; /** @@ -31,25 +30,25 @@ class ExportStockIndexData implements ExportStockIndexDataInterface private $logger; /** - * @var StockResolverInterface + * @var GetWebsiteIdByWebsiteCode */ - private $stockResolver; + private $getWebsiteIdByWebsiteCode; /** * ExportStockIndexData constructor * * @param StockIndexDumpProcessor $stockIndexDumpProcessor - * @param StockResolverInterface $stockResolver * @param LoggerInterface $logger + * @param GetWebsiteIdByWebsiteCode $getWebsiteIdByWebsiteCode */ public function __construct( StockIndexDumpProcessor $stockIndexDumpProcessor, - StockResolverInterface $stockResolver, - LoggerInterface $logger + LoggerInterface $logger, + GetWebsiteIdByWebsiteCode $getWebsiteIdByWebsiteCode ) { $this->stockIndexDumpProcessor = $stockIndexDumpProcessor; - $this->stockResolver = $stockResolver; $this->logger = $logger; + $this->getWebsiteIdByWebsiteCode = $getWebsiteIdByWebsiteCode; } /** @@ -63,9 +62,8 @@ public function execute( string $websiteCode ): array { try { - $stockId = $this->stockResolver - ->execute(SalesChannelInterface::TYPE_WEBSITE, $websiteCode)->getStockId(); - $items = $this->stockIndexDumpProcessor->execute($stockId); + $websiteId = $this->getWebsiteIdByWebsiteCode->execute($websiteCode); + $items = $this->stockIndexDumpProcessor->execute($websiteId); } catch (Exception $e) { $this->logger->critical($e->getMessage(), $e->getTrace()); throw new LocalizedException(__('Something went wrong. Export couldn\'t be executed, See log files for error details')); diff --git a/InventoryExportStock/Model/ResourceModel/ManageStockCondition.php b/InventoryExportStock/Model/ResourceModel/ManageStockCondition.php new file mode 100644 index 000000000000..03d65c6bba17 --- /dev/null +++ b/InventoryExportStock/Model/ResourceModel/ManageStockCondition.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryExportStock\Model\ResourceModel; + +use Magento\CatalogInventory\Api\StockConfigurationInterface; +use Magento\Framework\DB\Select; + +/** + * Class ManageStockCondition + */ +class ManageStockCondition +{ + /** + * @var StockConfigurationInterface + */ + private $configuration; + + /** + * @param StockConfigurationInterface $configuration + */ + public function __construct(StockConfigurationInterface $configuration) + { + $this->configuration = $configuration; + } + + /** + * Provide product manage stock condition for db select + * + * @param Select $select + * @return string + */ + public function execute(Select $select): string + { + $globalManageStock = (int)$this->configuration->getManageStock(); + + $condition = ' + (legacy_stock_item.use_config_manage_stock = 0 AND legacy_stock_item.manage_stock = 1)'; + if (1 === $globalManageStock) { + $condition .= ' OR legacy_stock_item.use_config_manage_stock = 1'; + } + + return $condition; + } +} diff --git a/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php b/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php index 90edf4f6437b..2aac277ff2c1 100644 --- a/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php +++ b/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php @@ -14,7 +14,7 @@ use Magento\Framework\Exception\LocalizedException; use Magento\InventoryExportStock\Model\GetQtyForNotManageStock; use Magento\InventoryIndexer\Model\StockIndexTableNameResolverInterface; -use Magento\InventorySales\Model\ResourceModel\IsStockItemSalableCondition\ManageStockCondition; +use Magento\InventorySales\Model\ResourceModel\IsStockItemSalableCondition\ManageStockCondition as NotManageStockCondition; use Psr\Log\LoggerInterface; use Zend_Db_Expr; @@ -34,9 +34,9 @@ class StockIndexDumpProcessor private $resourceConnection; /** - * @var ManageStockCondition + * @var NotManageStockCondition */ - private $manageStockCondition; + private $notManageStockCondition; /** * @var AdapterInterface @@ -47,16 +47,23 @@ class StockIndexDumpProcessor * @var GetQtyForNotManageStock */ private $getQtyForNotManageStock; + /** * @var LoggerInterface */ private $logger; + /** + * @var ManageStockCondition + */ + private $manageStockCondition; + /** * GetStockIndexDump constructor * * @param StockIndexTableNameResolverInterface $stockIndexTableNameResolver * @param ResourceConnection $resourceConnection + * @param NotManageStockCondition $notManageStockCondition * @param ManageStockCondition $manageStockCondition * @param GetQtyForNotManageStock $getQtyForNotManageStock * @param LoggerInterface $logger @@ -64,12 +71,14 @@ class StockIndexDumpProcessor public function __construct( StockIndexTableNameResolverInterface $stockIndexTableNameResolver, ResourceConnection $resourceConnection, + NotManageStockCondition $notManageStockCondition, ManageStockCondition $manageStockCondition, GetQtyForNotManageStock $getQtyForNotManageStock, LoggerInterface $logger ) { $this->stockIndexTableNameResolver = $stockIndexTableNameResolver; $this->resourceConnection = $resourceConnection; + $this->notManageStockCondition = $notManageStockCondition; $this->manageStockCondition = $manageStockCondition; $this->getQtyForNotManageStock = $getQtyForNotManageStock; $this->logger = $logger; @@ -78,18 +87,18 @@ public function __construct( /** * Provides sku and qty of products dumping them from stock index table * - * @param int $stockId + * @param int $websiteId * @return array * @throws LocalizedException */ - public function execute(int $stockId): array + public function execute(int $websiteId): array { $this->connection = $this->resourceConnection->getConnection(); $select = $this->connection->select(); try { $select->union([ - $this->getStockItemSelect($stockId), - $this->getStockIndexSelect($stockId) + $this->getStockItemSelect($websiteId), + $this->getStockIndexSelect($websiteId) ]); } catch (Exception $e) { $this->logger->critical($e->getMessage(), $e->getTrace()); @@ -102,52 +111,85 @@ public function execute(int $stockId): array /** * Provides stock select * - * @param int $stockId + * @param int $websiteId * @return Select */ - private function getStockIndexSelect(int $stockId): Select + private function getStockIndexSelect(int $websiteId): Select { $stockIndexTableName = $this->resourceConnection - ->getTableName($this->stockIndexTableNameResolver->execute($stockId)); - - return $this->connection->select() - ->from( - $stockIndexTableName, - [ - 'qty' => 'quantity', - 'is_salable' => 'is_salable', - 'sku' => 'sku' - ] - ); + ->getTableName($this->stockIndexTableNameResolver->execute($websiteId)); + + $legacyStockItemTable = $this->resourceConnection + ->getTableName('cataloginventory_stock_item'); + $productEntityTable = $this->resourceConnection + ->getTableName('catalog_product_entity'); + $productWebsiteTable = $this->resourceConnection + ->getTableName('catalog_product_website'); + + $select = $this->connection->select(); + $select->from( + ['stock_index' => $stockIndexTableName], + [ + 'qty' => 'quantity', + 'is_salable' => 'is_salable', + 'sku' => 'sku' + ] + )->join( + ['product_entity' => $productEntityTable], + 'product_entity.sku=stock_index.sku', + '' + )->join( + ['legacy_stock_item' => $legacyStockItemTable], + 'legacy_stock_item.product_id = product_entity.entity_id', + '' + )->join( + ['prod_website' => $productWebsiteTable], + 'legacy_stock_item.product_id = prod_website.product_id', + '' + )->where( + $this->manageStockCondition->execute($select) + )->where( + 'prod_website.website_id = ?', + $websiteId + ); + + return $select; } /** * Provides stock item select * - * @param int $stockId + * @param int $websiteId * @return Select */ - private function getStockItemSelect(int $stockId): Select + private function getStockItemSelect(int $websiteId): Select { $legacyStockItemTable = $this->resourceConnection ->getTableName('cataloginventory_stock_item'); $productEntityTable = $this->resourceConnection ->getTableName('catalog_product_entity'); $select = $this->connection->select(); - + $getQtyForNotManageStock = $this->getQtyForNotManageStock->execute(); + if ($getQtyForNotManageStock === null) { + $getQtyForNotManageStock = 'NULL'; + } $select->from( ['legacy_stock_item' => $legacyStockItemTable], - [new Zend_Db_Expr($this->getQtyForNotManageStock->execute() . ' as qty'), - new Zend_Db_Expr('1 as is_salable')] + [new Zend_Db_Expr($getQtyForNotManageStock . ' as qty'), + new Zend_Db_Expr('"1" as is_salable')] )->join( ['product_entity' => $productEntityTable], 'legacy_stock_item.product_id = product_entity.entity_id', ['sku'] + )->join( + ['pr_web' => 'catalog_product_website'], + 'legacy_stock_item.product_id = pr_web.product_id', + '' )->where( - $this->manageStockCondition->execute($select) + $this->notManageStockCondition->execute($select) )->where( - 'legacy_stock_item.stock_id = ?', - $stockId + 'pr_web.website_id = ?', + $websiteId ); return $select; diff --git a/InventorySales/Model/ResourceModel/GetWebsiteIdByWebsiteCode.php b/InventorySales/Model/ResourceModel/GetWebsiteIdByWebsiteCode.php new file mode 100644 index 000000000000..b1dcd876e25d --- /dev/null +++ b/InventorySales/Model/ResourceModel/GetWebsiteIdByWebsiteCode.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventorySales\Model\ResourceModel; + +use Magento\Framework\App\ResourceConnection; + +/** + * Get website id by website code + */ +class GetWebsiteIdByWebsiteCode +{ + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @param ResourceConnection $resourceConnection + */ + public function __construct( + ResourceConnection $resourceConnection + ) { + $this->resourceConnection = $resourceConnection; + } + + /** + * @param string $websiteCode + * @return int|null + */ + public function execute(string $websiteCode): ?int + { + $connection = $this->resourceConnection->getConnection(); + $tableName = $this->resourceConnection->getTableName('store_website'); + $selectQry = $connection->select()->from($tableName, 'website_id')->where('code = ?', $websiteCode); + + $result = $connection->fetchOne($selectQry); + return (false === $result) ? null : (int)$result; + } +} From 36bff148c77f90c8f7a7d99467ab473957b8c3f7 Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Tue, 23 Apr 2019 22:46:43 +0300 Subject: [PATCH 184/231] MSI-2062: refactoring and code styling changes --- .../Model/ExportStockIndexData.php | 27 ++++--------------- .../Model/ExportStockSalableQty.php | 5 ++-- .../ResourceModel/StockIndexDumpProcessor.php | 3 +-- InventoryExportStock/README.md | 2 +- InventoryExportStock/etc/module.xml | 3 +++ .../Api/ExportStockIndexDataInterface.php | 4 +-- InventoryExportStockApi/README.md | 2 +- 7 files changed, 15 insertions(+), 31 deletions(-) diff --git a/InventoryExportStock/Model/ExportStockIndexData.php b/InventoryExportStock/Model/ExportStockIndexData.php index 00b8208c0342..cb83a33c7a18 100644 --- a/InventoryExportStock/Model/ExportStockIndexData.php +++ b/InventoryExportStock/Model/ExportStockIndexData.php @@ -7,15 +7,13 @@ namespace Magento\InventoryExportStock\Model; -use Exception; use Magento\Framework\Exception\LocalizedException; use Magento\InventoryExportStock\Model\ResourceModel\StockIndexDumpProcessor; use Magento\InventoryExportStockApi\Api\ExportStockIndexDataInterface; use Magento\InventorySales\Model\ResourceModel\GetWebsiteIdByWebsiteCode; -use Psr\Log\LoggerInterface; /** - * Class ExportStockIndexData + * Class ExportStockIndexData provides stock index export */ class ExportStockIndexData implements ExportStockIndexDataInterface { @@ -24,11 +22,6 @@ class ExportStockIndexData implements ExportStockIndexDataInterface */ private $stockIndexDumpProcessor; - /** - * @var LoggerInterface - */ - private $logger; - /** * @var GetWebsiteIdByWebsiteCode */ @@ -38,16 +31,13 @@ class ExportStockIndexData implements ExportStockIndexDataInterface * ExportStockIndexData constructor * * @param StockIndexDumpProcessor $stockIndexDumpProcessor - * @param LoggerInterface $logger * @param GetWebsiteIdByWebsiteCode $getWebsiteIdByWebsiteCode */ public function __construct( StockIndexDumpProcessor $stockIndexDumpProcessor, - LoggerInterface $logger, GetWebsiteIdByWebsiteCode $getWebsiteIdByWebsiteCode ) { $this->stockIndexDumpProcessor = $stockIndexDumpProcessor; - $this->logger = $logger; $this->getWebsiteIdByWebsiteCode = $getWebsiteIdByWebsiteCode; } @@ -58,17 +48,10 @@ public function __construct( * @return array * @throws LocalizedException */ - public function execute( - string $websiteCode - ): array { - try { - $websiteId = $this->getWebsiteIdByWebsiteCode->execute($websiteCode); - $items = $this->stockIndexDumpProcessor->execute($websiteId); - } catch (Exception $e) { - $this->logger->critical($e->getMessage(), $e->getTrace()); - throw new LocalizedException(__('Something went wrong. Export couldn\'t be executed, See log files for error details')); - } + public function execute(string $websiteCode): array + { + $websiteId = $this->getWebsiteIdByWebsiteCode->execute($websiteCode); - return $items; + return $this->stockIndexDumpProcessor->execute($websiteId); } } diff --git a/InventoryExportStock/Model/ExportStockSalableQty.php b/InventoryExportStock/Model/ExportStockSalableQty.php index 844af781dc5c..17fa6ad4d31f 100644 --- a/InventoryExportStock/Model/ExportStockSalableQty.php +++ b/InventoryExportStock/Model/ExportStockSalableQty.php @@ -7,10 +7,10 @@ namespace Magento\InventoryExportStock\Model; -use Exception; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Framework\Api\SearchCriteriaInterface; use Magento\Framework\Api\SearchResultsInterface; +use Magento\Framework\Exception\LocalizedException; use Magento\InventoryExportStockApi\Api\Data\ExportStockSalableQtySearchResultInterface; use Magento\InventoryExportStockApi\Api\Data\ExportStockSalableQtySearchResultInterfaceFactory; use Magento\InventoryExportStockApi\Api\ExportStockSalableQtyInterface; @@ -36,6 +36,7 @@ class ExportStockSalableQty implements ExportStockSalableQtyInterface * @var PreciseExportStockProcessor */ private $preciseExportStockProcessor; + /** * @var StockResolverInterface */ @@ -64,7 +65,7 @@ public function __construct( /** * @inheritDoc * - * @throws Exception + * @throws LocalizedException */ public function execute( SearchCriteriaInterface $searchCriteria, diff --git a/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php b/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php index 2aac277ff2c1..f94a824bede2 100644 --- a/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php +++ b/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php @@ -7,7 +7,6 @@ namespace Magento\InventoryExportStock\Model\ResourceModel; -use Exception; use Magento\Framework\App\ResourceConnection; use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\DB\Select; @@ -100,7 +99,7 @@ public function execute(int $websiteId): array $this->getStockItemSelect($websiteId), $this->getStockIndexSelect($websiteId) ]); - } catch (Exception $e) { + } catch (\Exception $e) { $this->logger->critical($e->getMessage(), $e->getTrace()); throw new LocalizedException(__('Something went wrong. Export couldn\'t be executed, See log files for error details')); } diff --git a/InventoryExportStock/README.md b/InventoryExportStock/README.md index c2974cb89cde..57949a0c5da7 100644 --- a/InventoryExportStock/README.md +++ b/InventoryExportStock/README.md @@ -1,6 +1,6 @@ # InventoryExportStock module -The `InventoryExportStock` module provides stock export functionality. +The `InventoryExportStock` module provides aggregated stock export functionality. This module is part of the new inventory infrastructure. The [Inventory Management overview](https://devdocs.magento.com/guides/v2.3/inventory/index.html) diff --git a/InventoryExportStock/etc/module.xml b/InventoryExportStock/etc/module.xml index ab4e81bdcef1..5c45f9e05173 100644 --- a/InventoryExportStock/etc/module.xml +++ b/InventoryExportStock/etc/module.xml @@ -11,9 +11,12 @@ <sequence> <module name="Magento_Catalog"/> <module name="Magento_InventoryApi"/> + <module name="Magento_InventoryIndexer"/> <module name="Magento_InventoryCatalogApi"/> + <module name="Magento_InventoryConfigurationApi"/> <module name="Magento_InventoryExportStockApi"/> <module name="Magento_InventorySalesApi"/> + <module name="Magento_InventorySales"/> </sequence> </module> </config> diff --git a/InventoryExportStockApi/Api/ExportStockIndexDataInterface.php b/InventoryExportStockApi/Api/ExportStockIndexDataInterface.php index 72fe8918f146..27e3653ead21 100644 --- a/InventoryExportStockApi/Api/ExportStockIndexDataInterface.php +++ b/InventoryExportStockApi/Api/ExportStockIndexDataInterface.php @@ -19,7 +19,5 @@ interface ExportStockIndexDataInterface * @param string $websiteCode * @return array */ - public function execute( - string $websiteCode - ): array; + public function execute(string $websiteCode): array; } diff --git a/InventoryExportStockApi/README.md b/InventoryExportStockApi/README.md index 42c97793168b..acc559b68bca 100644 --- a/InventoryExportStockApi/README.md +++ b/InventoryExportStockApi/README.md @@ -1,6 +1,6 @@ # InventoryExportStockApi module -The `InventoryExportStockApi` module provides stock export functionality api. +The `InventoryExportStockApi` module provides provides aggregated stock export functionality api. This module is part of the new inventory infrastructure. The [Inventory Management overview](https://devdocs.magento.com/guides/v2.3/inventory/index.html) From 9cfaf561e2b55d17cdff7c6e6c66eb70a995803b Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Tue, 23 Apr 2019 15:13:54 -0500 Subject: [PATCH 185/231] MSI-1527 Attempting test fix by ordering Prodcut creation AFTER assign deafult stock to main site --- ...esholdForSimpleProductOnDefaultSourceTest.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminUserApplyOnlyXLeftThresholdForSimpleProductOnDefaultSourceTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminUserApplyOnlyXLeftThresholdForSimpleProductOnDefaultSourceTest.xml index 9db15c688544..6c692a28d41d 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminUserApplyOnlyXLeftThresholdForSimpleProductOnDefaultSourceTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminUserApplyOnlyXLeftThresholdForSimpleProductOnDefaultSourceTest.xml @@ -28,20 +28,13 @@ <magentoCLI command="cache:flush" stepKey="cleanCacheAfterFillThresholdConfig"/> <createData entity="_minimalSource" stepKey="createSource"/> - <createData entity="BasicMsiStockWithMainWebsite1" stepKey="createStock"/> + <createData entity="BasicMsiStock1" stepKey="createStock"/> <createData entity="SourceStockLinked1" stepKey="stockSourceLink"> <requiredEntity createDataKey="createStock"/> <requiredEntity createDataKey="createSource"/> </createData> - <createData entity="SimpleSubCategory" stepKey="createCategory"/> - <createData entity="SimpleProduct" stepKey="createSimpleProduct"> - <field key="price">10.00</field> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="Msi_US_Customer" stepKey="createCustomer"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> <waitForPageLoad stepKey="waitForDashboardLoad"/> @@ -55,6 +48,13 @@ <waitForPageLoad time="30" stepKey="waitForDefaultStockPageLoaded"/> <selectOption selector="{{AdminEditStockSalesChannelsSection.websites}}" userInput="Main Website" stepKey="selectDefaultWebsiteAsSalesChannelForDefaultStock"/> <click selector="{{AdminGridMainControls.saveAndContinue}}" stepKey="saveDefaultStock"/> + + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createSimpleProduct"> + <field key="price">10.00</field> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="Msi_US_Customer" stepKey="createCustomer"/> </before> <after> <comment userInput="Disable created source." stepKey="disableCreatedSourceComment"/> From 265698ca3ae3c975949239653fedf09ac63bb0ec Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Tue, 23 Apr 2019 23:58:14 +0300 Subject: [PATCH 186/231] MSI-2062: support rendering unassigned to stock products --- .../Model/PreciseExportStockProcessor.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/InventoryExportStock/Model/PreciseExportStockProcessor.php b/InventoryExportStock/Model/PreciseExportStockProcessor.php index 691107b7acd6..dfe881dd068b 100644 --- a/InventoryExportStock/Model/PreciseExportStockProcessor.php +++ b/InventoryExportStock/Model/PreciseExportStockProcessor.php @@ -92,12 +92,13 @@ public function execute(array $products, int $stockId): array $qty = $this->getProductSalableQtyByStock($sku, $stockId); $isSalable = $this->isProductSalable->execute($sku, $stockId); } catch (SkuIsNotAssignedToStockException $e) { - continue; + $qty = 0.0000; + $isSalable = false; } $items[] = [ 'sku' => $sku, - 'qty' => $qty, + 'qty' => (float)$qty, 'is_salable' => $isSalable ]; } @@ -134,12 +135,12 @@ private function getProductSkus(array $products): array */ private function getProductSalableQtyByStock(string $sku, int $stockId): ?float { + if (!$this->getStockItemConfiguration->execute($sku, $stockId)->isManageStock()) { + return $this->getQtyForNotManageStock->execute(); + } if (!$this->isProductAssignedToStock->execute($sku, $stockId)) { throw new SkuIsNotAssignedToStockException(__('The requested sku is not assigned to given stock.')); } - if (!$this->getStockItemConfiguration->execute($sku, $stockId)->isManageStock()) { - return (float)$this->getQtyForNotManageStock->execute(); - } if (!$this->isSourceItemManagementAllowedForSku->execute($sku)) { return null; } From d2d992f3cf7a652405bcb34bf9032fa867cee16d Mon Sep 17 00:00:00 2001 From: Vadim Justus <v.justus@techdivision.com> Date: Tue, 23 Apr 2019 23:59:59 +0200 Subject: [PATCH 187/231] magento-engcom/msi#2170: Implement pipeling ability for create-compensation CLI --- .../Command/CreateCompensations.php | 163 +++++++----------- .../Input/GetCommandlineStandardInput.php | 31 ++++ ...GetReservationFromCompensationArgument.php | 89 ++++++++++ .../Command/ShowInconsistencies.php | 2 - .../Model/GetOrdersInFinalState.php | 16 +- .../Model/GetOrdersInNotFinalState.php | 16 +- .../Model/GetSalableQuantityCompensations.php | 77 --------- 7 files changed, 200 insertions(+), 194 deletions(-) create mode 100644 InventoryReservationCli/Command/Input/GetCommandlineStandardInput.php create mode 100644 InventoryReservationCli/Command/Input/GetReservationFromCompensationArgument.php delete mode 100644 InventoryReservationCli/Model/GetSalableQuantityCompensations.php diff --git a/InventoryReservationCli/Command/CreateCompensations.php b/InventoryReservationCli/Command/CreateCompensations.php index 45a253675070..cc4428e3c833 100644 --- a/InventoryReservationCli/Command/CreateCompensations.php +++ b/InventoryReservationCli/Command/CreateCompensations.php @@ -10,14 +10,11 @@ use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\InputException; use Magento\Framework\Validation\ValidationException; -use Magento\InventoryReservationCli\Model\GetSalableQuantityCompensations\Proxy as GetSalableQuantityCompensations; -use Magento\InventoryReservationCli\Model\GetSalableQuantityInconsistencies\Proxy as GetSalableQuantityInconsistencies; -use Magento\InventoryReservationCli\Model\SalableQuantityInconsistency; -use Magento\InventoryReservationCli\Model\SalableQuantityInconsistency\FilterCompleteOrders\Proxy as FilterCompleteOrders; -use Magento\InventoryReservationCli\Model\SalableQuantityInconsistency\FilterIncompleteOrders\Proxy as FilterIncompleteOrders; -use Magento\InventoryReservationsApi\Model\AppendReservationsInterface\Proxy as AppendReservationsInterface; -use Magento\InventoryReservationsApi\Model\ReservationInterface; +use Magento\InventoryReservationCli\Command\Input\GetCommandlineStandardInput; +use Magento\InventoryReservationCli\Command\Input\GetReservationFromCompensationArgument; +use Magento\InventoryReservationsApi\Model\AppendReservationsInterface; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -32,24 +29,14 @@ class CreateCompensations extends Command { /** - * @var GetSalableQuantityInconsistencies + * @var GetCommandlineStandardInput */ - private $getSalableQuantityInconsistencies; + private $getCommandlineStandardInput; /** - * @var FilterCompleteOrders + * @var GetReservationFromCompensationArgument */ - private $filterCompleteOrders; - - /** - * @var FilterIncompleteOrders - */ - private $filterIncompleteOrders; - - /** - * @var GetSalableQuantityCompensations - */ - private $getSalableQuantityCompensations; + private $getReservationFromCompensationArgument; /** * @var AppendReservationsInterface @@ -57,24 +44,18 @@ class CreateCompensations extends Command private $appendReservations; /** - * @param GetSalableQuantityInconsistencies $getSalableQuantityInconsistencies - * @param GetSalableQuantityCompensations $getSalableQuantityCompensations + * @param GetCommandlineStandardInput $getCommandlineStandardInput + * @param GetReservationFromCompensationArgument $getReservationFromCompensationArgument * @param AppendReservationsInterface $appendReservations - * @param FilterCompleteOrders $filterCompleteOrders - * @param FilterIncompleteOrders $filterIncompleteOrders */ public function __construct( - GetSalableQuantityInconsistencies $getSalableQuantityInconsistencies, - GetSalableQuantityCompensations $getSalableQuantityCompensations, - AppendReservationsInterface $appendReservations, - FilterCompleteOrders $filterCompleteOrders, - FilterIncompleteOrders $filterIncompleteOrders + GetCommandlineStandardInput $getCommandlineStandardInput, + GetReservationFromCompensationArgument $getReservationFromCompensationArgument, + AppendReservationsInterface $appendReservations ) { parent::__construct(); - $this->getSalableQuantityInconsistencies = $getSalableQuantityInconsistencies; - $this->filterCompleteOrders = $filterCompleteOrders; - $this->filterIncompleteOrders = $filterIncompleteOrders; - $this->getSalableQuantityCompensations = $getSalableQuantityCompensations; + $this->getCommandlineStandardInput = $getCommandlineStandardInput; + $this->getReservationFromCompensationArgument = $getReservationFromCompensationArgument; $this->appendReservations = $appendReservations; } @@ -85,11 +66,11 @@ protected function configure() { $this ->setName('inventory:reservation:create-compensations') - ->setDescription('Create compensation reservations for detected inconsistencies') + ->setDescription('Create reservations by provided compensation arguments') ->addArgument( 'compensations', InputArgument::IS_ARRAY, - 'List of compensation arguments in format ' + 'List of compensation arguments in format "<ORDER_INCREMENT_ID>:<SKU>:<QUANTITY>:<STOCK-ID>"' ) ->addOption( 'raw', @@ -102,63 +83,23 @@ protected function configure() } /** - * Format output - * - * @param OutputInterface $output - * @param ReservationInterface[] $compensations + * @param InputInterface $input + * @return array + * @throws InvalidArgumentException */ - private function prettyOutput(OutputInterface $output, array $compensations): void + private function getCompensationsArguments(InputInterface $input): array { - $output->writeln('<info>Following reservations were created:</info>'); - - foreach ($compensations as $reservation) { - $output->writeln( - sprintf( - 'Product <comment>%s</comment> compensated by <comment>%+f</comment> for stock id <comment>%s</comment>', - $reservation->getSku(), - $reservation->getQuantity(), - $reservation->getStockId() - ) - ); - } - } + $compensationArguments = $input->getArgument('compensations'); - /** - * Output without formatting - * - * @param OutputInterface $output - * @param ReservationInterface[] $compensations - */ - private function rawOutput(OutputInterface $output, array $compensations): void - { - foreach ($compensations as $reservation) { - $output->writeln( - sprintf( - '%s:%f:%s', - $reservation->getSku(), - $reservation->getQuantity(), - $reservation->getStockId() - ) - ); + if (empty($compensationArguments)) { + $compensationArguments = $this->getCommandlineStandardInput->execute(); } - } - - /** - * @param InputInterface $input - * @return SalableQuantityInconsistency[] - * @throws ValidationException - */ - private function getFilteredInconsistencies(InputInterface $input): array - { - $inconsistencies = $this->getSalableQuantityInconsistencies->execute(); - if ($input->getOption('complete-orders')) { - $inconsistencies = $this->filterCompleteOrders->execute($inconsistencies); - } elseif ($input->getOption('incomplete-orders')) { - $inconsistencies = $this->filterIncompleteOrders->execute($inconsistencies); + if (empty($compensationArguments)) { + throw new InvalidArgumentException('A list of compensations needs to be defined as argument or STDIN.'); } - return $inconsistencies; + return $compensationArguments; } /** @@ -169,28 +110,44 @@ private function getFilteredInconsistencies(InputInterface $input): array * @return int * @throws ValidationException * @throws InputException - * @throws CouldNotSaveException */ public function execute(InputInterface $input, OutputInterface $output): int { - $inconsistencies = $this->getFilteredInconsistencies($input); - $compensations = $this->getSalableQuantityCompensations->execute($inconsistencies); - - if (empty($compensations)) { - $output->writeln('<info>No required compensations calculated.</info>'); - return 0; - } - - if (!$input->getOption('dry-run')) { - $this->appendReservations->execute($compensations); - } + $output->writeln('<info>Following reservations were created:</info>'); - if ($input->getOption('raw')) { - $this->rawOutput($output, $compensations); - } else { - $this->prettyOutput($output, $compensations); + $hasErrors = false; + foreach ($this->getCompensationsArguments($input) as $compensationsArgument) { + try { + $compensation = $this->getReservationFromCompensationArgument->execute($compensationsArgument); + $this->appendReservations->execute([$compensation]); + $output->writeln( + sprintf( + ' - Product <comment>%s</comment> was compensated by ' + . '<comment>%+f</comment> for stock <comment>%s</comment>', + $compensation->getSku(), + -$compensation->getQuantity(), + $compensation->getStockId() + ) + ); + } catch (CouldNotSaveException $exception) { + $hasErrors = true; + $output->writeln(sprintf(' - <error>%s</error>', $exception->getMessage())); + } catch (InvalidArgumentException $exception) { + $hasErrors = true; + $output->writeln(sprintf( + ' - <error>Error while parsing argument "%s". %s</error>', + $compensationsArgument, + $exception->getMessage() + )); + } catch (\Exception $exception) { + $output->writeln(sprintf( + ' - <error>Argument "%s" caused exception "%s"</error>', + $compensationsArgument, + $exception->getMessage() + )); + } } - return 0; + return $hasErrors ? 1 : 0; } } diff --git a/InventoryReservationCli/Command/Input/GetCommandlineStandardInput.php b/InventoryReservationCli/Command/Input/GetCommandlineStandardInput.php new file mode 100644 index 000000000000..9adfa0dd6661 --- /dev/null +++ b/InventoryReservationCli/Command/Input/GetCommandlineStandardInput.php @@ -0,0 +1,31 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservationCli\Command\Input; + +/** + * Fetches standard input for cli commands and retrieves as array + */ +class GetCommandlineStandardInput +{ + /** + * @return array + */ + public function execute(): array + { + $values = []; + $handle = fopen('php://stdin', 'r'); + if ($handle) { + while ($line = fgets($handle)) { + $values[] = trim($line); + } + fclose($handle); + } + + return array_filter($values); + } +} diff --git a/InventoryReservationCli/Command/Input/GetReservationFromCompensationArgument.php b/InventoryReservationCli/Command/Input/GetReservationFromCompensationArgument.php new file mode 100644 index 000000000000..c52c4fc318c5 --- /dev/null +++ b/InventoryReservationCli/Command/Input/GetReservationFromCompensationArgument.php @@ -0,0 +1,89 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryReservationCli\Command\Input; + +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\Validation\ValidationException; +use Magento\InventoryReservationsApi\Model\ReservationBuilderInterface; +use Magento\InventoryReservationsApi\Model\ReservationInterface; +use Magento\Sales\Api\OrderRepositoryInterface; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Builds reservation model from given compensation input argument + */ +class GetReservationFromCompensationArgument +{ + /** + * @var OrderRepositoryInterface + */ + private $orderRepository; + + /** + * @var ReservationBuilderInterface + */ + private $reservationBuilder; + + /** + * @var SerializerInterface + */ + private $serializer; + + /** + * @param OrderRepositoryInterface $orderRepository + * @param ReservationBuilderInterface $reservationBuilder + * @param SerializerInterface $serializer + */ + public function __construct( + OrderRepositoryInterface $orderRepository, + ReservationBuilderInterface $reservationBuilder, + SerializerInterface $serializer + ) { + $this->orderRepository = $orderRepository; + $this->reservationBuilder = $reservationBuilder; + $this->serializer = $serializer; + } + + /** + * @param string $argument + * @return array + * @throws InvalidArgumentException + */ + private function parseArgument(string $argument): array + { + $pattern = '/(?P<increment_id>.*):(?P<sku>.*):(?P<quantity>.*):(?P<stock_id>.*)/'; + if (preg_match($pattern, $argument, $match)) { + return $match; + } + + throw new InvalidArgumentException(sprintf('Given argument does not match pattern "%s"', $pattern)); + } + + /** + * @param string $argument + * @return ReservationInterface + * @throws InvalidArgumentException + * @throws ValidationException + */ + public function execute(string $argument): ReservationInterface + { + $argumentParts = $this->parseArgument($argument); + $order = $this->orderRepository->get($argumentParts['increment_id']); + + return $this->reservationBuilder + ->setSku((string)$argumentParts['sku']) + ->setQuantity((float)$argumentParts['quantity']) + ->setStockId((int)$argumentParts['stock_id']) + ->setMetadata($this->serializer->serialize([ + 'event_type' => 'manual_compensation', + 'object_type' => 'order', + 'object_id' => $order->getEntityId(), + ])) + ->build(); + } +} diff --git a/InventoryReservationCli/Command/ShowInconsistencies.php b/InventoryReservationCli/Command/ShowInconsistencies.php index 19a709d5bc90..4379269d9e73 100644 --- a/InventoryReservationCli/Command/ShowInconsistencies.php +++ b/InventoryReservationCli/Command/ShowInconsistencies.php @@ -7,7 +7,6 @@ namespace Magento\InventoryReservationCli\Command; -use Magento\Framework\Validation\ValidationException; use Magento\InventoryReservationCli\Model\GetSalableQuantityInconsistencies\Proxy as GetSalableQuantityInconsistencies; use Magento\InventoryReservationCli\Model\SalableQuantityInconsistency; use Magento\InventoryReservationCli\Model\SalableQuantityInconsistency\FilterCompleteOrders\Proxy as FilterCompleteOrders; @@ -152,7 +151,6 @@ private function rawOutput(OutputInterface $output, array $inconsistencies): voi * @param InputInterface $input * @param OutputInterface $output * @return int - * @throws ValidationException */ public function execute(InputInterface $input, OutputInterface $output): int { diff --git a/InventoryReservationCli/Model/GetOrdersInFinalState.php b/InventoryReservationCli/Model/GetOrdersInFinalState.php index 33aa09c26780..c807737390ea 100644 --- a/InventoryReservationCli/Model/GetOrdersInFinalState.php +++ b/InventoryReservationCli/Model/GetOrdersInFinalState.php @@ -28,16 +28,24 @@ class GetOrdersInFinalState */ private $searchCriteriaBuilder; + /** + * @var GetCompleteOrderStatusList + */ + private $getCompleteOrderStatusList; + /** * @param OrderRepositoryInterface $orderRepository * @param SearchCriteriaBuilder $searchCriteriaBuilder + * @param GetCompleteOrderStatusList $getCompleteOrderStatusList */ public function __construct( OrderRepositoryInterface $orderRepository, - SearchCriteriaBuilder $searchCriteriaBuilder + SearchCriteriaBuilder $searchCriteriaBuilder, + GetCompleteOrderStatusList $getCompleteOrderStatusList ) { $this->orderRepository = $orderRepository; $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->getCompleteOrderStatusList = $getCompleteOrderStatusList; } /** @@ -51,11 +59,7 @@ public function execute(array $orderIds): array /** @var SearchCriteriaInterface $filter */ $filter = $this->searchCriteriaBuilder ->addFilter('entity_id', $orderIds, 'in') - ->addFilter('state', [ - Order::STATE_COMPLETE, - Order::STATE_CLOSED, - Order::STATE_CANCELED - ], 'in') + ->addFilter('state', $this->getCompleteOrderStatusList->execute(), 'in') ->create(); $orderSearchResult = $this->orderRepository->getList($filter); diff --git a/InventoryReservationCli/Model/GetOrdersInNotFinalState.php b/InventoryReservationCli/Model/GetOrdersInNotFinalState.php index 358c0d16a947..b39f14491a5f 100644 --- a/InventoryReservationCli/Model/GetOrdersInNotFinalState.php +++ b/InventoryReservationCli/Model/GetOrdersInNotFinalState.php @@ -28,16 +28,24 @@ class GetOrdersInNotFinalState */ private $searchCriteriaBuilder; + /** + * @var GetCompleteOrderStatusList + */ + private $getCompleteOrderStatusList; + /** * @param OrderRepositoryInterface $orderRepository * @param SearchCriteriaBuilder $searchCriteriaBuilder + * @param GetCompleteOrderStatusList $getCompleteOrderStatusList */ public function __construct( OrderRepositoryInterface $orderRepository, - SearchCriteriaBuilder $searchCriteriaBuilder + SearchCriteriaBuilder $searchCriteriaBuilder, + GetCompleteOrderStatusList $getCompleteOrderStatusList ) { $this->orderRepository = $orderRepository; $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->getCompleteOrderStatusList = $getCompleteOrderStatusList; } /** @@ -49,11 +57,7 @@ public function execute(): array { /** @var SearchCriteriaInterface $filter */ $filter = $this->searchCriteriaBuilder - ->addFilter('state', [ - Order::STATE_COMPLETE, - Order::STATE_CLOSED, - Order::STATE_CANCELED - ], 'nin') + ->addFilter('state', $this->getCompleteOrderStatusList->execute(), 'nin') ->create(); $orderSearchResult = $this->orderRepository->getList($filter); diff --git a/InventoryReservationCli/Model/GetSalableQuantityCompensations.php b/InventoryReservationCli/Model/GetSalableQuantityCompensations.php deleted file mode 100644 index 9d86045def56..000000000000 --- a/InventoryReservationCli/Model/GetSalableQuantityCompensations.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryReservationCli\Model; - -use Magento\Framework\Serialize\SerializerInterface; -use Magento\InventoryReservationsApi\Model\ReservationBuilderInterface; -use Magento\InventoryReservationsApi\Model\ReservationInterface; -use Magento\InventorySalesApi\Model\StockByWebsiteIdResolverInterface; - -/** - * Returns compensation reservations for given inconsistencies - */ -class GetSalableQuantityCompensations -{ - /** - * @var ReservationBuilderInterface - */ - private $reservationBuilder; - - /** - * @var SerializerInterface - */ - private $serializer; - - /** - * @var StockByWebsiteIdResolverInterface - */ - private $stockByWebsiteIdResolver; - - /** - * @param ReservationBuilderInterface $reservationBuilder - * @param SerializerInterface $serializer - * @param StockByWebsiteIdResolverInterface $stockByWebsiteIdResolver - */ - public function __construct( - ReservationBuilderInterface $reservationBuilder, - SerializerInterface $serializer, - StockByWebsiteIdResolverInterface $stockByWebsiteIdResolver - ) { - $this->reservationBuilder = $reservationBuilder; - $this->serializer = $serializer; - $this->stockByWebsiteIdResolver = $stockByWebsiteIdResolver; - } - - /** - * Returns compensation reservations for given inconsistencies - * - * @param SalableQuantityInconsistency[] $inconsistencies - * @return ReservationInterface[] - * @throws \Magento\Framework\Validation\ValidationException - */ - public function execute(array $inconsistencies): array - { - $compensations = []; - foreach ($inconsistencies as $inconsistency) { - foreach ($inconsistency->getItems() as $sku => $quantity) { - $compensations[] = $this->reservationBuilder - ->setSku($sku) - ->setQuantity((float)$quantity * -1) - ->setStockId($inconsistency->getStockId()) - ->setMetadata($this->serializer->serialize([ - 'event_type' => 'manual_compensation', - 'object_type' => 'order', - 'object_id' => $inconsistency->getObjectId(), - ])) - ->build(); - } - } - - return $compensations; - } -} From ed4ae499db5f7e4404908e4e38b99725a9397b62 Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Wed, 24 Apr 2019 01:09:23 +0300 Subject: [PATCH 188/231] MSI-2062: Fixed issues with wrong stock and additional check for is product assigned to stock --- .../Model/ExportStockIndexData.php | 15 ++++- .../Model/GetStockItemConfiguration.php | 52 +++++++++++++++ .../Model/PreciseExportStockProcessor.php | 64 ++++++++++--------- .../ResourceModel/StockIndexDumpProcessor.php | 10 +-- 4 files changed, 105 insertions(+), 36 deletions(-) create mode 100644 InventoryExportStock/Model/GetStockItemConfiguration.php diff --git a/InventoryExportStock/Model/ExportStockIndexData.php b/InventoryExportStock/Model/ExportStockIndexData.php index cb83a33c7a18..b861c4062590 100644 --- a/InventoryExportStock/Model/ExportStockIndexData.php +++ b/InventoryExportStock/Model/ExportStockIndexData.php @@ -11,6 +11,8 @@ use Magento\InventoryExportStock\Model\ResourceModel\StockIndexDumpProcessor; use Magento\InventoryExportStockApi\Api\ExportStockIndexDataInterface; use Magento\InventorySales\Model\ResourceModel\GetWebsiteIdByWebsiteCode; +use Magento\InventorySalesApi\Api\Data\SalesChannelInterface; +use Magento\InventorySalesApi\Api\StockResolverInterface; /** * Class ExportStockIndexData provides stock index export @@ -26,19 +28,26 @@ class ExportStockIndexData implements ExportStockIndexDataInterface * @var GetWebsiteIdByWebsiteCode */ private $getWebsiteIdByWebsiteCode; + /** + * @var StockResolverInterface + */ + private $stockResolver; /** * ExportStockIndexData constructor * * @param StockIndexDumpProcessor $stockIndexDumpProcessor * @param GetWebsiteIdByWebsiteCode $getWebsiteIdByWebsiteCode + * @param StockResolverInterface $stockResolver */ public function __construct( StockIndexDumpProcessor $stockIndexDumpProcessor, - GetWebsiteIdByWebsiteCode $getWebsiteIdByWebsiteCode + GetWebsiteIdByWebsiteCode $getWebsiteIdByWebsiteCode, + StockResolverInterface $stockResolver ) { $this->stockIndexDumpProcessor = $stockIndexDumpProcessor; $this->getWebsiteIdByWebsiteCode = $getWebsiteIdByWebsiteCode; + $this->stockResolver = $stockResolver; } /** @@ -51,7 +60,9 @@ public function __construct( public function execute(string $websiteCode): array { $websiteId = $this->getWebsiteIdByWebsiteCode->execute($websiteCode); + $stockId = $this->stockResolver + ->execute(SalesChannelInterface::TYPE_WEBSITE, $websiteCode)->getStockId(); - return $this->stockIndexDumpProcessor->execute($websiteId); + return $this->stockIndexDumpProcessor->execute($websiteId, $stockId); } } diff --git a/InventoryExportStock/Model/GetStockItemConfiguration.php b/InventoryExportStock/Model/GetStockItemConfiguration.php new file mode 100644 index 000000000000..74b60217a95a --- /dev/null +++ b/InventoryExportStock/Model/GetStockItemConfiguration.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryExportStock\Model; + +use Magento\InventoryConfiguration\Model\GetLegacyStockItem; +use Magento\InventoryConfiguration\Model\StockItemConfigurationFactory; +use Magento\InventoryConfigurationApi\Api\Data\StockItemConfigurationInterface; + +/** + * @inheritdoc + */ +class GetStockItemConfiguration +{ + /** + * @var GetLegacyStockItem + */ + private $getLegacyStockItem; + + /** + * @var StockItemConfigurationFactory + */ + private $stockItemConfigurationFactory; + + /** + * @param GetLegacyStockItem $getLegacyStockItem + * @param StockItemConfigurationFactory $stockItemConfigurationFactory + */ + public function __construct( + GetLegacyStockItem $getLegacyStockItem, + StockItemConfigurationFactory $stockItemConfigurationFactory + ) { + $this->getLegacyStockItem = $getLegacyStockItem; + $this->stockItemConfigurationFactory = $stockItemConfigurationFactory; + } + + /** + * @inheritdoc + */ + public function execute(string $sku): StockItemConfigurationInterface + { + return $this->stockItemConfigurationFactory->create( + [ + 'stockItem' => $this->getLegacyStockItem->execute($sku) + ] + ); + } +} diff --git a/InventoryExportStock/Model/PreciseExportStockProcessor.php b/InventoryExportStock/Model/PreciseExportStockProcessor.php index dfe881dd068b..22aa9050a72c 100644 --- a/InventoryExportStock/Model/PreciseExportStockProcessor.php +++ b/InventoryExportStock/Model/PreciseExportStockProcessor.php @@ -11,7 +11,6 @@ use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\LocalizedException; use Magento\InventoryApi\Model\IsProductAssignedToStockInterface; -use Magento\InventoryConfigurationApi\Api\GetStockItemConfigurationInterface; use Magento\InventoryConfigurationApi\Exception\SkuIsNotAssignedToStockException; use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForSkuInterface; use Magento\InventorySalesApi\Api\GetProductSalableQtyInterface; @@ -33,7 +32,7 @@ class PreciseExportStockProcessor private $getProductSalableQty; /** - * @var GetStockItemConfigurationInterface + * @var GetStockItemConfiguration */ private $getStockItemConfiguration; @@ -41,37 +40,38 @@ class PreciseExportStockProcessor * @var GetQtyForNotManageStock */ private $getQtyForNotManageStock; - /** - * @var IsProductAssignedToStockInterface - */ - private $isProductAssignedToStock; + /** * @var IsProductSalableInterface */ private $isProductSalable; + /** + * @var IsProductAssignedToStockInterface + */ + private $isProductAssignedToStock; /** * @param IsSourceItemManagementAllowedForSkuInterface $isSourceItemManagementAllowedForSku * @param GetProductSalableQtyInterface $getProductSalableQty - * @param GetStockItemConfigurationInterface $getStockItemConfiguration * @param GetQtyForNotManageStock $getQtyForNotManageStock - * @param IsProductAssignedToStockInterface $isProductAssignedToStock * @param IsProductSalableInterface $isProductSalable + * @param GetStockItemConfiguration $getStockItemConfiguration + * @param IsProductAssignedToStockInterface $isProductAssignedToStock */ public function __construct( IsSourceItemManagementAllowedForSkuInterface $isSourceItemManagementAllowedForSku, GetProductSalableQtyInterface $getProductSalableQty, - GetStockItemConfigurationInterface $getStockItemConfiguration, GetQtyForNotManageStock $getQtyForNotManageStock, - IsProductAssignedToStockInterface $isProductAssignedToStock, - IsProductSalableInterface $isProductSalable + IsProductSalableInterface $isProductSalable, + GetStockItemConfiguration $getStockItemConfiguration, + IsProductAssignedToStockInterface $isProductAssignedToStock ) { $this->isSourceItemManagementAllowedForSku = $isSourceItemManagementAllowedForSku; $this->getProductSalableQty = $getProductSalableQty; $this->getStockItemConfiguration = $getStockItemConfiguration; $this->getQtyForNotManageStock = $getQtyForNotManageStock; - $this->isProductAssignedToStock = $isProductAssignedToStock; $this->isProductSalable = $isProductSalable; + $this->isProductAssignedToStock = $isProductAssignedToStock; } /** @@ -89,18 +89,11 @@ public function execute(array $products, int $stockId): array $items = []; foreach ($skus as $sku) { try { - $qty = $this->getProductSalableQtyByStock($sku, $stockId); - $isSalable = $this->isProductSalable->execute($sku, $stockId); + $items[] = $this->getItem($sku, $stockId); } catch (SkuIsNotAssignedToStockException $e) { - $qty = 0.0000; - $isSalable = false; + continue; } - $items[] = [ - 'sku' => $sku, - 'qty' => (float)$qty, - 'is_salable' => $isSalable - ]; } return $items; @@ -124,27 +117,38 @@ private function getProductSkus(array $products): array } /** - * Provides qty by stock and sku + * Provides is product salable, and is salable by sku * * @param string $sku * @param int $stockId - * @return float|null - * @throws InputException - * @throws LocalizedException + * @return array * @throws SkuIsNotAssignedToStockException + * @throws LocalizedException */ - private function getProductSalableQtyByStock(string $sku, int $stockId): ?float + private function getItem(string $sku, int $stockId): array { - if (!$this->getStockItemConfiguration->execute($sku, $stockId)->isManageStock()) { - return $this->getQtyForNotManageStock->execute(); + if (!$this->getStockItemConfiguration->execute($sku)->isManageStock()) { + return [ + 'sku' => $sku, + 'qty' => $this->getQtyForNotManageStock->execute(), + 'is_salable' => true + ]; } if (!$this->isProductAssignedToStock->execute($sku, $stockId)) { throw new SkuIsNotAssignedToStockException(__('The requested sku is not assigned to given stock.')); } if (!$this->isSourceItemManagementAllowedForSku->execute($sku)) { - return null; + return [ + 'sku' => $sku, + 'qty' => null, + 'is_salable' => $this->isProductSalable->execute($sku, $stockId) + ]; } - return $this->getProductSalableQty->execute($sku, $stockId); + return [ + 'sku' => $sku, + 'qty' => $this->getProductSalableQty->execute($sku, $stockId), + 'is_salable' => $this->isProductSalable->execute($sku, $stockId) + ]; } } diff --git a/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php b/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php index f94a824bede2..b0559133cfe5 100644 --- a/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php +++ b/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php @@ -87,17 +87,18 @@ public function __construct( * Provides sku and qty of products dumping them from stock index table * * @param int $websiteId + * @param int $stockId * @return array * @throws LocalizedException */ - public function execute(int $websiteId): array + public function execute(int $websiteId, int $stockId): array { $this->connection = $this->resourceConnection->getConnection(); $select = $this->connection->select(); try { $select->union([ $this->getStockItemSelect($websiteId), - $this->getStockIndexSelect($websiteId) + $this->getStockIndexSelect($websiteId, $stockId) ]); } catch (\Exception $e) { $this->logger->critical($e->getMessage(), $e->getTrace()); @@ -111,12 +112,13 @@ public function execute(int $websiteId): array * Provides stock select * * @param int $websiteId + * @param int $stockId * @return Select */ - private function getStockIndexSelect(int $websiteId): Select + private function getStockIndexSelect(int $websiteId, int $stockId): Select { $stockIndexTableName = $this->resourceConnection - ->getTableName($this->stockIndexTableNameResolver->execute($websiteId)); + ->getTableName($this->stockIndexTableNameResolver->execute($stockId)); $legacyStockItemTable = $this->resourceConnection ->getTableName('cataloginventory_stock_item'); From 0402c5e2642ddd8b07a59f182a37e1b75f543237 Mon Sep 17 00:00:00 2001 From: vad1m777 <gam.vadik@gmail.com> Date: Wed, 24 Apr 2019 12:41:31 +0300 Subject: [PATCH 189/231] fix php static --- InventoryExportStock/composer.json | 8 ++++++-- InventoryExportStockApi/composer.json | 3 +-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/InventoryExportStock/composer.json b/InventoryExportStock/composer.json index 31cad87c0f43..fe764fcaff12 100644 --- a/InventoryExportStock/composer.json +++ b/InventoryExportStock/composer.json @@ -5,10 +5,14 @@ "php": "~7.1.3||~7.2.0", "magento/framework": "*", "magento/module-inventory-api": "*", - "magento/module-inventory-catalog-api": "*", + "magento/module-inventory-export-stock-api": "*", "magento/module-inventory-sales-api": "*", + "magento/module-inventory-sales": "*", "magento/module-catalog": "*", - "magento/module-inventory-export-stock-api": "*" + "magento/module-catalog-inventory": "*", + "magento/module-inventory-indexer": "*", + "magento/module-inventory-configuration": "*", + "magento/module-inventory-configuration-api": "*" }, "type": "magento2-module", "license": [ diff --git a/InventoryExportStockApi/composer.json b/InventoryExportStockApi/composer.json index d4cca0c653f3..ec08f5717551 100644 --- a/InventoryExportStockApi/composer.json +++ b/InventoryExportStockApi/composer.json @@ -3,8 +3,7 @@ "description": "N/A", "require": { "php": "~7.1.3||~7.2.0", - "magento/framework": "*", - "magento/module-inventory-api": "*" + "magento/framework": "*" }, "type": "magento2-module", "license": [ From fbe8cc1b3718ffee0f91ac2ab650179573ba0a07 Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Wed, 24 Apr 2019 17:02:26 +0300 Subject: [PATCH 190/231] MSI-2062: Changed order of conditions in PrciseProcessor --- .../Model/PreciseExportStockProcessor.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/InventoryExportStock/Model/PreciseExportStockProcessor.php b/InventoryExportStock/Model/PreciseExportStockProcessor.php index 22aa9050a72c..f88b3fb183db 100644 --- a/InventoryExportStock/Model/PreciseExportStockProcessor.php +++ b/InventoryExportStock/Model/PreciseExportStockProcessor.php @@ -45,6 +45,7 @@ class PreciseExportStockProcessor * @var IsProductSalableInterface */ private $isProductSalable; + /** * @var IsProductAssignedToStockInterface */ @@ -127,6 +128,13 @@ private function getProductSkus(array $products): array */ private function getItem(string $sku, int $stockId): array { + if (!$this->isSourceItemManagementAllowedForSku->execute($sku)) { + return [ + 'sku' => $sku, + 'qty' => null, + 'is_salable' => $this->isProductSalable->execute($sku, $stockId) + ]; + } if (!$this->getStockItemConfiguration->execute($sku)->isManageStock()) { return [ 'sku' => $sku, @@ -137,13 +145,6 @@ private function getItem(string $sku, int $stockId): array if (!$this->isProductAssignedToStock->execute($sku, $stockId)) { throw new SkuIsNotAssignedToStockException(__('The requested sku is not assigned to given stock.')); } - if (!$this->isSourceItemManagementAllowedForSku->execute($sku)) { - return [ - 'sku' => $sku, - 'qty' => null, - 'is_salable' => $this->isProductSalable->execute($sku, $stockId) - ]; - } return [ 'sku' => $sku, From 3f73d760bab2587a7ba2add8442b803432d0f34f Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Wed, 24 Apr 2019 17:05:33 +0300 Subject: [PATCH 191/231] MSI-2043: adjusted test and fixed backorders --- .../Test/_files/source_items_out_of_stock.php | 85 +++---------------- .../source_items_out_of_stock_rollback.php | 32 +------ .../Order/PlaceOrderOnDefaultStockTest.php | 1 + ...s_for_bundle_options_on_default_source.php | 46 ++++++++++ ...dle_options_on_default_source_rollback.php | 34 ++++++++ .../Model/Import/Command/Append.php | 22 +++++ .../IsAnySourceItemInStockCondition.php | 50 ++++++----- .../IsAnySourceItemInStockCondition.php | 31 ++++--- .../IsAnySourceItemInStockConditionTest.php | 16 ++-- .../IsProductSalable/MinQtyConditionTest.php | 4 +- .../IsAnySourceItemInStockConditionTest.php | 16 ++-- InventorySales/i18n/en_US.csv | 1 + 12 files changed, 183 insertions(+), 155 deletions(-) create mode 100644 InventoryBundleProduct/Test/_files/source_items_for_bundle_options_on_default_source.php create mode 100644 InventoryBundleProduct/Test/_files/source_items_for_bundle_options_on_default_source_rollback.php diff --git a/InventoryApi/Test/_files/source_items_out_of_stock.php b/InventoryApi/Test/_files/source_items_out_of_stock.php index 8c49581f5768..083e65687f1d 100644 --- a/InventoryApi/Test/_files/source_items_out_of_stock.php +++ b/InventoryApi/Test/_files/source_items_out_of_stock.php @@ -1,89 +1,32 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. */ declare(strict_types=1); +require_once __DIR__ . '/source_items.php'; + use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\InventoryApi\Api\Data\SourceItemInterface; -use Magento\InventoryApi\Api\Data\SourceItemInterfaceFactory; +use Magento\InventoryApi\Api\SourceItemRepositoryInterface; use Magento\InventoryApi\Api\SourceItemsSaveInterface; use Magento\TestFramework\Helper\Bootstrap; /** @var DataObjectHelper $dataObjectHelper */ $dataObjectHelper = Bootstrap::getObjectManager()->get(DataObjectHelper::class); -/** @var SourceItemInterfaceFactory $sourceItemFactory */ -$sourceItemFactory = Bootstrap::getObjectManager()->get(SourceItemInterfaceFactory::class); +/** @var SourceItemRepositoryInterface $sourceItemRepository */ +$sourceItemRepository = Bootstrap::getObjectManager()->get(SourceItemRepositoryInterface::class); +/** @var SearchCriteriaBuilder $searchCriteriaBuilder */ +$searchCriteriaBuilder = Bootstrap::getObjectManager()->get(SearchCriteriaBuilder::class); /** @var SourceItemsSaveInterface $sourceItemsSave */ $sourceItemsSave = Bootstrap::getObjectManager()->get(SourceItemsSaveInterface::class); -/** - * SKU-1 - EU-source-1(id:10) - 5.5qty - * SKU-1 - EU-source-2(id:20) - 3qty - * SKU-1 - EU-source-3(id:30) - 10qty (out of stock) - * SKU-1 - EU-source-4(id:40) - 10qty (disabled source) - * - * SKU-2 - US-source-1(id:30) - 5qty - * - * SKU-3 - EU-source-2(id:20) - 6qty (out of stock) - */ -$sourcesItemsData = [ - [ - SourceItemInterface::SOURCE_CODE => 'eu-1', - SourceItemInterface::SKU => 'SKU-1', - SourceItemInterface::QUANTITY => 5.5, - SourceItemInterface::STATUS => SourceItemInterface::STATUS_OUT_OF_STOCK, - ], - [ - SourceItemInterface::SOURCE_CODE => 'eu-2', - SourceItemInterface::SKU => 'SKU-1', - SourceItemInterface::QUANTITY => 3, - SourceItemInterface::STATUS => SourceItemInterface::STATUS_OUT_OF_STOCK, - ], - [ - SourceItemInterface::SOURCE_CODE => 'eu-3', - SourceItemInterface::SKU => 'SKU-1', - SourceItemInterface::QUANTITY => 10, - SourceItemInterface::STATUS => SourceItemInterface::STATUS_OUT_OF_STOCK, - ], - [ - SourceItemInterface::SOURCE_CODE => 'eu-disabled', - SourceItemInterface::SKU => 'SKU-1', - SourceItemInterface::QUANTITY => 10, - SourceItemInterface::STATUS => SourceItemInterface::STATUS_OUT_OF_STOCK, - ], - [ - SourceItemInterface::SOURCE_CODE => 'us-1', - SourceItemInterface::SKU => 'SKU-2', - SourceItemInterface::QUANTITY => 5, - SourceItemInterface::STATUS => SourceItemInterface::STATUS_OUT_OF_STOCK, - ], - [ - SourceItemInterface::SOURCE_CODE => 'eu-2', - SourceItemInterface::SKU => 'SKU-3', - SourceItemInterface::QUANTITY => 6, - SourceItemInterface::STATUS => SourceItemInterface::STATUS_OUT_OF_STOCK, - ], - [ - SourceItemInterface::SOURCE_CODE => 'eu-2', - SourceItemInterface::SKU => 'SKU-4', - SourceItemInterface::QUANTITY => 6, - SourceItemInterface::STATUS => SourceItemInterface::STATUS_OUT_OF_STOCK, - ], - [ - SourceItemInterface::SOURCE_CODE => 'eu-1', - SourceItemInterface::SKU => 'SKU-6', - SourceItemInterface::QUANTITY => 0, - SourceItemInterface::STATUS => SourceItemInterface::STATUS_OUT_OF_STOCK, - ], -]; +$sourceItems = $sourceItemRepository->getList($searchCriteriaBuilder->create())->getItems(); -$sourceItems = []; -foreach ($sourcesItemsData as $sourceItemData) { - /** @var SourceItemInterface $source */ - $sourceItem = $sourceItemFactory->create(); - $dataObjectHelper->populateWithArray($sourceItem, $sourceItemData, SourceItemInterface::class); - $sourceItems[] = $sourceItem; +foreach ($sourceItems as $sourceItem) { + $sourceItem->setStatus(SourceItemInterface::STATUS_OUT_OF_STOCK); } + $sourceItemsSave->execute($sourceItems); diff --git a/InventoryApi/Test/_files/source_items_out_of_stock_rollback.php b/InventoryApi/Test/_files/source_items_out_of_stock_rollback.php index 14c3b615a0f2..6edcbbecf20e 100644 --- a/InventoryApi/Test/_files/source_items_out_of_stock_rollback.php +++ b/InventoryApi/Test/_files/source_items_out_of_stock_rollback.php @@ -1,34 +1,8 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. */ declare(strict_types=1); -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\InventoryApi\Api\Data\SourceItemInterface; -use Magento\InventoryApi\Api\SourceItemRepositoryInterface; -use Magento\InventoryApi\Api\SourceItemsDeleteInterface; -use Magento\TestFramework\Helper\Bootstrap; - -/** @var SourceItemRepositoryInterface $sourceItemRepository */ -$sourceItemRepository = Bootstrap::getObjectManager()->get(SourceItemRepositoryInterface::class); -/** @var SourceItemsDeleteInterface $sourceItemsDelete */ -$sourceItemsDelete = Bootstrap::getObjectManager()->get(SourceItemsDeleteInterface::class); -/** @var SearchCriteriaBuilder $searchCriteriaBuilder */ -$searchCriteriaBuilder = Bootstrap::getObjectManager()->get(SearchCriteriaBuilder::class); - -$searchCriteria = $searchCriteriaBuilder->addFilter( - SourceItemInterface::SKU, - ['SKU-1', 'SKU-2', 'SKU-3', 'SKU-4'], - 'in' -)->create(); -$sourceItems = $sourceItemRepository->getList($searchCriteria)->getItems(); - -/** - * Tests which are wrapped with MySQL transaction clear all data by transaction rollback. - * In that case there is "if" which checks that SKU1, SKU2 and SKU3 still exists in database. - */ -if (!empty($sourceItems)) { - $sourceItemsDelete->execute($sourceItems); -} +require_once __DIR__ . '/source_items_rollback.php'; diff --git a/InventoryBundleProduct/Test/Integration/Order/PlaceOrderOnDefaultStockTest.php b/InventoryBundleProduct/Test/Integration/Order/PlaceOrderOnDefaultStockTest.php index 4b35f36a8492..6c5a82769a75 100644 --- a/InventoryBundleProduct/Test/Integration/Order/PlaceOrderOnDefaultStockTest.php +++ b/InventoryBundleProduct/Test/Integration/Order/PlaceOrderOnDefaultStockTest.php @@ -29,6 +29,7 @@ * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * * @magentoDataFixture ../../../../app/code/Magento/InventoryBundleProduct/Test/_files/default_stock_bundle_products.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryBundleProduct/Test/_files/source_items_for_bundle_options_on_default_source.php * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/quote.php * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php */ diff --git a/InventoryBundleProduct/Test/_files/source_items_for_bundle_options_on_default_source.php b/InventoryBundleProduct/Test/_files/source_items_for_bundle_options_on_default_source.php new file mode 100644 index 000000000000..48a5eea8383b --- /dev/null +++ b/InventoryBundleProduct/Test/_files/source_items_for_bundle_options_on_default_source.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Api\DataObjectHelper; +use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryApi\Api\Data\SourceItemInterfaceFactory; +use Magento\InventoryApi\Api\SourceItemsSaveInterface; +use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var DataObjectHelper $dataObjectHelper */ +$dataObjectHelper = Bootstrap::getObjectManager()->get(DataObjectHelper::class); +/** @var SourceItemInterfaceFactory $sourceItemFactory */ +$sourceItemFactory = Bootstrap::getObjectManager()->get(SourceItemInterfaceFactory::class); +/** @var SourceItemsSaveInterface $sourceItemsSave */ +$sourceItemsSave = Bootstrap::getObjectManager()->get(SourceItemsSaveInterface::class); +/** @var DefaultSourceProviderInterface $defaultSourceProvider */ +$defaultSourceProvider = Bootstrap::getObjectManager()->get(DefaultSourceProviderInterface::class); + +$sourcesItemsData = [ + [ + SourceItemInterface::SOURCE_CODE => $defaultSourceProvider->getCode(), + SourceItemInterface::SKU => 'simple-out-of-stock', + SourceItemInterface::QUANTITY => 0, + SourceItemInterface::STATUS => SourceItemInterface::STATUS_IN_STOCK, + ], + [ + SourceItemInterface::SOURCE_CODE => $defaultSourceProvider->getCode(), + SourceItemInterface::SKU => 'simple', + SourceItemInterface::QUANTITY => 22, + SourceItemInterface::STATUS => SourceItemInterface::STATUS_IN_STOCK, + ] +]; + +$sourceItems = []; +foreach ($sourcesItemsData as $sourceItemData) { + /** @var SourceItemInterface $source */ + $sourceItem = $sourceItemFactory->create(); + $dataObjectHelper->populateWithArray($sourceItem, $sourceItemData, SourceItemInterface::class); + $sourceItems[] = $sourceItem; +} +$sourceItemsSave->execute($sourceItems); diff --git a/InventoryBundleProduct/Test/_files/source_items_for_bundle_options_on_default_source_rollback.php b/InventoryBundleProduct/Test/_files/source_items_for_bundle_options_on_default_source_rollback.php new file mode 100644 index 000000000000..7b5877623525 --- /dev/null +++ b/InventoryBundleProduct/Test/_files/source_items_for_bundle_options_on_default_source_rollback.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryApi\Api\SourceItemRepositoryInterface; +use Magento\InventoryApi\Api\SourceItemsDeleteInterface; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var SourceItemRepositoryInterface $sourceItemRepository */ +$sourceItemRepository = Bootstrap::getObjectManager()->get(SourceItemRepositoryInterface::class); +/** @var SourceItemsDeleteInterface $sourceItemsDelete */ +$sourceItemsDelete = Bootstrap::getObjectManager()->get(SourceItemsDeleteInterface::class); +/** @var SearchCriteriaBuilder $searchCriteriaBuilder */ +$searchCriteriaBuilder = Bootstrap::getObjectManager()->get(SearchCriteriaBuilder::class); + +$searchCriteria = $searchCriteriaBuilder->addFilter( + SourceItemInterface::SKU, + ['simple', 'simple-out-of-stock'], + 'in' +)->create(); +$sourceItems = $sourceItemRepository->getList($searchCriteria)->getItems(); + +/** + * Tests which are wrapped with MySQL transaction clear all data by transaction rollback. + * In that case there is "if" which checks that SKU1, SKU2 and SKU3 still exists in database. + */ +if (!empty($sourceItems)) { + $sourceItemsDelete->execute($sourceItems); +} diff --git a/InventoryImportExport/Model/Import/Command/Append.php b/InventoryImportExport/Model/Import/Command/Append.php index 48eec176810f..4fa247c8a1f6 100644 --- a/InventoryImportExport/Model/Import/Command/Append.php +++ b/InventoryImportExport/Model/Import/Command/Append.php @@ -42,7 +42,29 @@ public function __construct( */ public function execute(array $bunch) { + \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Psr\Log\LoggerInterface::class)->debug('time start ' . time()); + \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Psr\Log\LoggerInterface::class)->debug('bunch-size ' . count($bunch)); + $sourceItems = $this->sourceItemConvert->convert($bunch); $this->sourceItemsSave->execute($sourceItems); + \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Psr\Log\LoggerInterface::class)->debug('memory ' . $this->getNiceFileSize(memory_get_usage())); + + \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Psr\Log\LoggerInterface::class)->debug('time end ' . time()); + } + + function getNiceFileSize($bytes, $binaryPrefix=true) { + if ($binaryPrefix) { + $unit=array('B','KiB','MiB','GiB','TiB','PiB'); + if ($bytes==0) return '0 ' . $unit[0]; + return @round($bytes/pow(1024,($i=floor(log($bytes,1024)))),2) .' '. (isset($unit[$i]) ? $unit[$i] : 'B'); + } else { + $unit=array('B','KB','MB','GB','TB','PB'); + if ($bytes==0) return '0 ' . $unit[0]; + return @round($bytes/pow(1000,($i=floor(log($bytes,1000)))),2) .' '. (isset($unit[$i]) ? $unit[$i] : 'B'); + } } } diff --git a/InventorySales/Model/IsProductSalableCondition/IsAnySourceItemInStockCondition.php b/InventorySales/Model/IsProductSalableCondition/IsAnySourceItemInStockCondition.php index 75ffdb26c818..4450d5bf0f6b 100644 --- a/InventorySales/Model/IsProductSalableCondition/IsAnySourceItemInStockCondition.php +++ b/InventorySales/Model/IsProductSalableCondition/IsAnySourceItemInStockCondition.php @@ -9,13 +9,13 @@ use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryApi\Api\GetSourcesAssignedToStockOrderedByPriorityInterface; use Magento\InventoryApi\Api\SourceItemRepositoryInterface; -use Magento\InventoryCatalogApi\Model\GetProductTypesBySkusInterface; -use Magento\InventoryConfigurationApi\Model\GetAllowedProductTypesForSourceItemManagementInterface; +use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForSkuInterface; use Magento\InventorySalesApi\Api\IsProductSalableInterface; /** - * @inheritdoc + * Check if product has source items with the in stock status */ class IsAnySourceItemInStockCondition implements IsProductSalableInterface { @@ -30,31 +30,31 @@ class IsAnySourceItemInStockCondition implements IsProductSalableInterface private $searchCriteriaBuilder; /** - * @var GetProductTypesBySkusInterface + * @var GetSourcesAssignedToStockOrderedByPriorityInterface */ - private $getProductTypesBySkus; + private $getSourcesAssignedToStockOrderedByPriority; /** - * @var GetAllowedProductTypesForSourceItemManagementInterface + * @var IsSourceItemManagementAllowedForSkuInterface */ - private $getManageableTypes; + private $isSourceItemManagementAllowedForSku; /** * @param SourceItemRepositoryInterface $sourceItemRepository * @param SearchCriteriaBuilder $searchCriteriaBuilder - * @param GetProductTypesBySkusInterface $getProductTypesBySkus - * @param GetAllowedProductTypesForSourceItemManagementInterface $getManageableTypes + * @param GetSourcesAssignedToStockOrderedByPriorityInterface $getSourcesAssignedToStockOrderedByPriority + * @param IsSourceItemManagementAllowedForSkuInterface $isSourceItemManagementAllowedForSku */ public function __construct( SourceItemRepositoryInterface $sourceItemRepository, SearchCriteriaBuilder $searchCriteriaBuilder, - GetProductTypesBySkusInterface $getProductTypesBySkus, - GetAllowedProductTypesForSourceItemManagementInterface $getManageableTypes + GetSourcesAssignedToStockOrderedByPriorityInterface $getSourcesAssignedToStockOrderedByPriority, + IsSourceItemManagementAllowedForSkuInterface $isSourceItemManagementAllowedForSku ) { $this->sourceItemRepository = $sourceItemRepository; $this->searchCriteriaBuilder = $searchCriteriaBuilder; - $this->getProductTypesBySkus = $getProductTypesBySkus; - $this->getManageableTypes = $getManageableTypes; + $this->getSourcesAssignedToStockOrderedByPriority = $getSourcesAssignedToStockOrderedByPriority; + $this->isSourceItemManagementAllowedForSku = $isSourceItemManagementAllowedForSku; } /** @@ -64,11 +64,15 @@ public function __construct( */ public function execute(string $sku, int $stockId): bool { - if (!$this->isProductStockManageable($sku)) { + if (!$this->isSourceItemManagementAllowedForSku->execute($sku)) { return true; } + + $sourceCodes = $this->getSourceCodesAssignedToStock($stockId); + $searchCriteria = $this->searchCriteriaBuilder ->addFilter(SourceItemInterface::SKU, $sku) + ->addFilter(SourceItemInterface::SOURCE_CODE, $sourceCodes, 'in') ->addFilter(SourceItemInterface::STATUS, SourceItemInterface::STATUS_IN_STOCK) ->create(); @@ -78,15 +82,19 @@ public function execute(string $sku, int $stockId): bool } /** - * @param string $sku + * Provides source codes for certain stock * - * @return bool + * @param int $stockId + * @return array */ - private function isProductStockManageable(string $sku): bool + private function getSourceCodesAssignedToStock(int $stockId): array { - return in_array( - $this->getProductTypesBySkus->execute([$sku])[$sku], - $this->getManageableTypes->execute() - ); + $sources = $this->getSourcesAssignedToStockOrderedByPriority->execute($stockId); + $sourceCodes = []; + foreach ($sources as $source) { + $sourceCodes[] = $source->getSourceCode(); + } + + return $sourceCodes; } } diff --git a/InventorySales/Model/IsProductSalableForRequestedQtyCondition/IsAnySourceItemInStockCondition.php b/InventorySales/Model/IsProductSalableForRequestedQtyCondition/IsAnySourceItemInStockCondition.php index 7e27ffd37277..4396a69ee72f 100644 --- a/InventorySales/Model/IsProductSalableForRequestedQtyCondition/IsAnySourceItemInStockCondition.php +++ b/InventorySales/Model/IsProductSalableForRequestedQtyCondition/IsAnySourceItemInStockCondition.php @@ -7,20 +7,19 @@ namespace Magento\InventorySales\Model\IsProductSalableForRequestedQtyCondition; -use Magento\InventorySalesApi\Api\IsProductSalableForRequestedQtyInterface; +use Magento\InventorySales\Model\IsProductSalableCondition\IsAnySourceItemInStockCondition as IsAnySourceItemInStock; +use Magento\InventorySalesApi\Api\Data\ProductSalabilityErrorInterfaceFactory; use Magento\InventorySalesApi\Api\Data\ProductSalableResultInterface; use Magento\InventorySalesApi\Api\Data\ProductSalableResultInterfaceFactory; -use Magento\InventorySalesApi\Api\Data\ProductSalabilityErrorInterfaceFactory; -use Magento\InventorySales\Model\IsProductSalableCondition\IsAnySourceItemInStockCondition - as IsAnySourceInStockConditionCondition; +use Magento\InventorySalesApi\Api\IsProductSalableForRequestedQtyInterface; /** - * @inheritdoc + * @inheritDoc */ class IsAnySourceItemInStockCondition implements IsProductSalableForRequestedQtyInterface { /** - * @var IsAnySourceInStockConditionCondition + * @var IsAnySourceItemInStock */ private $isAnySourceInStockCondition; @@ -35,12 +34,12 @@ class IsAnySourceItemInStockCondition implements IsProductSalableForRequestedQty private $productSalableResultFactory; /** - * @param IsAnySourceInStockConditionCondition $isAnySourceInStockCondition + * @param IsAnySourceItemInStock $isAnySourceInStockCondition * @param ProductSalabilityErrorInterfaceFactory $productSalabilityErrorFactory * @param ProductSalableResultInterfaceFactory $productSalableResultFactory */ public function __construct( - IsAnySourceInStockConditionCondition $isAnySourceInStockCondition, + IsAnySourceItemInStock $isAnySourceInStockCondition, ProductSalabilityErrorInterfaceFactory $productSalabilityErrorFactory, ProductSalableResultInterfaceFactory $productSalableResultFactory ) { @@ -56,16 +55,16 @@ public function __construct( */ public function execute(string $sku, int $stockId, float $requestedQty): ProductSalableResultInterface { - $errors = []; $isValid = $this->isAnySourceInStockCondition->execute($sku, $stockId); - if (!$isValid) { - $errors = [ - $this->productSalabilityErrorFactory->create([ - 'code' => 'stock_item_is_any_source_in_stock-no_source_items_in_stock', - 'message' => __('There are no source items with in stock status') - ]) - ]; + if ($isValid) { + return $this->productSalableResultFactory->create(['errors' => []]); } + $errors = [ + $this->productSalabilityErrorFactory->create([ + 'code' => 'is_any_source_item_in_stock-no_source_items_in_stock', + 'message' => __('There are no source items with the in stock status') + ]) + ]; return $this->productSalableResultFactory->create(['errors' => $errors]); } diff --git a/InventorySales/Test/Integration/IsProductSalable/IsAnySourceItemInStockConditionTest.php b/InventorySales/Test/Integration/IsProductSalable/IsAnySourceItemInStockConditionTest.php index 05f646c35baf..23d7ced822e8 100644 --- a/InventorySales/Test/Integration/IsProductSalable/IsAnySourceItemInStockConditionTest.php +++ b/InventorySales/Test/Integration/IsProductSalable/IsAnySourceItemInStockConditionTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. */ declare(strict_types=1); @@ -11,16 +11,16 @@ use Magento\CatalogInventory\Api\Data\StockItemInterface; use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory; use Magento\CatalogInventory\Api\StockItemRepositoryInterface; -use Magento\InventorySales\Model\IsProductSalableCondition\IsAnySourceItemInStockCondition; +use Magento\InventorySalesApi\Api\IsProductSalableInterface; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; class IsAnySourceItemInStockConditionTest extends TestCase { /** - * @var IsAnySourceItemInStockCondition + * @var IsProductSalableInterface */ - private $isAnySourceInStockCondition; + private $isProductSalable; /** * @var ProductRepositoryInterface @@ -40,8 +40,8 @@ class IsAnySourceItemInStockConditionTest extends TestCase protected function setUp() { $objectManager = Bootstrap::getObjectManager(); - $this->isAnySourceInStockCondition = $objectManager->get( - IsAnySourceItemInStockCondition::class + $this->isProductSalable = $objectManager->get( + IsProductSalableInterface::class ); $this->productRepository = $objectManager->get(ProductRepositoryInterface::class); $this->stockItemCriteriaFactory = $objectManager->get(StockItemCriteriaInterfaceFactory::class); @@ -69,6 +69,6 @@ public function testSourceItemsAreOutOfStock() $legacyStockItem->setBackorders(1); $legacyStockItem->setUseConfigBackorders(0); $this->stockItemRepository->save($legacyStockItem); - $this->assertFalse($this->isAnySourceInStockCondition->execute('SKU-1', 10)); + $this->assertFalse($this->isProductSalable->execute('SKU-1', 10)); } } diff --git a/InventorySales/Test/Integration/IsProductSalable/MinQtyConditionTest.php b/InventorySales/Test/Integration/IsProductSalable/MinQtyConditionTest.php index 618bce340166..a951a63ebc63 100644 --- a/InventorySales/Test/Integration/IsProductSalable/MinQtyConditionTest.php +++ b/InventorySales/Test/Integration/IsProductSalable/MinQtyConditionTest.php @@ -108,9 +108,9 @@ public function executeWithManageStockFalseAndMinQty(): array ['SKU-2', 10, false], ['SKU-2', 20, true], ['SKU-2', 30, true], - ['SKU-3', 10, true], + ['SKU-3', 10, false], ['SKU-3', 20, false], - ['SKU-3', 30, true], + ['SKU-3', 30, false], ['SKU-6', 10, true], ]; } diff --git a/InventorySales/Test/Integration/IsProductSalableForRequestedQty/IsAnySourceItemInStockConditionTest.php b/InventorySales/Test/Integration/IsProductSalableForRequestedQty/IsAnySourceItemInStockConditionTest.php index 669fa09046b7..d845463153f4 100644 --- a/InventorySales/Test/Integration/IsProductSalableForRequestedQty/IsAnySourceItemInStockConditionTest.php +++ b/InventorySales/Test/Integration/IsProductSalableForRequestedQty/IsAnySourceItemInStockConditionTest.php @@ -1,7 +1,7 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. */ declare(strict_types=1); @@ -11,16 +11,16 @@ use Magento\CatalogInventory\Api\Data\StockItemInterface; use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory; use Magento\CatalogInventory\Api\StockItemRepositoryInterface; -use Magento\InventorySales\Model\IsProductSalableForRequestedQtyCondition\IsAnySourceItemInStockCondition; +use Magento\InventorySalesApi\Api\IsProductSalableForRequestedQtyInterface; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; class IsAnySourceItemInStockConditionTest extends TestCase { /** - * @var IsAnySourceItemInStockCondition + * @var IsProductSalableForRequestedQtyInterface */ - private $isAnySourceInStockCondition; + private $isProductSalable; /** * @var ProductRepositoryInterface @@ -40,8 +40,8 @@ class IsAnySourceItemInStockConditionTest extends TestCase protected function setUp() { $objectManager = Bootstrap::getObjectManager(); - $this->isAnySourceInStockCondition = $objectManager->get( - IsAnySourceItemInStockCondition::class + $this->isProductSalable = $objectManager->get( + IsProductSalableForRequestedQtyInterface::class ); $this->productRepository = $objectManager->get(ProductRepositoryInterface::class); $this->stockItemCriteriaFactory = $objectManager->get(StockItemCriteriaInterfaceFactory::class); @@ -69,6 +69,6 @@ public function testSourceItemsAreOutOfStock() $legacyStockItem->setBackorders(1); $legacyStockItem->setUseConfigBackorders(0); $this->stockItemRepository->save($legacyStockItem); - $this->assertFalse($this->isAnySourceInStockCondition->execute('SKU-1', 10, 1)->isSalable()); + $this->assertFalse($this->isProductSalable->execute('SKU-1', 10, 1)->isSalable()); } } diff --git a/InventorySales/i18n/en_US.csv b/InventorySales/i18n/en_US.csv index 3b270b9ff389..95c08fe2f35c 100644 --- a/InventorySales/i18n/en_US.csv +++ b/InventorySales/i18n/en_US.csv @@ -24,3 +24,4 @@ "Stock has at least one sale channel and could not be deleted.","Stock has at least one sale channel and could not be deleted." "Could not replace Sales Channels for Stock","Could not replace Sales Channels for Stock" "Wrong condition.","Wrong condition." +"There are no source items with the in stock status","There are no source items with the in stock status" From 96905dbf89a0c79415eac448c4536c7169f032f7 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Wed, 24 Apr 2019 17:11:43 +0300 Subject: [PATCH 192/231] MSI-2043: revert unnesersary changes --- .../Model/Import/Command/Append.php | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/InventoryImportExport/Model/Import/Command/Append.php b/InventoryImportExport/Model/Import/Command/Append.php index 4fa247c8a1f6..48eec176810f 100644 --- a/InventoryImportExport/Model/Import/Command/Append.php +++ b/InventoryImportExport/Model/Import/Command/Append.php @@ -42,29 +42,7 @@ public function __construct( */ public function execute(array $bunch) { - \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Psr\Log\LoggerInterface::class)->debug('time start ' . time()); - \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Psr\Log\LoggerInterface::class)->debug('bunch-size ' . count($bunch)); - $sourceItems = $this->sourceItemConvert->convert($bunch); $this->sourceItemsSave->execute($sourceItems); - \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Psr\Log\LoggerInterface::class)->debug('memory ' . $this->getNiceFileSize(memory_get_usage())); - - \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Psr\Log\LoggerInterface::class)->debug('time end ' . time()); - } - - function getNiceFileSize($bytes, $binaryPrefix=true) { - if ($binaryPrefix) { - $unit=array('B','KiB','MiB','GiB','TiB','PiB'); - if ($bytes==0) return '0 ' . $unit[0]; - return @round($bytes/pow(1024,($i=floor(log($bytes,1024)))),2) .' '. (isset($unit[$i]) ? $unit[$i] : 'B'); - } else { - $unit=array('B','KB','MB','GB','TB','PB'); - if ($bytes==0) return '0 ' . $unit[0]; - return @round($bytes/pow(1000,($i=floor(log($bytes,1000)))),2) .' '. (isset($unit[$i]) ? $unit[$i] : 'B'); - } } } From 137083a13a7ac7362b2fc962b1cc7933586b2fec Mon Sep 17 00:00:00 2001 From: vad1m777 <gam.vadik@gmail.com> Date: Wed, 24 Apr 2019 17:18:38 +0300 Subject: [PATCH 193/231] fix php static --- .../Api/Data/ExportStockSalableQtySearchResultInterface.php | 4 +--- InventoryExportStockApi/Api/ExportStockIndexDataInterface.php | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/InventoryExportStockApi/Api/Data/ExportStockSalableQtySearchResultInterface.php b/InventoryExportStockApi/Api/Data/ExportStockSalableQtySearchResultInterface.php index 69d40075f53f..7b2237cbf9a6 100644 --- a/InventoryExportStockApi/Api/Data/ExportStockSalableQtySearchResultInterface.php +++ b/InventoryExportStockApi/Api/Data/ExportStockSalableQtySearchResultInterface.php @@ -16,9 +16,7 @@ interface ExportStockSalableQtySearchResultInterface extends SearchResultsInterface { /** - * Get stock data array - * - * @return array + * {@inheritdoc} */ public function getItems(); diff --git a/InventoryExportStockApi/Api/ExportStockIndexDataInterface.php b/InventoryExportStockApi/Api/ExportStockIndexDataInterface.php index 27e3653ead21..9cbd976f9edc 100644 --- a/InventoryExportStockApi/Api/ExportStockIndexDataInterface.php +++ b/InventoryExportStockApi/Api/ExportStockIndexDataInterface.php @@ -17,7 +17,7 @@ interface ExportStockIndexDataInterface * Provides stock index export from inventory_stock_% table * * @param string $websiteCode - * @return array + * @return string[] */ public function execute(string $websiteCode): array; } From a612b7de6e9746d54556baa8fc72df5b7e491295 Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Wed, 24 Apr 2019 18:48:20 +0300 Subject: [PATCH 194/231] MSI-2062: Set null for complex products dump export --- .../Model/ResourceModel/StockIndexDumpProcessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php b/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php index b0559133cfe5..f47cccedc977 100644 --- a/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php +++ b/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php @@ -131,7 +131,7 @@ private function getStockIndexSelect(int $websiteId, int $stockId): Select $select->from( ['stock_index' => $stockIndexTableName], [ - 'qty' => 'quantity', + 'qty' => new \Zend_Db_Expr('IF(product_entity.type_id = \'simple\', quantity, NULL)'), 'is_salable' => 'is_salable', 'sku' => 'sku' ] From 75b4da7851fdca3f4641501f56d4ee6052f833e8 Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Wed, 24 Apr 2019 11:04:45 -0500 Subject: [PATCH 195/231] Update preconditions of MSI-1973 for stability --- ...efaultStockAfterFullInvoiceInAdminTest.xml | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundForOrderWithSimpleProductOnDefaultStockAfterFullInvoiceInAdminTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundForOrderWithSimpleProductOnDefaultStockAfterFullInvoiceInAdminTest.xml index ef337d1f6139..68d56bbc7813 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundForOrderWithSimpleProductOnDefaultStockAfterFullInvoiceInAdminTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundForOrderWithSimpleProductOnDefaultStockAfterFullInvoiceInAdminTest.xml @@ -20,20 +20,34 @@ </annotations> <before> - <createData entity="MsiCustomer1" stepKey="createCustomer"/> <createData entity="BasicMsiStock1" stepKey="createStock"/> <createData entity="FullSource1" stepKey="createSource"/> <createData entity="SourceStockLinked1" stepKey="linkSourceStock"> <requiredEntity createDataKey="createStock"/> <requiredEntity createDataKey="createSource"/> </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <waitForPageLoad stepKey="waitForDashboardLoad"/> + + <comment userInput="Assign main website to default stock" stepKey="assignChannelToStockComment"/> + <amOnPage url="{{AdminManageStockPage.url}}" stepKey="navigateToStockListPageToAssignMainWebsiteToDefaultStock"/> + <waitForPageLoad time="30" stepKey="waitForStockListPageLoad"/> + <actionGroup ref="searchAdminDataGridByKeyword" stepKey="searchDefaultStockByNameForAssignMainWebsiteChannel"> + <argument name="keyword" value="_defaultStock.name"/> + </actionGroup> + <click selector="{{AdminGridRow.editByValue(_defaultStock.name)}}" stepKey="clickEditDefaultStock"/> + <waitForPageLoad time="30" stepKey="waitForDefaultStockPageLoaded"/> + <selectOption selector="{{AdminEditStockSalesChannelsSection.websites}}" userInput="Main Website" stepKey="selectDefaultWebsiteAsSalesChannelForDefaultStock"/> + <click selector="{{AdminGridMainControls.saveAndContinue}}" stepKey="saveDefaultStock"/> + <createData entity="SimpleSubCategory" stepKey="simpleCategory"/> <createData entity="SimpleProduct" stepKey="simpleProduct"> <field key="qty">100.00</field> <requiredEntity createDataKey="simpleCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <waitForPageLoad stepKey="waitForDashboardLoad"/> + + <createData entity="MsiCustomer1" stepKey="createCustomer"/> + </before> <after> <actionGroup ref="logout" stepKey="logoutOfAdmin1"/> From bc9f703df4593527ffccf024af8a994e4af408f6 Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Wed, 24 Apr 2019 19:33:04 +0300 Subject: [PATCH 196/231] MSI-2062: Set null for complex products dump export. IN clause added --- .../ResourceModel/StockIndexDumpProcessor.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php b/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php index f47cccedc977..10df4f44bf87 100644 --- a/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php +++ b/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php @@ -7,10 +7,13 @@ namespace Magento\InventoryExportStock\Model\ResourceModel; +use Magento\Catalog\Model\Product\Type; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; use Magento\Framework\App\ResourceConnection; use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\DB\Select; use Magento\Framework\Exception\LocalizedException; +use Magento\GroupedProduct\Model\Product\Type\Grouped; use Magento\InventoryExportStock\Model\GetQtyForNotManageStock; use Magento\InventoryIndexer\Model\StockIndexTableNameResolverInterface; use Magento\InventorySales\Model\ResourceModel\IsStockItemSalableCondition\ManageStockCondition as NotManageStockCondition; @@ -128,10 +131,19 @@ private function getStockIndexSelect(int $websiteId, int $stockId): Select ->getTableName('catalog_product_website'); $select = $this->connection->select(); + $ifExpression = ' + IF( + `product_entity`.`type_id` IN ( + \'' . Configurable::TYPE_CODE . '\', + \'' . Type::TYPE_BUNDLE . '\', + \'' . Grouped::TYPE_CODE . '\'), + NULL, + `quantity` + )'; $select->from( ['stock_index' => $stockIndexTableName], [ - 'qty' => new \Zend_Db_Expr('IF(product_entity.type_id = \'simple\', quantity, NULL)'), + 'qty' => new \Zend_Db_Expr($ifExpression), 'is_salable' => 'is_salable', 'sku' => 'sku' ] From 7884d575931be1e7e20a0e2b005115cdce4909a0 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Wed, 24 Apr 2019 12:35:44 -0500 Subject: [PATCH 197/231] #2170: Fix installation by updating wording to Salable instead of Saleable --- InventoryReservationCli/etc/di.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/InventoryReservationCli/etc/di.xml b/InventoryReservationCli/etc/di.xml index 1f1b5f0a8e82..226eee817e65 100644 --- a/InventoryReservationCli/etc/di.xml +++ b/InventoryReservationCli/etc/di.xml @@ -9,10 +9,10 @@ <type name="Magento\Framework\Console\CommandList"> <arguments> <argument name="commands" xsi:type="array"> - <item name="inventory_saleable_quantity_inconsistency" xsi:type="object"> + <item name="inventory_salable_quantity_inconsistency" xsi:type="object"> Magento\InventoryReservationCli\Command\ShowInconsistencies </item> - <item name="inventory_saleable_quantity_compensations" xsi:type="object"> + <item name="inventory_salable_quantity_compensations" xsi:type="object"> Magento\InventoryReservationCli\Command\CreateCompensations </item> </argument> @@ -20,7 +20,7 @@ </type> <type name="Magento\InventoryReservationCli\Command\ShowInconsistencies"> <arguments> - <argument name="getSaleableQuantityInconsistencies" xsi:type="object"> + <argument name="getSalableQuantityInconsistencies" xsi:type="object"> Magento\InventoryReservationCli\Model\GetSalableQuantityInconsistencies\Proxy </argument> <argument name="filterCompleteOrders" xsi:type="object"> From 0e3ca02f2bbf2cfdc6cca5eea390943ca68b7023 Mon Sep 17 00:00:00 2001 From: "al.kravchuk" <al.kravchuk@ism-ukraine.com> Date: Wed, 24 Apr 2019 20:40:29 +0300 Subject: [PATCH 198/231] MSI-2156 Adjust prs #2127, #2201. --- .../Test/_files/source_items_out_of_stock.php | 32 ----------------- .../source_items_out_of_stock_rollback.php | 8 ----- .../IsAnySourceItemInStockCondition.php | 7 +++- .../IsAnySourceItemInStockCondition.php | 17 +++++---- .../IsAnySourceItemInStockConditionTest.php | 35 +++++++++++++++--- .../ManageStockConditionTest.php | 4 +-- .../IsAnySourceItemInStockConditionTest.php | 36 ++++++++++++++++--- .../ManageStockConditionTest.php | 4 +-- 8 files changed, 81 insertions(+), 62 deletions(-) delete mode 100644 InventoryApi/Test/_files/source_items_out_of_stock.php delete mode 100644 InventoryApi/Test/_files/source_items_out_of_stock_rollback.php diff --git a/InventoryApi/Test/_files/source_items_out_of_stock.php b/InventoryApi/Test/_files/source_items_out_of_stock.php deleted file mode 100644 index 083e65687f1d..000000000000 --- a/InventoryApi/Test/_files/source_items_out_of_stock.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -require_once __DIR__ . '/source_items.php'; - -use Magento\Framework\Api\DataObjectHelper; -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\InventoryApi\Api\Data\SourceItemInterface; -use Magento\InventoryApi\Api\SourceItemRepositoryInterface; -use Magento\InventoryApi\Api\SourceItemsSaveInterface; -use Magento\TestFramework\Helper\Bootstrap; - -/** @var DataObjectHelper $dataObjectHelper */ -$dataObjectHelper = Bootstrap::getObjectManager()->get(DataObjectHelper::class); -/** @var SourceItemRepositoryInterface $sourceItemRepository */ -$sourceItemRepository = Bootstrap::getObjectManager()->get(SourceItemRepositoryInterface::class); -/** @var SearchCriteriaBuilder $searchCriteriaBuilder */ -$searchCriteriaBuilder = Bootstrap::getObjectManager()->get(SearchCriteriaBuilder::class); -/** @var SourceItemsSaveInterface $sourceItemsSave */ -$sourceItemsSave = Bootstrap::getObjectManager()->get(SourceItemsSaveInterface::class); - -$sourceItems = $sourceItemRepository->getList($searchCriteriaBuilder->create())->getItems(); - -foreach ($sourceItems as $sourceItem) { - $sourceItem->setStatus(SourceItemInterface::STATUS_OUT_OF_STOCK); -} - -$sourceItemsSave->execute($sourceItems); diff --git a/InventoryApi/Test/_files/source_items_out_of_stock_rollback.php b/InventoryApi/Test/_files/source_items_out_of_stock_rollback.php deleted file mode 100644 index 6edcbbecf20e..000000000000 --- a/InventoryApi/Test/_files/source_items_out_of_stock_rollback.php +++ /dev/null @@ -1,8 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -require_once __DIR__ . '/source_items_rollback.php'; diff --git a/InventorySales/Model/IsProductSalableCondition/IsAnySourceItemInStockCondition.php b/InventorySales/Model/IsProductSalableCondition/IsAnySourceItemInStockCondition.php index 4450d5bf0f6b..7862b101d501 100644 --- a/InventorySales/Model/IsProductSalableCondition/IsAnySourceItemInStockCondition.php +++ b/InventorySales/Model/IsProductSalableCondition/IsAnySourceItemInStockCondition.php @@ -85,14 +85,19 @@ public function execute(string $sku, int $stockId): bool * Provides source codes for certain stock * * @param int $stockId + * * @return array + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\LocalizedException */ private function getSourceCodesAssignedToStock(int $stockId): array { $sources = $this->getSourcesAssignedToStockOrderedByPriority->execute($stockId); $sourceCodes = []; foreach ($sources as $source) { - $sourceCodes[] = $source->getSourceCode(); + if ($source->isEnabled()) { + $sourceCodes[] = $source->getSourceCode(); + } } return $sourceCodes; diff --git a/InventorySales/Model/IsProductSalableForRequestedQtyCondition/IsAnySourceItemInStockCondition.php b/InventorySales/Model/IsProductSalableForRequestedQtyCondition/IsAnySourceItemInStockCondition.php index 4396a69ee72f..84adf3d6d19c 100644 --- a/InventorySales/Model/IsProductSalableForRequestedQtyCondition/IsAnySourceItemInStockCondition.php +++ b/InventorySales/Model/IsProductSalableForRequestedQtyCondition/IsAnySourceItemInStockCondition.php @@ -14,7 +14,7 @@ use Magento\InventorySalesApi\Api\IsProductSalableForRequestedQtyInterface; /** - * @inheritDoc + * @inheritdoc */ class IsAnySourceItemInStockCondition implements IsProductSalableForRequestedQtyInterface { @@ -55,16 +55,15 @@ public function __construct( */ public function execute(string $sku, int $stockId, float $requestedQty): ProductSalableResultInterface { - $isValid = $this->isAnySourceInStockCondition->execute($sku, $stockId); - if ($isValid) { - return $this->productSalableResultFactory->create(['errors' => []]); - } - $errors = [ - $this->productSalabilityErrorFactory->create([ + $errors = []; + + if (!$this->isAnySourceInStockCondition->execute($sku, $stockId)) { + $data = [ 'code' => 'is_any_source_item_in_stock-no_source_items_in_stock', 'message' => __('There are no source items with the in stock status') - ]) - ]; + ]; + $errors[] = $this->productSalabilityErrorFactory->create($data); + } return $this->productSalableResultFactory->create(['errors' => $errors]); } diff --git a/InventorySales/Test/Integration/IsProductSalable/IsAnySourceItemInStockConditionTest.php b/InventorySales/Test/Integration/IsProductSalable/IsAnySourceItemInStockConditionTest.php index 23d7ced822e8..f43fdeffe702 100644 --- a/InventorySales/Test/Integration/IsProductSalable/IsAnySourceItemInStockConditionTest.php +++ b/InventorySales/Test/Integration/IsProductSalable/IsAnySourceItemInStockConditionTest.php @@ -53,13 +53,22 @@ protected function setUp() * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stocks.php * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stock_source_links.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items_out_of_stock.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php + * + * @dataProvider sourceItemsStockData * * @magentoDbIsolation disabled + * + * @param string $sku + * @param int $stockId + * @param bool $expected + * @return void + * + * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function testSourceItemsAreOutOfStock() + public function testSourceItemsAreOutOfStock(string $sku, int $stockId, bool $expected): void { - $product = $this->productRepository->get('SKU-1'); + $product = $this->productRepository->get($sku); $stockItemSearchCriteria = $this->stockItemCriteriaFactory->create(); $stockItemSearchCriteria->setProductsFilter($product->getId()); $stockItemsCollection = $this->stockItemRepository->getList($stockItemSearchCriteria); @@ -69,6 +78,24 @@ public function testSourceItemsAreOutOfStock() $legacyStockItem->setBackorders(1); $legacyStockItem->setUseConfigBackorders(0); $this->stockItemRepository->save($legacyStockItem); - $this->assertFalse($this->isProductSalable->execute('SKU-1', 10)); + $this->assertEquals($expected, $this->isProductSalable->execute($sku, $stockId)); + } + + /** + * @return array + */ + public function sourceItemsStockData(): array + { + return [ + ['SKU-1', 10, true], + ['SKU-1', 20, false], + ['SKU-1', 30, true], + ['SKU-2', 10, false], + ['SKU-2', 20, true], + ['SKU-2', 30, true], + ['SKU-3', 10, false], + ['SKU-3', 20, false], + ['SKU-3', 30, false], + ]; } } diff --git a/InventorySales/Test/Integration/IsProductSalable/ManageStockConditionTest.php b/InventorySales/Test/Integration/IsProductSalable/ManageStockConditionTest.php index a6dcb6ec2bf6..6def12f4936e 100644 --- a/InventorySales/Test/Integration/IsProductSalable/ManageStockConditionTest.php +++ b/InventorySales/Test/Integration/IsProductSalable/ManageStockConditionTest.php @@ -64,9 +64,9 @@ public function executeWithManageStockFalseDataProvider(): array ['SKU-2', 10, false], ['SKU-2', 20, true], ['SKU-2', 30, true], - ['SKU-3', 10, true], + ['SKU-3', 10, false], ['SKU-3', 20, false], - ['SKU-3', 30, true], + ['SKU-3', 30, false], ]; } } diff --git a/InventorySales/Test/Integration/IsProductSalableForRequestedQty/IsAnySourceItemInStockConditionTest.php b/InventorySales/Test/Integration/IsProductSalableForRequestedQty/IsAnySourceItemInStockConditionTest.php index d845463153f4..5e1729359911 100644 --- a/InventorySales/Test/Integration/IsProductSalableForRequestedQty/IsAnySourceItemInStockConditionTest.php +++ b/InventorySales/Test/Integration/IsProductSalableForRequestedQty/IsAnySourceItemInStockConditionTest.php @@ -53,13 +53,23 @@ protected function setUp() * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stocks.php * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stock_source_links.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items_out_of_stock.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php + * + * @dataProvider sourceItemsStockData * * @magentoDbIsolation disabled + * + * @param string $sku + * @param int $stockId + * @param bool $expected + * @return void + * + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function testSourceItemsAreOutOfStock() + public function testSourceItemsAreOutOfStock(string $sku, int $stockId, bool $expected): void { - $product = $this->productRepository->get('SKU-1'); + $product = $this->productRepository->get($sku); $stockItemSearchCriteria = $this->stockItemCriteriaFactory->create(); $stockItemSearchCriteria->setProductsFilter($product->getId()); $stockItemsCollection = $this->stockItemRepository->getList($stockItemSearchCriteria); @@ -69,6 +79,24 @@ public function testSourceItemsAreOutOfStock() $legacyStockItem->setBackorders(1); $legacyStockItem->setUseConfigBackorders(0); $this->stockItemRepository->save($legacyStockItem); - $this->assertFalse($this->isProductSalable->execute('SKU-1', 10, 1)->isSalable()); + $this->assertEquals($expected, $this->isProductSalable->execute($sku, $stockId, 1)->isSalable()); + } + + /** + * @return array + */ + public function sourceItemsStockData(): array + { + return [ + ['SKU-1', 10, true], + ['SKU-1', 20, false], + ['SKU-1', 30, true], + ['SKU-2', 10, false], + ['SKU-2', 20, true], + ['SKU-2', 30, true], + ['SKU-3', 10, false], + ['SKU-3', 20, false], + ['SKU-3', 30, false], + ]; } } diff --git a/InventorySales/Test/Integration/IsProductSalableForRequestedQty/ManageStockConditionTest.php b/InventorySales/Test/Integration/IsProductSalableForRequestedQty/ManageStockConditionTest.php index bb366064e28c..b7562d580173 100644 --- a/InventorySales/Test/Integration/IsProductSalableForRequestedQty/ManageStockConditionTest.php +++ b/InventorySales/Test/Integration/IsProductSalableForRequestedQty/ManageStockConditionTest.php @@ -65,9 +65,9 @@ public function executeWithManageStockFalseDataProvider(): array ['SKU-2', 10, 1, false], ['SKU-2', 20, 1, true], ['SKU-2', 30, 1, true], - ['SKU-3', 10, 1, true], + ['SKU-3', 10, 1, false], ['SKU-3', 20, 1, false], - ['SKU-3', 30, 1, true], + ['SKU-3', 30, 1, false], ]; } } From 6f1312fa9541f9bd2c8d1253d302036ea09f6c01 Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Wed, 24 Apr 2019 21:05:10 +0300 Subject: [PATCH 199/231] MSI-2062: Replace stock index dump data export responce with array of objects instead of array --- .../Model/ExportStockIndexData.php | 22 +++++- .../Model/ProductStockIndexData.php | 67 +++++++++++++++++++ .../Model/ProductStockIndexDataMapper.php | 51 ++++++++++++++ InventoryExportStock/etc/di.xml | 2 + .../Data/ProductStockIndexDataInterface.php | 64 ++++++++++++++++++ .../Api/ExportStockIndexDataInterface.php | 2 +- 6 files changed, 204 insertions(+), 4 deletions(-) create mode 100644 InventoryExportStock/Model/ProductStockIndexData.php create mode 100644 InventoryExportStock/Model/ProductStockIndexDataMapper.php create mode 100644 InventoryExportStockApi/Api/Data/ProductStockIndexDataInterface.php diff --git a/InventoryExportStock/Model/ExportStockIndexData.php b/InventoryExportStock/Model/ExportStockIndexData.php index b861c4062590..470eb6ab352b 100644 --- a/InventoryExportStock/Model/ExportStockIndexData.php +++ b/InventoryExportStock/Model/ExportStockIndexData.php @@ -9,6 +9,8 @@ use Magento\Framework\Exception\LocalizedException; use Magento\InventoryExportStock\Model\ResourceModel\StockIndexDumpProcessor; +use Magento\InventoryExportStockApi\Api\Data\ProductStockIndexDataInterface; +use Magento\InventoryExportStockApi\Api\Data\ProductStockIndexDataInterfaceFactory; use Magento\InventoryExportStockApi\Api\ExportStockIndexDataInterface; use Magento\InventorySales\Model\ResourceModel\GetWebsiteIdByWebsiteCode; use Magento\InventorySalesApi\Api\Data\SalesChannelInterface; @@ -28,33 +30,42 @@ class ExportStockIndexData implements ExportStockIndexDataInterface * @var GetWebsiteIdByWebsiteCode */ private $getWebsiteIdByWebsiteCode; + /** * @var StockResolverInterface */ private $stockResolver; + /** + * @var ProductStockIndexDataMapper + */ + private $productStockIndexDataMapper; + /** * ExportStockIndexData constructor * * @param StockIndexDumpProcessor $stockIndexDumpProcessor * @param GetWebsiteIdByWebsiteCode $getWebsiteIdByWebsiteCode * @param StockResolverInterface $stockResolver + * @param ProductStockIndexDataMapper $productStockIndexDataMapper */ public function __construct( StockIndexDumpProcessor $stockIndexDumpProcessor, GetWebsiteIdByWebsiteCode $getWebsiteIdByWebsiteCode, - StockResolverInterface $stockResolver + StockResolverInterface $stockResolver, + ProductStockIndexDataMapper $productStockIndexDataMapper ) { $this->stockIndexDumpProcessor = $stockIndexDumpProcessor; $this->getWebsiteIdByWebsiteCode = $getWebsiteIdByWebsiteCode; $this->stockResolver = $stockResolver; + $this->productStockIndexDataMapper = $productStockIndexDataMapper; } /** * Provides stock index export from inventory_stock_% table * * @param string $websiteCode - * @return array + * @return ProductStockIndexDataInterface[] * @throws LocalizedException */ public function execute(string $websiteCode): array @@ -62,7 +73,12 @@ public function execute(string $websiteCode): array $websiteId = $this->getWebsiteIdByWebsiteCode->execute($websiteCode); $stockId = $this->stockResolver ->execute(SalesChannelInterface::TYPE_WEBSITE, $websiteCode)->getStockId(); + $items = $this->stockIndexDumpProcessor->execute($websiteId, $stockId); + $productsData = []; + foreach ($items as $item) { + $productsData[] = $this->productStockIndexDataMapper->execute($item); + } - return $this->stockIndexDumpProcessor->execute($websiteId, $stockId); + return $productsData; } } diff --git a/InventoryExportStock/Model/ProductStockIndexData.php b/InventoryExportStock/Model/ProductStockIndexData.php new file mode 100644 index 000000000000..7d0188fab602 --- /dev/null +++ b/InventoryExportStock/Model/ProductStockIndexData.php @@ -0,0 +1,67 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryExportStock\Model; + +use Magento\Framework\Model\AbstractExtensibleModel; +use Magento\InventoryExportStockApi\Api\Data\ProductStockIndexDataInterface; + +/** + * Class ProductStockIndexData + */ +class ProductStockIndexData extends AbstractExtensibleModel implements ProductStockIndexDataInterface +{ + /** + * @inheritDoc + */ + public function getSku(): string + { + return $this->getData(self::SKU); + } + + /** + * @inheritDoc + */ + public function getQty(): ?float + { + return $this->getData(self::QTY); + } + + /** + * Provides product is salable flag + * + * @return int + */ + public function getIsSalable(): bool + { + return $this->getData(self::IS_SALABLE); + } + + /** + * @inheritDoc + */ + public function setSku(string $sku): void + { + $this->setData(self::SKU, $sku); + } + + /** + * @inheritDoc + */ + public function setQty(?float $qty): void + { + $this->setData(self::QTY, $qty); + } + + /** + * @inheritDoc + */ + public function setIsSalable(bool $isSalable): void + { + $this->setData(self::IS_SALABLE, $isSalable); + } +} diff --git a/InventoryExportStock/Model/ProductStockIndexDataMapper.php b/InventoryExportStock/Model/ProductStockIndexDataMapper.php new file mode 100644 index 000000000000..416d4701c1cd --- /dev/null +++ b/InventoryExportStock/Model/ProductStockIndexDataMapper.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryExportStock\Model; + +use Magento\InventoryExportStockApi\Api\Data\ProductStockIndexDataInterface; + +/** + * Class ProductStockIndexDataMapper + */ +class ProductStockIndexDataMapper +{ + /** + * @var ProductStockIndexDataFactory + */ + private $productStockIndexDataFactory; + + /** + * @param ProductStockIndexDataFactory $productStockIndexDataFactory + */ + public function __construct( + ProductStockIndexDataFactory $productStockIndexDataFactory + ) { + $this->productStockIndexDataFactory = $productStockIndexDataFactory; + } + + /** + * Creates ProductStockIndexData object and set values inside of it + * + * @param array $item + * @return ProductStockIndexDataInterface + */ + public function execute(array $item): ProductStockIndexDataInterface + { + /** @var ProductStockIndexDataInterface $productStockDataObject */ + $productStockDataObject = $this->productStockIndexDataFactory->create(); + $productStockDataObject->setSku($item[ProductStockIndexDataInterface::SKU]); + $productStockDataObject->setIsSalable((bool)$item[ProductStockIndexDataInterface::IS_SALABLE]); + $qty = $item[ProductStockIndexDataInterface::QTY]; + if ($qty !== null) { + $qty = (float)$qty; + } + $productStockDataObject->setQty($qty); + + return $productStockDataObject; + } +} diff --git a/InventoryExportStock/etc/di.xml b/InventoryExportStock/etc/di.xml index 6b2184189352..0918b420394b 100644 --- a/InventoryExportStock/etc/di.xml +++ b/InventoryExportStock/etc/di.xml @@ -13,6 +13,8 @@ type="Magento\InventoryExportStock\Model\ExportStockSalableQty"/> <preference for="Magento\InventoryExportStockApi\Api\ExportStockIndexDataInterface" type="Magento\InventoryExportStock\Model\ExportStockIndexData"/> + <preference for="Magento\InventoryExportStockApi\Api\Data\ProductStockIndexDataInterface" + type="Magento\InventoryExportStock\Model\ProductStockIndexData"/> <type name="Magento\InventoryExportStock\Model\GetQtyForNotManageStock"> <arguments> <argument name="qtyForNotManageStock" xsi:type="null"/> diff --git a/InventoryExportStockApi/Api/Data/ProductStockIndexDataInterface.php b/InventoryExportStockApi/Api/Data/ProductStockIndexDataInterface.php new file mode 100644 index 000000000000..5afb13fc6a3e --- /dev/null +++ b/InventoryExportStockApi/Api/Data/ProductStockIndexDataInterface.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryExportStockApi\Api\Data; + +/** + * Class ExportStockIndexDataResultInterface for result Inventory stock index dump export + */ +interface ProductStockIndexDataInterface +{ + public const QTY = 'qty'; + public const IS_SALABLE = 'is_salable'; + public const SKU = 'sku'; + + /** + * Provides product SKU + * + * @return string + */ + public function getSku(): string; + + /** + * Provides product QTY + * + * @return float|null + */ + public function getQty(): ?float; + + /** + * Provides product is salable flag + * + * @return bool + */ + public function getIsSalable(): bool; + + /** + * Sets SKU + * + * @param string $sku + * @return void + */ + public function setSku(string $sku): void; + + /** + * Sets QTY + * + * @param float|null $qty + * @return void + */ + public function setQty(?float $qty): void; + + /** + * Sets is salable flag + * + * @param bool $isSalable + * @return void + */ + public function setIsSalable(bool $isSalable): void; + +} diff --git a/InventoryExportStockApi/Api/ExportStockIndexDataInterface.php b/InventoryExportStockApi/Api/ExportStockIndexDataInterface.php index 9cbd976f9edc..0c15ad204d29 100644 --- a/InventoryExportStockApi/Api/ExportStockIndexDataInterface.php +++ b/InventoryExportStockApi/Api/ExportStockIndexDataInterface.php @@ -17,7 +17,7 @@ interface ExportStockIndexDataInterface * Provides stock index export from inventory_stock_% table * * @param string $websiteCode - * @return string[] + * @return \Magento\InventoryExportStockApi\Api\Data\ProductStockIndexDataInterface[] */ public function execute(string $websiteCode): array; } From 049c402c07f52a0f768cec3958eeff4494ee39ff Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Wed, 24 Apr 2019 21:26:11 +0300 Subject: [PATCH 200/231] MSI-2062: Replace qty from ?float to float only in responce --- .../Model/PreciseExportStockProcessor.php | 6 ++---- InventoryExportStock/Model/ProductStockIndexData.php | 8 +++----- .../Model/ProductStockIndexDataMapper.php | 6 +----- .../Api/Data/ProductStockIndexDataInterface.php | 8 ++++---- 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/InventoryExportStock/Model/PreciseExportStockProcessor.php b/InventoryExportStock/Model/PreciseExportStockProcessor.php index f88b3fb183db..b09ce38562c0 100644 --- a/InventoryExportStock/Model/PreciseExportStockProcessor.php +++ b/InventoryExportStock/Model/PreciseExportStockProcessor.php @@ -8,7 +8,6 @@ namespace Magento\InventoryExportStock\Model; use Magento\Catalog\Api\Data\ProductInterface; -use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\LocalizedException; use Magento\InventoryApi\Model\IsProductAssignedToStockInterface; use Magento\InventoryConfigurationApi\Exception\SkuIsNotAssignedToStockException; @@ -81,7 +80,6 @@ public function __construct( * @param array $products * @param int $stockId * @return array - * @throws InputException * @throws LocalizedException */ public function execute(array $products, int $stockId): array @@ -131,14 +129,14 @@ private function getItem(string $sku, int $stockId): array if (!$this->isSourceItemManagementAllowedForSku->execute($sku)) { return [ 'sku' => $sku, - 'qty' => null, + 'qty' => 0.0000, 'is_salable' => $this->isProductSalable->execute($sku, $stockId) ]; } if (!$this->getStockItemConfiguration->execute($sku)->isManageStock()) { return [ 'sku' => $sku, - 'qty' => $this->getQtyForNotManageStock->execute(), + 'qty' => (float)$this->getQtyForNotManageStock->execute(), 'is_salable' => true ]; } diff --git a/InventoryExportStock/Model/ProductStockIndexData.php b/InventoryExportStock/Model/ProductStockIndexData.php index 7d0188fab602..28905b3aab88 100644 --- a/InventoryExportStock/Model/ProductStockIndexData.php +++ b/InventoryExportStock/Model/ProductStockIndexData.php @@ -26,15 +26,13 @@ public function getSku(): string /** * @inheritDoc */ - public function getQty(): ?float + public function getQty(): float { return $this->getData(self::QTY); } /** - * Provides product is salable flag - * - * @return int + * @inheritDoc */ public function getIsSalable(): bool { @@ -52,7 +50,7 @@ public function setSku(string $sku): void /** * @inheritDoc */ - public function setQty(?float $qty): void + public function setQty(float $qty): void { $this->setData(self::QTY, $qty); } diff --git a/InventoryExportStock/Model/ProductStockIndexDataMapper.php b/InventoryExportStock/Model/ProductStockIndexDataMapper.php index 416d4701c1cd..31a179c81e16 100644 --- a/InventoryExportStock/Model/ProductStockIndexDataMapper.php +++ b/InventoryExportStock/Model/ProductStockIndexDataMapper.php @@ -40,11 +40,7 @@ public function execute(array $item): ProductStockIndexDataInterface $productStockDataObject = $this->productStockIndexDataFactory->create(); $productStockDataObject->setSku($item[ProductStockIndexDataInterface::SKU]); $productStockDataObject->setIsSalable((bool)$item[ProductStockIndexDataInterface::IS_SALABLE]); - $qty = $item[ProductStockIndexDataInterface::QTY]; - if ($qty !== null) { - $qty = (float)$qty; - } - $productStockDataObject->setQty($qty); + $productStockDataObject->setQty((float)$item[ProductStockIndexDataInterface::QTY]); return $productStockDataObject; } diff --git a/InventoryExportStockApi/Api/Data/ProductStockIndexDataInterface.php b/InventoryExportStockApi/Api/Data/ProductStockIndexDataInterface.php index 5afb13fc6a3e..4ab2a7fc07e4 100644 --- a/InventoryExportStockApi/Api/Data/ProductStockIndexDataInterface.php +++ b/InventoryExportStockApi/Api/Data/ProductStockIndexDataInterface.php @@ -26,9 +26,9 @@ public function getSku(): string; /** * Provides product QTY * - * @return float|null + * @return float */ - public function getQty(): ?float; + public function getQty(): float; /** * Provides product is salable flag @@ -48,10 +48,10 @@ public function setSku(string $sku): void; /** * Sets QTY * - * @param float|null $qty + * @param float $qty * @return void */ - public function setQty(?float $qty): void; + public function setQty(float $qty): void; /** * Sets is salable flag From d8f133008b3d12bae34181d00260f43f784424d9 Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Thu, 25 Apr 2019 15:07:18 +0300 Subject: [PATCH 201/231] MSI-2062: Fix static tests --- InventoryExportStock/Model/ProductStockIndexData.php | 4 ++-- InventoryExportStock/composer.json | 4 +++- InventoryExportStock/etc/module.xml | 2 ++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/InventoryExportStock/Model/ProductStockIndexData.php b/InventoryExportStock/Model/ProductStockIndexData.php index 28905b3aab88..bf80c8de57db 100644 --- a/InventoryExportStock/Model/ProductStockIndexData.php +++ b/InventoryExportStock/Model/ProductStockIndexData.php @@ -7,13 +7,13 @@ namespace Magento\InventoryExportStock\Model; -use Magento\Framework\Model\AbstractExtensibleModel; +use Magento\Framework\Model\AbstractModel; use Magento\InventoryExportStockApi\Api\Data\ProductStockIndexDataInterface; /** * Class ProductStockIndexData */ -class ProductStockIndexData extends AbstractExtensibleModel implements ProductStockIndexDataInterface +class ProductStockIndexData extends AbstractModel implements ProductStockIndexDataInterface { /** * @inheritDoc diff --git a/InventoryExportStock/composer.json b/InventoryExportStock/composer.json index fe764fcaff12..aa84aa982636 100644 --- a/InventoryExportStock/composer.json +++ b/InventoryExportStock/composer.json @@ -12,7 +12,9 @@ "magento/module-catalog-inventory": "*", "magento/module-inventory-indexer": "*", "magento/module-inventory-configuration": "*", - "magento/module-inventory-configuration-api": "*" + "magento/module-inventory-configuration-api": "*", + "magento/module-inventory-configurable-product": "*", + "magento/module-inventory-grouped-product": "*" }, "type": "magento2-module", "license": [ diff --git a/InventoryExportStock/etc/module.xml b/InventoryExportStock/etc/module.xml index 5c45f9e05173..700fcde6765a 100644 --- a/InventoryExportStock/etc/module.xml +++ b/InventoryExportStock/etc/module.xml @@ -10,6 +10,8 @@ <module name="Magento_InventoryExportStock" setup_version="1.0.0"> <sequence> <module name="Magento_Catalog"/> + <module name="Magento_ConfigurableProduct"/> + <module name="Magento_GroupedProduct"/> <module name="Magento_InventoryApi"/> <module name="Magento_InventoryIndexer"/> <module name="Magento_InventoryCatalogApi"/> From cc27e911457abfb7ef254fda5eba836032f4e38e Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Thu, 25 Apr 2019 15:36:36 +0300 Subject: [PATCH 202/231] MSI-2062: fix applying notManageStockCondition di config value as default value for complex products --- .../Model/ResourceModel/StockIndexDumpProcessor.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php b/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php index 10df4f44bf87..9b3779a8e4c0 100644 --- a/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php +++ b/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php @@ -186,9 +186,18 @@ private function getStockItemSelect(int $websiteId): Select if ($getQtyForNotManageStock === null) { $getQtyForNotManageStock = 'NULL'; } + $ifExpression = ' + IF( + `product_entity`.`type_id` IN ( + \'' . Configurable::TYPE_CODE . '\', + \'' . Type::TYPE_BUNDLE . '\', + \'' . Grouped::TYPE_CODE . '\'), + NULL, + ' . $getQtyForNotManageStock . ' + )'; $select->from( ['legacy_stock_item' => $legacyStockItemTable], - [new Zend_Db_Expr($getQtyForNotManageStock . ' as qty'), + ['qty' =>new Zend_Db_Expr($ifExpression), new Zend_Db_Expr('"1" as is_salable')] )->join( ['product_entity' => $productEntityTable], From 1ce7f60327483eb5dbdcfc47eccaf735c6497f29 Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Thu, 25 Apr 2019 15:40:03 +0300 Subject: [PATCH 203/231] MSI-2062: get rid of use declaration for Zend_Db_Expr --- .../Model/ResourceModel/StockIndexDumpProcessor.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php b/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php index 9b3779a8e4c0..055c9f1afa6c 100644 --- a/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php +++ b/InventoryExportStock/Model/ResourceModel/StockIndexDumpProcessor.php @@ -18,7 +18,6 @@ use Magento\InventoryIndexer\Model\StockIndexTableNameResolverInterface; use Magento\InventorySales\Model\ResourceModel\IsStockItemSalableCondition\ManageStockCondition as NotManageStockCondition; use Psr\Log\LoggerInterface; -use Zend_Db_Expr; /** * Class GetStockIndexDump provides sku and qty of products dumping them from stock index table @@ -197,8 +196,8 @@ private function getStockItemSelect(int $websiteId): Select )'; $select->from( ['legacy_stock_item' => $legacyStockItemTable], - ['qty' =>new Zend_Db_Expr($ifExpression), - new Zend_Db_Expr('"1" as is_salable')] + ['qty' =>new \Zend_Db_Expr($ifExpression), + new \Zend_Db_Expr('"1" as is_salable')] )->join( ['product_entity' => $productEntityTable], 'legacy_stock_item.product_id = product_entity.entity_id', From 0cd06c437d086db80a041c68731aa5f33466fd0c Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Thu, 25 Apr 2019 17:25:29 +0300 Subject: [PATCH 204/231] MSI-2062: fis typo in declarad dependecies --- InventoryExportStock/composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/InventoryExportStock/composer.json b/InventoryExportStock/composer.json index aa84aa982636..8e69e99aa7ca 100644 --- a/InventoryExportStock/composer.json +++ b/InventoryExportStock/composer.json @@ -13,8 +13,8 @@ "magento/module-inventory-indexer": "*", "magento/module-inventory-configuration": "*", "magento/module-inventory-configuration-api": "*", - "magento/module-inventory-configurable-product": "*", - "magento/module-inventory-grouped-product": "*" + "magento/module-configurable-product": "*", + "magento/module-grouped-product": "*" }, "type": "magento2-module", "license": [ From 15746702e9e41ea4c8ae26ff790186827a18524a Mon Sep 17 00:00:00 2001 From: Sergey Mutaf <seruymt@gmail.com> Date: Thu, 25 Apr 2019 20:05:38 +0300 Subject: [PATCH 205/231] MSI-1947:Source items deduction during Credit memo creation for unshipped order items - fixes for failed api-functional test --- .../DeductSourceItemQuantityOnRefundObserver.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/InventorySales/Observer/SalesInventory/DeductSourceItemQuantityOnRefundObserver.php b/InventorySales/Observer/SalesInventory/DeductSourceItemQuantityOnRefundObserver.php index 7cbec55770b1..f080682019b7 100644 --- a/InventorySales/Observer/SalesInventory/DeductSourceItemQuantityOnRefundObserver.php +++ b/InventorySales/Observer/SalesInventory/DeductSourceItemQuantityOnRefundObserver.php @@ -16,6 +16,7 @@ use Magento\InventorySalesApi\Model\ReturnProcessor\Request\ItemsToRefundInterfaceFactory; use Magento\Sales\Api\Data\CreditmemoItemInterface as CreditmemoItem; use Magento\Sales\Api\Data\OrderItemInterface; +use Magento\Sales\Api\OrderRepositoryInterface; class DeductSourceItemQuantityOnRefundObserver implements ObserverInterface { @@ -44,25 +45,33 @@ class DeductSourceItemQuantityOnRefundObserver implements ObserverInterface */ private $deductSourceItemQuantityOnRefund; + /** + * @var OrderRepositoryInterface + */ + private $orderRepository; + /** * @param GetSkuFromOrderItemInterface $getSkuFromOrderItem * @param ItemsToRefundInterfaceFactory $itemsToRefundFactory * @param IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemManagementAllowedForProductType * @param GetProductTypesBySkusInterface $getProductTypesBySkus * @param DeductSourceItemQuantityOnRefund $deductSourceItemQuantityOnRefund + * @param OrderRepositoryInterface $orderRepository */ public function __construct( GetSkuFromOrderItemInterface $getSkuFromOrderItem, ItemsToRefundInterfaceFactory $itemsToRefundFactory, IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemManagementAllowedForProductType, GetProductTypesBySkusInterface $getProductTypesBySkus, - DeductSourceItemQuantityOnRefund $deductSourceItemQuantityOnRefund + DeductSourceItemQuantityOnRefund $deductSourceItemQuantityOnRefund, + OrderRepositoryInterface $orderRepository ) { $this->getSkuFromOrderItem = $getSkuFromOrderItem; $this->itemsToRefundFactory = $itemsToRefundFactory; $this->isSourceItemManagementAllowedForProductType = $isSourceItemManagementAllowedForProductType; $this->getProductTypesBySkus = $getProductTypesBySkus; $this->deductSourceItemQuantityOnRefund = $deductSourceItemQuantityOnRefund; + $this->orderRepository = $orderRepository; } /** @@ -73,7 +82,7 @@ public function execute(Observer $observer) { /* @var $creditmemo \Magento\Sales\Model\Order\Creditmemo */ $creditmemo = $observer->getEvent()->getCreditmemo(); - $order = $creditmemo->getOrder(); + $order = $this->orderRepository->get($creditmemo->getOrderId()); $itemsToRefund = $refundedOrderItemIds = []; /** @var CreditmemoItem $item */ foreach ($creditmemo->getItems() as $item) { From c5005d1c5301a736103ac96c60566d3fbbc2719d Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Thu, 25 Apr 2019 12:30:22 -0500 Subject: [PATCH 206/231] Fixes various kinds of static tests --- .../Api/BulkPartialInventoryTransferInterface.php | 7 ++++++- .../Api/Data/PartialInventoryTransferItemInterface.php | 4 +++- .../Model/PartialInventoryTransferValidatorInterface.php | 7 ++++++- InventoryReservationCli/etc/di.xml | 2 +- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/InventoryCatalogApi/Api/BulkPartialInventoryTransferInterface.php b/InventoryCatalogApi/Api/BulkPartialInventoryTransferInterface.php index 2226e95c64d7..ad484ebcdc30 100644 --- a/InventoryCatalogApi/Api/BulkPartialInventoryTransferInterface.php +++ b/InventoryCatalogApi/Api/BulkPartialInventoryTransferInterface.php @@ -7,6 +7,11 @@ namespace Magento\InventoryCatalogApi\Api; +/** + * Transfer Inventory between sources. Moves specified items from origin source to destination source. + * + * @api + */ interface BulkPartialInventoryTransferInterface { /** @@ -19,4 +24,4 @@ interface BulkPartialInventoryTransferInterface * @throws \Magento\Framework\Validation\ValidationException */ public function execute(string $originSourceCode, string $destinationSourceCode, array $items): void; -} \ No newline at end of file +} diff --git a/InventoryCatalogApi/Api/Data/PartialInventoryTransferItemInterface.php b/InventoryCatalogApi/Api/Data/PartialInventoryTransferItemInterface.php index 8856765aba83..eae4c5eb374a 100644 --- a/InventoryCatalogApi/Api/Data/PartialInventoryTransferItemInterface.php +++ b/InventoryCatalogApi/Api/Data/PartialInventoryTransferItemInterface.php @@ -8,6 +8,8 @@ namespace Magento\InventoryCatalogApi\Api\Data; /** + * Specifies item and quantity for partial inventory transfer. + * * @api */ interface PartialInventoryTransferItemInterface @@ -34,4 +36,4 @@ public function getQty(): float; * @param float $qty */ public function setQty(float $qty): void; -} \ No newline at end of file +} diff --git a/InventoryCatalogApi/Model/PartialInventoryTransferValidatorInterface.php b/InventoryCatalogApi/Model/PartialInventoryTransferValidatorInterface.php index 6b75d96885b0..7746e0a2c77b 100644 --- a/InventoryCatalogApi/Model/PartialInventoryTransferValidatorInterface.php +++ b/InventoryCatalogApi/Model/PartialInventoryTransferValidatorInterface.php @@ -10,6 +10,11 @@ use Magento\Framework\Validation\ValidationResult; use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferItemInterface; +/** + * Validator for Partial Inventory transfer API. + * + * @api + */ interface PartialInventoryTransferValidatorInterface { /** @@ -21,4 +26,4 @@ interface PartialInventoryTransferValidatorInterface * @return ValidationResult */ public function validate(string $originSourceCode, string $destinationSourceCode, array $items): ValidationResult; -} \ No newline at end of file +} diff --git a/InventoryReservationCli/etc/di.xml b/InventoryReservationCli/etc/di.xml index 226eee817e65..5094270705ac 100644 --- a/InventoryReservationCli/etc/di.xml +++ b/InventoryReservationCli/etc/di.xml @@ -6,7 +6,7 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> - <type name="Magento\Framework\Console\CommandList"> + <type name="Magento\Framework\Console\CommandListInterface"> <arguments> <argument name="commands" xsi:type="array"> <item name="inventory_salable_quantity_inconsistency" xsi:type="object"> From 5deeb2248e0caf1a3d3cf02e17408a8959a8bceb Mon Sep 17 00:00:00 2001 From: Maksym Novik <novik.kor@gmail.com> Date: Thu, 25 Apr 2019 18:00:52 +0300 Subject: [PATCH 207/231] Product status is 'In Stock' on storefront even it is Out of Stock in all Sources #2156. Fixed integration tests --- .../Order/PlaceOrderOnNotDefaultStockTest.php | 9 +++--- .../set_product_configurable_zero_qty.php | 29 +++++++++++++++++++ InventorySales/etc/di.xml | 4 +-- 3 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 InventoryConfigurableProduct/Test/_files/set_product_configurable_zero_qty.php diff --git a/InventoryConfigurableProduct/Test/Integration/Order/PlaceOrderOnNotDefaultStockTest.php b/InventoryConfigurableProduct/Test/Integration/Order/PlaceOrderOnNotDefaultStockTest.php index ed8f56af0b5c..79ae12a5d836 100644 --- a/InventoryConfigurableProduct/Test/Integration/Order/PlaceOrderOnNotDefaultStockTest.php +++ b/InventoryConfigurableProduct/Test/Integration/Order/PlaceOrderOnNotDefaultStockTest.php @@ -170,14 +170,14 @@ public function testPlaceOrderWithOutOffStockProduct() * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stocks.php * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stock_source_links.php * @magentoDataFixture ../../../../app/code/Magento/InventoryConfigurableProduct/Test/_files/source_items_configurable.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryConfigurableProduct/Test/_files/set_product_configurable_out_of_stock.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryConfigurableProduct/Test/_files/set_product_configurable_zero_qty.php * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/stock_website_sales_channels.php * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/quote.php * @magentoConfigFixture store_for_us_website_store cataloginventory/item_options/backorders 1 * * @magentoDbIsolation disabled */ - public function testPlaceOrderWithOutOffStockProductAndBackOrdersTurnedOn() + public function testPlaceOrderWithZeroStockProductAndBackOrdersTurnedOn() { $sku = 'configurable'; $qty = 8; @@ -202,14 +202,15 @@ public function testPlaceOrderWithOutOffStockProductAndBackOrdersTurnedOn() * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stocks.php * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stock_source_links.php * @magentoDataFixture ../../../../app/code/Magento/InventoryConfigurableProduct/Test/_files/source_items_configurable.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryConfigurableProduct/Test/_files/set_product_configurable_out_of_stock.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryConfigurableProduct/Test/_files/set_product_configurable_zero_qty.php * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/stock_website_sales_channels.php * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/quote.php * @magentoConfigFixture current_store cataloginventory/item_options/manage_stock 0 + * @magentoConfigFixture default_store cataloginventory/item_options/manage_stock 0 * * @magentoDbIsolation disabled */ - public function testPlaceOrderWithOutOffStockProductAndManageStockTurnedOff() + public function testPlaceOrderWithZeroStockProductAndManageStockTurnedOff() { $sku = 'configurable'; $qty = 6; diff --git a/InventoryConfigurableProduct/Test/_files/set_product_configurable_zero_qty.php b/InventoryConfigurableProduct/Test/_files/set_product_configurable_zero_qty.php new file mode 100644 index 000000000000..aebbf312070c --- /dev/null +++ b/InventoryConfigurableProduct/Test/_files/set_product_configurable_zero_qty.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Inventory\Model\SourceItem\Command\SourceItemsSave; +use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryApi\Api\SourceItemRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var SourceItemRepositoryInterface $sourceItemRepository */ +$sourceItemRepository = Bootstrap::getObjectManager()->create(SourceItemRepositoryInterface::class); + +$searchCriteriaBuilder = Bootstrap::getObjectManager()->create(SearchCriteriaBuilder::class); +$searchCriteria = $searchCriteriaBuilder + ->addFilter(SourceItemInterface::SKU, 'simple_10') + ->addFilter(SourceItemInterface::SOURCE_CODE, 'us-1') + ->create(); + +$sourceItems = $sourceItemRepository->getList($searchCriteria)->getItems(); +$sourceItem = reset($sourceItems); +$sourceItem->setStatus(1); + +/** @var SourceItemsSave $sourceItemSave */ +$sourceItemSave = Bootstrap::getObjectManager()->create(SourceItemsSave::class); +$sourceItemSave->execute([$sourceItem]); diff --git a/InventorySales/etc/di.xml b/InventorySales/etc/di.xml index e51606e18f1c..8495bde0fbe6 100644 --- a/InventorySales/etc/di.xml +++ b/InventorySales/etc/di.xml @@ -59,7 +59,7 @@ <item name="required" xsi:type="boolean">true</item> <item name="object" xsi:type="object">Magento\InventorySales\Model\IsProductSalableCondition\IsSetInStockStatusForCompositeProductCondition</item> </item> - <item name="stock_item_is_any_source_in_stock" xsi:type="array"> + <item name="is_any_source_item_in_stock" xsi:type="array"> <item name="required" xsi:type="boolean">true</item> <item name="object" xsi:type="object">Magento\InventorySales\Model\IsProductSalableCondition\IsAnySourceItemInStockCondition</item> </item> @@ -88,7 +88,7 @@ <item name="required" xsi:type="boolean">true</item> <item name="object" xsi:type="object">Magento\InventorySales\Model\IsProductSalableForRequestedQtyCondition\IsCorrectQtyCondition</item> </item> - <item name="stock_item_is_any_source_in_stock" xsi:type="array"> + <item name="is_any_source_item_in_stock" xsi:type="array"> <item name="required" xsi:type="boolean">true</item> <item name="object" xsi:type="object">Magento\InventorySales\Model\IsProductSalableForRequestedQtyCondition\IsAnySourceItemInStockCondition</item> </item> From e4462f8c58f937d11168cd5a59581bf49cee95ef Mon Sep 17 00:00:00 2001 From: Eugene Shakhsuvarov <shakhsuv@adobe.com> Date: Thu, 25 Apr 2019 17:59:10 -0500 Subject: [PATCH 208/231] Strict type declaration added --- InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php b/InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php index b594bbbaa889..14fa3c743834 100644 --- a/InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php +++ b/InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\InventoryInStorePickupApi\Api\Data; use Magento\Framework\Api\ExtensibleDataInterface; From 7fa5cef6cbb09e6b0308c6586f7a39ca69f37b6e Mon Sep 17 00:00:00 2001 From: Eugene Shakhsuvarov <shakhsuv@adobe.com> Date: Thu, 25 Apr 2019 18:57:45 -0500 Subject: [PATCH 209/231] Add missing @api annotation --- .../Api/Data/ProductStockIndexDataInterface.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/InventoryExportStockApi/Api/Data/ProductStockIndexDataInterface.php b/InventoryExportStockApi/Api/Data/ProductStockIndexDataInterface.php index 4ab2a7fc07e4..feb898491e25 100644 --- a/InventoryExportStockApi/Api/Data/ProductStockIndexDataInterface.php +++ b/InventoryExportStockApi/Api/Data/ProductStockIndexDataInterface.php @@ -9,6 +9,8 @@ /** * Class ExportStockIndexDataResultInterface for result Inventory stock index dump export + * + * @api */ interface ProductStockIndexDataInterface { From 5e5e0c3229222423d132465d45b2d5bd7ae3b260 Mon Sep 17 00:00:00 2001 From: Maksym Novik <m.novik@ism-ukraine.com> Date: Fri, 26 Apr 2019 15:39:55 +0300 Subject: [PATCH 210/231] Product status is 'In Stock' on storefront even it is Out of Stock in all Sources #2156. Fixed case when trying to update source item of product which does not exist anymore --- ...SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InventoryLegacySynchronization/Plugin/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php b/InventoryLegacySynchronization/Plugin/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php index caedbf36ccdf..28622b01372d 100644 --- a/InventoryLegacySynchronization/Plugin/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php +++ b/InventoryLegacySynchronization/Plugin/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php @@ -77,7 +77,7 @@ public function afterExecute(SourceItemsDeleteInterface $subject, $result, array $sku = $sourceItem->getSku(); - $typeId = $this->getProductTypeBySku->execute([$sku])[$sku]; + $typeId = $this->getProductTypeBySku->execute([$sku])[$sku] ?? ''; if (false === $this->isSourceItemsAllowedForProductType->execute($typeId)) { continue; } From 652639794dc52921cd0f149ebeddb044f0cf60b4 Mon Sep 17 00:00:00 2001 From: "al.kravchuk" <al.kravchuk@ism-ukraine.com> Date: Sat, 27 Apr 2019 20:00:05 +0300 Subject: [PATCH 211/231] MSI-2185 Improve GetNearbySourcesByPostcode API. Changes to API provided: - Pickup Locations, instead of Sources - Match geo-position based on full address, not only postcode - Add possibility to use not only offline mode, but also Google Map API - Expose new web API - Improve tests coverage - Make a list of Pickup Locations reliable on stock id --- InventoryInStorePickup/Model/Address.php | 87 +++++++++ .../AddressToSourceSelectionAddress.php | 51 ++++++ .../Offline/GetNearbySourcesByPostcode.php | 107 ----------- .../Model/GetNearbyPickupLocations.php | 129 ++++++++++++++ .../Source/GetDistanceOrderedSourceCodes.php | 73 ++++++++ .../GetNearbySourcesByPostcodeTest.php | 71 -------- .../GetNearbyPickupLocationsOfflineTest.php | 167 ++++++++++++++++++ .../source_pickup_location_attributes.php | 38 ++++ InventoryInStorePickup/etc/di.xml | 9 +- .../Api/Data/AddressInterface.php | 44 +++++ .../Api/GetNearbyPickupLocationsInterface.php | 27 +++ .../GetNearbySourcesByPostcodeInterface.php | 31 ---- .../Model/GetNearbySourcesByPostcode.php | 70 -------- InventoryInStorePickupApi/etc/acl.xml | 20 +++ InventoryInStorePickupApi/etc/di.xml | 12 -- InventoryInStorePickupApi/etc/webapi.xml | 16 ++ 16 files changed, 654 insertions(+), 298 deletions(-) create mode 100644 InventoryInStorePickup/Model/Address.php create mode 100644 InventoryInStorePickup/Model/Convert/AddressToSourceSelectionAddress.php delete mode 100644 InventoryInStorePickup/Model/DistanceProvider/Offline/GetNearbySourcesByPostcode.php create mode 100644 InventoryInStorePickup/Model/GetNearbyPickupLocations.php create mode 100644 InventoryInStorePickup/Model/ResourceModel/Source/GetDistanceOrderedSourceCodes.php delete mode 100644 InventoryInStorePickup/Test/Integration/DistanceProvider/Offline/GetNearbySourcesByPostcodeTest.php create mode 100644 InventoryInStorePickup/Test/Integration/GetNearbyPickupLocationsOfflineTest.php create mode 100644 InventoryInStorePickup/Test/_files/source_pickup_location_attributes.php create mode 100644 InventoryInStorePickupApi/Api/Data/AddressInterface.php create mode 100644 InventoryInStorePickupApi/Api/GetNearbyPickupLocationsInterface.php delete mode 100644 InventoryInStorePickupApi/Api/GetNearbySourcesByPostcodeInterface.php delete mode 100644 InventoryInStorePickupApi/Model/GetNearbySourcesByPostcode.php create mode 100644 InventoryInStorePickupApi/etc/acl.xml delete mode 100644 InventoryInStorePickupApi/etc/di.xml create mode 100644 InventoryInStorePickupApi/etc/webapi.xml diff --git a/InventoryInStorePickup/Model/Address.php b/InventoryInStorePickup/Model/Address.php new file mode 100644 index 000000000000..74167b390cf5 --- /dev/null +++ b/InventoryInStorePickup/Model/Address.php @@ -0,0 +1,87 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickup\Model; + +use Magento\InventoryInStorePickupApi\Api\Data\AddressInterface; + +/** + * @inheritdoc + * @codeCoverageIgnore + */ +class Address implements AddressInterface +{ + /** + * @var string|null + */ + private $country; + + /** + * @var string|null + */ + private $postcode; + + /** + * @var string|null + */ + private $region; + + /** + * @var string|null + */ + private $city; + + /*** + * @param string|null $country + * @param string|null $postcode + * @param string|null $region + * @param string|null $city + */ + public function __construct( + ?string $country, + ?string $postcode, + ?string $region, + ?string $city + ) { + $this->country = $country; + $this->postcode = $postcode; + $this->region = $region; + $this->city = $city; + } + + /** + * @inheritdoc + */ + public function getCountry(): ?string + { + return $this->country; + } + + /** + * @inheritdoc + */ + public function getPostcode(): ?string + { + return $this->postcode; + } + + /** + * @inheritdoc + */ + public function getRegion(): ?string + { + return $this->region; + } + + /** + * @inheritdoc + */ + public function getCity(): ?string + { + return $this->city; + } +} diff --git a/InventoryInStorePickup/Model/Convert/AddressToSourceSelectionAddress.php b/InventoryInStorePickup/Model/Convert/AddressToSourceSelectionAddress.php new file mode 100644 index 000000000000..e4c3c6581a9b --- /dev/null +++ b/InventoryInStorePickup/Model/Convert/AddressToSourceSelectionAddress.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickup\Model\Convert; + +use Magento\InventoryInStorePickupApi\Api\Data\AddressInterface; +use Magento\InventorySourceSelectionApi\Api\Data\AddressInterface as SourceSelectionAddressInterface; +use Magento\InventorySourceSelectionApi\Api\Data\AddressInterfaceFactory; + +/** + * Create Source Selection Address based on Pickup Locations Address request. + */ +class AddressToSourceSelectionAddress +{ + /** + * @var \Magento\InventorySourceSelectionApi\Api\Data\AddressInterfaceFactory + */ + private $addressInterfaceFactory; + + /** + * AddressToSourceSelectionAddress constructor. + * + * @param \Magento\InventorySourceSelectionApi\Api\Data\AddressInterfaceFactory $addressInterfaceFactory + */ + public function __construct(AddressInterfaceFactory $addressInterfaceFactory) + { + $this->addressInterfaceFactory = $addressInterfaceFactory; + } + + /** + * @param \Magento\InventoryInStorePickupApi\Api\Data\AddressInterface $address + * + * @return \Magento\InventorySourceSelectionApi\Api\Data\AddressInterface + */ + public function execute(AddressInterface $address): SourceSelectionAddressInterface + { + $data = [ + 'country' => $address->getCountry() ?? '', + 'postcode' => $address->getPostcode() ?? '', + 'region' => $address->getRegion() ?? '', + 'city' => $address->getCity() ??'', + 'street' => '' + ]; + + return $this->addressInterfaceFactory->create($data); + } +} diff --git a/InventoryInStorePickup/Model/DistanceProvider/Offline/GetNearbySourcesByPostcode.php b/InventoryInStorePickup/Model/DistanceProvider/Offline/GetNearbySourcesByPostcode.php deleted file mode 100644 index 2708f627a46c..000000000000 --- a/InventoryInStorePickup/Model/DistanceProvider/Offline/GetNearbySourcesByPostcode.php +++ /dev/null @@ -1,107 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryInStorePickup\Model\DistanceProvider\Offline; - -use Magento\Framework\App\ResourceConnection; -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\InventoryApi\Api\Data\SourceInterface; -use Magento\InventoryApi\Api\Data\SourceInterfaceFactory; -use Magento\InventoryInStorePickupApi\Api\GetNearbySourcesByPostcodeInterface; - -/** - * Find nearest Inventory Sources by postal code using Haversine formula (Great Circle Distance) database query. - */ -class GetNearbySourcesByPostcode implements GetNearbySourcesByPostcodeInterface -{ - private const EARTH_RADIUS_KM = 6372.797; - - /** - * @var ResourceConnection - */ - private $resourceConnection; - - /** - * @var SourceInterfaceFactory - */ - private $sourceInterfaceFactory; - - /** - * @param ResourceConnection $resourceConnection - * @param SourceInterfaceFactory $sourceInterfaceFactory - */ - public function __construct( - ResourceConnection $resourceConnection, - SourceInterfaceFactory $sourceInterfaceFactory - ) { - $this->resourceConnection = $resourceConnection; - $this->sourceInterfaceFactory = $sourceInterfaceFactory; - } - - /** - * {@inheritdoc} - * - * @throws - */ - public function execute(string $country, string $postcode, int $radius): array - { - $connection = $this->resourceConnection->getConnection(); - $tableName = $this->resourceConnection->getTableName('inventory_geoname'); - $sourceTable = $this->resourceConnection->getTableName('inventory_source'); - - $query = $connection->select()->from($tableName) - ->where('country_code = ?', $country) - ->where('postcode = ?', $postcode) - ->limit(1); - $row = $connection->fetchRow($query); - - if (!$row) { - throw new NoSuchEntityException( - __('Unknown postcode %1 in %2', $postcode, $country) - ); - } - - // Still here so the target postcode is valid - $lat = (float)$row['latitude']; - $lng = (float)$row['longitude']; - - // Build up a radial query - $query = $connection->select() - ->from($sourceTable) - ->columns(['*', $this->createDistanceColumn($lat, $lng) . ' AS distance']) - ->where(SourceInterface::ENABLED) - ->having('distance <= ?', $radius) - ->order('distance ASC'); - - $rows = $connection->fetchAll($query); - $results = []; - foreach ($rows as $row) { - $item = $this->sourceInterfaceFactory->create(['data' => $row]); - $results[] = $item; - } - - return $results; - } - - /** - * Construct DB query to calculate Great Circle Distance - * - * @param float $latitude - * @param float $longitude - * @return string - */ - private function createDistanceColumn(float $latitude, float $longitude): string - { - return '(' . self::EARTH_RADIUS_KM . ' * ACOS(' - . 'COS(RADIANS(' . $latitude . ')) * ' - . 'COS(RADIANS(latitude)) * ' - . 'COS(RADIANS(longitude) - RADIANS(' . $longitude . ')) + ' - . 'SIN(RADIANS(' . $latitude . ')) * ' - . 'SIN(RADIANS(latitude))' - . '))'; - } -} diff --git a/InventoryInStorePickup/Model/GetNearbyPickupLocations.php b/InventoryInStorePickup/Model/GetNearbyPickupLocations.php new file mode 100644 index 000000000000..b63c15c70b61 --- /dev/null +++ b/InventoryInStorePickup/Model/GetNearbyPickupLocations.php @@ -0,0 +1,129 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickup\Model; + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\InventoryApi\Api\Data\SourceInterface; +use Magento\InventoryApi\Api\Data\StockSourceLinkInterface; +use Magento\InventoryApi\Api\GetStockSourceLinksInterface; +use Magento\InventoryApi\Api\SourceRepositoryInterface; +use Magento\InventoryDistanceBasedSourceSelectionApi\Api\GetLatLngFromAddressInterface; +use Magento\InventoryInStorePickup\Model\Convert\AddressToSourceSelectionAddress; +use Magento\InventoryInStorePickup\Model\PickupLocation\Mapper; +use Magento\InventoryInStorePickup\Model\ResourceModel\Source\GetDistanceOrderedSourceCodes; +use Magento\InventoryInStorePickupApi\Api\Data\AddressInterface; +use Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface; +use Magento\InventoryInStorePickupApi\Api\GetNearbyPickupLocationsInterface; + +/** + * Find nearest Pickup Locations by postal code using Haversine formula (Great Circle Distance) database query. + */ +class GetNearbyPickupLocations implements GetNearbyPickupLocationsInterface +{ + /** + * @var \Magento\InventoryInStorePickup\Model\PickupLocation\Mapper + */ + private $mapper; + + /** + * @var \Magento\InventoryInStorePickup\Model\Convert\AddressToSourceSelectionAddress + */ + private $addressToSourceSelectionAddress; + + /** + * @var \Magento\InventoryDistanceBasedSourceSelectionApi\Api\GetLatLngFromAddressInterface + */ + private $getLatLngFromAddress; + + /** + * @var \Magento\InventoryInStorePickup\Model\ResourceModel\Source\GetDistanceOrderedSourceCodes + */ + private $getDistanceOrderedSourceCodes; + + /** + * @var \Magento\InventoryApi\Api\GetStockSourceLinksInterface + */ + private $getStockSourceLinks; + + /** + * @var \Magento\Framework\Api\SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + + /** + * @var \Magento\InventoryApi\Api\SourceRepositoryInterface + */ + private $sourceRepository; + + /** + * @param \Magento\InventoryInStorePickup\Model\PickupLocation\Mapper $mapper + * @param \Magento\InventoryInStorePickup\Model\Convert\AddressToSourceSelectionAddress $addressToSourceSelectionAddress + * @param \Magento\InventoryDistanceBasedSourceSelectionApi\Api\GetLatLngFromAddressInterface $getLatLngFromAddress + * @param \Magento\InventoryInStorePickup\Model\ResourceModel\Source\GetDistanceOrderedSourceCodes $getDistanceOrderedSourceCodes + * @param \Magento\InventoryApi\Api\GetStockSourceLinksInterface $getStockSourceLinks + * @param \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder + * @param \Magento\InventoryApi\Api\SourceRepositoryInterface $sourceRepository + */ + public function __construct( + Mapper $mapper, + AddressToSourceSelectionAddress $addressToSourceSelectionAddress, + GetLatLngFromAddressInterface $getLatLngFromAddress, + GetDistanceOrderedSourceCodes $getDistanceOrderedSourceCodes, + GetStockSourceLinksInterface $getStockSourceLinks, + SearchCriteriaBuilder $searchCriteriaBuilder, + SourceRepositoryInterface $sourceRepository + ) { + $this->mapper = $mapper; + $this->addressToSourceSelectionAddress = $addressToSourceSelectionAddress; + $this->getLatLngFromAddress = $getLatLngFromAddress; + $this->getDistanceOrderedSourceCodes = $getDistanceOrderedSourceCodes; + $this->getStockSourceLinks = $getStockSourceLinks; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->sourceRepository = $sourceRepository; + } + + /** + * @inheritdoc + */ + public function execute(AddressInterface $address, int $radius, int $stockId): array + { + $sourceSelectionAddress = $this->addressToSourceSelectionAddress->execute($address); + $latLng = $this->getLatLngFromAddress->execute($sourceSelectionAddress); + + $codes = $this->getDistanceOrderedSourceCodes->execute($latLng, $radius); + + $searchCriteria = $this->searchCriteriaBuilder + ->addFilter(StockSourceLinkInterface::STOCK_ID, $stockId) + ->addFilter(StockSourceLinkInterface::SOURCE_CODE, $codes, 'in') + ->create(); + $searchResult = $this->getStockSourceLinks->execute($searchCriteria); + $stockCodes = []; + + foreach ($searchResult->getItems() as $item) { + $stockCodes[] = $item->getSourceCode(); + } + + $searchCriteria = $this->searchCriteriaBuilder + ->addFilter(SourceInterface::SOURCE_CODE, $stockCodes, 'in') + ->addFilter(PickupLocationInterface::IS_PICKUP_LOCATION_ACTIVE, true) + ->create(); + $searchResult = $this->sourceRepository->getList($searchCriteria); + + $results = []; + + foreach ($searchResult->getItems() as $source) { + $results[] = $this->mapper->map($source); + } + + usort($results, function (PickupLocationInterface $left, PickupLocationInterface $right) use ($codes) { + return array_search($left->getSourceCode(), $codes) <=> array_search($right->getSourceCode(), $codes); + }); + + return $results; + } +} diff --git a/InventoryInStorePickup/Model/ResourceModel/Source/GetDistanceOrderedSourceCodes.php b/InventoryInStorePickup/Model/ResourceModel/Source/GetDistanceOrderedSourceCodes.php new file mode 100644 index 000000000000..058372bb6b98 --- /dev/null +++ b/InventoryInStorePickup/Model/ResourceModel/Source/GetDistanceOrderedSourceCodes.php @@ -0,0 +1,73 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickup\Model\ResourceModel\Source; + +use Magento\Framework\App\ResourceConnection; +use Magento\InventoryApi\Api\Data\SourceInterface; +use Magento\InventoryDistanceBasedSourceSelectionApi\Api\Data\LatLngInterface; + +/** + * Get Source Codes, ordered by distance to request coordinates. + */ +class GetDistanceOrderedSourceCodes +{ + private const EARTH_RADIUS_KM = 6372.797; + + /** + * @var \Magento\Framework\App\ResourceConnection + */ + private $resourceConnection; + + /** + * GetDistanceOrderedSourceCodes constructor. + * + * @param \Magento\Framework\App\ResourceConnection $resourceConnection + */ + public function __construct(ResourceConnection $resourceConnection) + { + $this->resourceConnection = $resourceConnection; + } + + /** + * @param \Magento\InventoryDistanceBasedSourceSelectionApi\Api\Data\LatLngInterface $latLng + * @param int $radius + * + * @return string[] + */ + public function execute(LatLngInterface $latLng, int $radius): array + { + $connection = $this->resourceConnection->getConnection(); + $sourceTable = $this->resourceConnection->getTableName('inventory_source'); + $query = $connection->select() + ->from($sourceTable) + ->where(SourceInterface::ENABLED) + ->columns(['source_code', $this->createDistanceColumn($latLng) . ' AS distance']) + ->having('distance <= ?', $radius) + ->order('distance ASC'); + + return $connection->fetchCol($query); + } + + /** + * Construct DB query to calculate Great Circle Distance + * + * @param LatLngInterface $latLng + * + * @return string + */ + private function createDistanceColumn(LatLngInterface $latLng): string + { + return '(' . self::EARTH_RADIUS_KM . ' * ACOS(' + . 'COS(RADIANS(' . $latLng->getLat() . ')) * ' + . 'COS(RADIANS(latitude)) * ' + . 'COS(RADIANS(longitude) - RADIANS(' . $latLng->getLng() . ')) + ' + . 'SIN(RADIANS(' . $latLng->getLat() . ')) * ' + . 'SIN(RADIANS(latitude))' + . '))'; + } +} diff --git a/InventoryInStorePickup/Test/Integration/DistanceProvider/Offline/GetNearbySourcesByPostcodeTest.php b/InventoryInStorePickup/Test/Integration/DistanceProvider/Offline/GetNearbySourcesByPostcodeTest.php deleted file mode 100644 index c42c7c8dc9c8..000000000000 --- a/InventoryInStorePickup/Test/Integration/DistanceProvider/Offline/GetNearbySourcesByPostcodeTest.php +++ /dev/null @@ -1,71 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryInStorePickup\Test\Integration\DistanceProvider\Offline; - -use Magento\InventoryApi\Api\Data\SourceInterface; -use Magento\InventoryInStorePickup\Model\DistanceProvider\Offline\GetNearbySourcesByPostcode; -use Magento\TestFramework\Helper\Bootstrap; -use PHPUnit\Framework\TestCase; - -class GetNearbySourcesByPostcodeTest extends TestCase -{ - /** - * @var GetNearbySourcesByPostcode - */ - private $getNearbySourcesByPostcode; - - protected function setUp() - { - $this->getNearbySourcesByPostcode = Bootstrap::getObjectManager()->get(GetNearbySourcesByPostcode::class); - } - - /** - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryInStorePickup/Test/_files/source_addresses.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stocks.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stock_source_links.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryInStorePickup/Test/_files/inventory_geoname.php - * - * @param string $country - * @param string $postcode - * @param int $radius - * @param array $sortedSourceCodes - * - * @dataProvider executeDataProvider - * - * @magentoDbIsolation disabled - * @throws - */ - public function testExecute(string $country, string $postcode, int $radius, array $sortedSourceCodes) - { - /** @var SourceInterface[] $sources */ - $sources = $this->getNearbySourcesByPostcode->execute($country, $postcode, $radius); - - $this->assertCount(count($sortedSourceCodes), $sources); - foreach ($sortedSourceCodes as $key => $code) { - $this->assertEquals($code, $sources[$key]->getSourceCode()); - } - } - - /** - * @return array - */ - public function executeDataProvider(): array - { - return [ - ['DE', '81671', 500, ['eu-3']], - ['FR', '56290', 1000, ['eu-1', 'eu-2']], - ['FR', '84490', 1000, ['eu-2', 'eu-1', 'eu-3']], - ['IT', '12022', 350, ['eu-2']], - ['IT', '39030', 350, ['eu-3']], - ['DE', '26419', 750, ['eu-1', 'eu-3']], - ]; - } -} diff --git a/InventoryInStorePickup/Test/Integration/GetNearbyPickupLocationsOfflineTest.php b/InventoryInStorePickup/Test/Integration/GetNearbyPickupLocationsOfflineTest.php new file mode 100644 index 000000000000..16a82eb25cbb --- /dev/null +++ b/InventoryInStorePickup/Test/Integration/GetNearbyPickupLocationsOfflineTest.php @@ -0,0 +1,167 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickup\Test\Integration; + +use Magento\InventoryInStorePickup\Model\AddressFactory; +use Magento\InventoryInStorePickup\Model\GetNearbyPickupLocations; +use Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +class GetNearbyPickupLocationsOfflineTest extends TestCase +{ + /** + * @var GetNearbyPickupLocations + */ + private $getNearbyPickupLocations; + + /** + * @var AddressFactory + */ + private $addressFactory; + + protected function setUp() + { + $this->getNearbyPickupLocations = Bootstrap::getObjectManager()->get(GetNearbyPickupLocations::class); + $this->addressFactory = Bootstrap::getObjectManager()->get(AddressFactory::class); + } + + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryInStorePickup/Test/_files/source_addresses.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryInStorePickup/Test/_files/source_pickup_location_attributes.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stocks.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stock_source_links.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryInStorePickup/Test/_files/inventory_geoname.php + * @magentoConfigFixture default/cataloginventory/source_selection_distance_based/provider offline + * + * @param array $addressData + * @param int $radius + * @param int $stockId + * @param array $sortedSourceCodes + * + * @dataProvider executeDataProvider + * @magentoAppArea frontend + * + * @magentoDbIsolation disabled + */ + public function testExecute( + array $addressData, + int $radius, + int $stockId, + array $sortedSourceCodes + ) { + $address = $this->addressFactory->create($addressData); + + /** @var PickupLocationInterface[] $sources */ + $pickupLocations = $this->getNearbyPickupLocations->execute($address, $radius, $stockId); + + $this->assertCount(count($sortedSourceCodes), $pickupLocations); + foreach ($sortedSourceCodes as $key => $code) { + $this->assertEquals($code, $pickupLocations[$key]->getSourceCode()); + } + } + + /** + * [ + * Address[ + * Country, + * Postcode, + * Region, + * City + * ] + * Radius (in KM), + * Stock Id, + * Expected Pickup Locations Codes[] + * ] + * + * @return array + */ + public function executeDataProvider(): array + { + return [ + [ + [ + 'country' => 'DE', + 'postcode' => '81671', + 'region' => null, + 'city' => null, + ], + 500, + 10, + ['eu-3'] + ], + [ + [ + 'country' => 'FR', + 'postcode' => null, + 'region' => 'Bretagne', + 'city' => null + ], + 1000, + 10, + ['eu-1'] + ], + [ + [ + 'country' => 'FR', + 'postcode' => null, + 'region' => null, + 'city' => 'Saint-Saturnin-lès-Apt' + ], + 1000, + 30, + ['eu-1', 'eu-3'] + ], + [ + [ + 'country' => 'IT', + 'postcode' => '12022', + 'region' => null, + 'city' => null + ], + 350, + 10, + [] + ], + [ + [ + 'country' => 'IT', + 'postcode' => '39030', + 'region' => 'Trentino-Alto Adige', + 'city' => 'Rasun Di Sotto' + ], + 350, + 10, + ['eu-3'] + ], + [ + [ + 'country' => 'DE', + 'postcode' => '86559', + 'region' => null, + 'city' => null + ], + 750, + 30, + ['eu-3', 'eu-1'] + ], + [ + [ + 'country' => 'US', + 'postcode' => null, + 'region' => 'Kansas', + 'city' => null + ], + 1000, + 20, + ['us-1'] + ] + ]; + } +} diff --git a/InventoryInStorePickup/Test/_files/source_pickup_location_attributes.php b/InventoryInStorePickup/Test/_files/source_pickup_location_attributes.php new file mode 100644 index 000000000000..0c9195409bf3 --- /dev/null +++ b/InventoryInStorePickup/Test/_files/source_pickup_location_attributes.php @@ -0,0 +1,38 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\InventoryApi\Api\SourceRepositoryInterface; +use Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var SourceRepositoryInterface $sourceRepository */ +$sourceRepository = Bootstrap::getObjectManager()->get(SourceRepositoryInterface::class); + +$pickupLocationAttributesMap = [ + 'eu-1' => [ + PickupLocationInterface::IS_PICKUP_LOCATION_ACTIVE => true + ], + 'eu-2' => [ + PickupLocationInterface::IS_PICKUP_LOCATION_ACTIVE => false + ], + 'eu-3' => [ + PickupLocationInterface::IS_PICKUP_LOCATION_ACTIVE => true + ], + 'eu-disabled' => [ + PickupLocationInterface::IS_PICKUP_LOCATION_ACTIVE => false + ], + 'us-1' => [ + PickupLocationInterface::IS_PICKUP_LOCATION_ACTIVE => true + ] +]; + +foreach ($pickupLocationAttributesMap as $sourceCode => $value) { + $source = $sourceRepository->get($sourceCode); + $extension = $source->getExtensionAttributes(); + $extension->setIsPickupLocationActive($value[PickupLocationInterface::IS_PICKUP_LOCATION_ACTIVE]); + $sourceRepository->save($source); +} diff --git a/InventoryInStorePickup/etc/di.xml b/InventoryInStorePickup/etc/di.xml index 1a33c8f9812a..5b36a39dd2d2 100644 --- a/InventoryInStorePickup/etc/di.xml +++ b/InventoryInStorePickup/etc/di.xml @@ -18,13 +18,8 @@ <preference for="Magento\InventoryInStorePickupApi\Api\GetNearbySourcesByPostcodeInterface" type="Magento\InventoryInStorePickup\Model\GetNearbySourcesByPostcode"/> <preference for="Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface" type="Magento\InventoryInStorePickup\Model\PickupLocation" /> - <type name="Magento\InventoryInStorePickupApi\Model\GetNearbySourcesByPostcode"> - <arguments> - <argument name="providers" xsi:type="array"> - <item name="offline" xsi:type="object">Magento\InventoryInStorePickup\Model\DistanceProvider\Offline\GetNearbySourcesByPostcode</item> - </argument> - </arguments> - </type> + <preference for="Magento\InventoryInStorePickupApi\Api\GetNearbyPickupLocationsInterface" type="Magento\InventoryInStorePickup\Model\GetNearbyPickupLocations"/> + <preference for="Magento\InventoryInStorePickupApi\Api\Data\AddressInterface" type="Magento\InventoryInStorePickup\Model\Address" /> <preference for="Magento\InventoryInStorePickupApi\Api\NotifyOrderIsReadyForPickupInterface" type="Magento\InventoryInStorePickup\Model\NotifyOrderIsReadyForPickup"/> diff --git a/InventoryInStorePickupApi/Api/Data/AddressInterface.php b/InventoryInStorePickupApi/Api/Data/AddressInterface.php new file mode 100644 index 000000000000..192fddaffb9c --- /dev/null +++ b/InventoryInStorePickupApi/Api/Data/AddressInterface.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickupApi\Api\Data; + +/** + * Data interface for nearest pickup locations search request. + * + * @api + */ +interface AddressInterface +{ + /** + * Requested country + * + * @return string|null + */ + public function getCountry(): ?string; + + /** + * Requested postcode + * + * @return string|null + */ + public function getPostcode(): ?string; + + /** + * Requested region + * + * @return string|null + */ + public function getRegion(): ?string; + + /** + * Requested city + * + * @return string|null + */ + public function getCity(): ?string; +} diff --git a/InventoryInStorePickupApi/Api/GetNearbyPickupLocationsInterface.php b/InventoryInStorePickupApi/Api/GetNearbyPickupLocationsInterface.php new file mode 100644 index 000000000000..ab3b2010745b --- /dev/null +++ b/InventoryInStorePickupApi/Api/GetNearbyPickupLocationsInterface.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickupApi\Api; + +use Magento\InventoryInStorePickupApi\Api\Data\AddressInterface; +use Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface; + +/** + * Get nearby sources of a given zip code, based on the given radius in KM. + * + * @api + */ +interface GetNearbyPickupLocationsInterface +{ + /** + * @param AddressInterface $address + * @param int $radius + * @param int $stockId + * @return PickupLocationInterface[] + */ + public function execute(AddressInterface $address, int $radius, int $stockId): array; +} diff --git a/InventoryInStorePickupApi/Api/GetNearbySourcesByPostcodeInterface.php b/InventoryInStorePickupApi/Api/GetNearbySourcesByPostcodeInterface.php deleted file mode 100644 index bd1b3ca9136e..000000000000 --- a/InventoryInStorePickupApi/Api/GetNearbySourcesByPostcodeInterface.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryInStorePickupApi\Api; - -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\InventoryApi\Api\Data\SourceInterface; - -/** - * Get nearby sources of a given zip code, based on the given radius in KM. - * - * @api - */ -interface GetNearbySourcesByPostcodeInterface -{ - /** - * Get nearby sources to a given postcode code, based on the given radius in KM - * - * @param string $country - * @param string $postcode - * @param int $radius - * @return SourceInterface[] - * - * @throws NoSuchEntityException - */ - public function execute(string $country, string $postcode, int $radius): array; -} diff --git a/InventoryInStorePickupApi/Model/GetNearbySourcesByPostcode.php b/InventoryInStorePickupApi/Model/GetNearbySourcesByPostcode.php deleted file mode 100644 index 000344622769..000000000000 --- a/InventoryInStorePickupApi/Model/GetNearbySourcesByPostcode.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryInStorePickupApi\Model; - -use InvalidArgumentException; -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\InventoryDistanceBasedSourceSelectionApi\Api\GetDistanceProviderCodeInterface; -use Magento\InventoryInStorePickupApi\Api\GetNearbySourcesByPostcodeInterface; - -/** - * Get nearby sources of a given zip code. - * - * @api - */ -class GetNearbySourcesByPostcode implements GetNearbySourcesByPostcodeInterface -{ - /** - * @var GetNearbySourcesByPostcodeInterface[] - */ - private $providers; - - /** - * @var GetDistanceProviderCodeInterface - */ - private $getDistanceProviderCode; - - /** - * @param GetDistanceProviderCodeInterface $getDistanceProviderCode - * @param GetNearbySourcesByPostcodeInterface[] $providers - */ - public function __construct( - GetDistanceProviderCodeInterface $getDistanceProviderCode, - array $providers - ) { - foreach ($providers as $providerCode => $provider) { - if (!($provider instanceof GetNearbySourcesByPostcodeInterface)) { - throw new InvalidArgumentException( - sprintf( - "Nearby Sources provider %s must implement %s", - $providerCode, - GetNearbySourcesByPostcodeInterface::class - ) - ); - } - } - - $this->providers = $providers; - $this->getDistanceProviderCode = $getDistanceProviderCode; - } - - /** - * @inheritdoc - */ - public function execute(string $country, string $postcode, int $radius): array - { - $code = $this->getDistanceProviderCode->execute(); - if (!isset($this->providers[$code])) { - throw new NoSuchEntityException( - __('No such sources from postcode provider: %1', $code) - ); - } - - return $this->providers[$code]->execute($country, $postcode, $radius); - } -} diff --git a/InventoryInStorePickupApi/etc/acl.xml b/InventoryInStorePickupApi/etc/acl.xml new file mode 100644 index 000000000000..e44263c7ce9d --- /dev/null +++ b/InventoryInStorePickupApi/etc/acl.xml @@ -0,0 +1,20 @@ +<?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:Acl/etc/acl.xsd"> + <acl> + <resources> + <resource id="Magento_Backend::admin"> + <resource id="Magento_Backend::stores"> + <resource id="Magento_InventoryApi::inventory"> + <resource id="Magento_InventoryApi::inStorePickup" title="In-Store Pickup" translate="title" sortOrder="20"/> + </resource> + </resource> + </resource> + </resources> + </acl> +</config> diff --git a/InventoryInStorePickupApi/etc/di.xml b/InventoryInStorePickupApi/etc/di.xml deleted file mode 100644 index 0ff53d9d7177..000000000000 --- a/InventoryInStorePickupApi/etc/di.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?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"> - <preference for="Magento\InventoryInStorePickupApi\Api\GetNearbySourcesByPostcodeInterface" - type="Magento\InventoryInStorePickupApi\Model\GetNearbySourcesByPostcode"/> -</config> diff --git a/InventoryInStorePickupApi/etc/webapi.xml b/InventoryInStorePickupApi/etc/webapi.xml new file mode 100644 index 000000000000..c9d2f47c52bf --- /dev/null +++ b/InventoryInStorePickupApi/etc/webapi.xml @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Webapi:etc/webapi.xsd"> + <route url="/V1/inventory/in-store-pickup/get-nearby-pickup-locations" method="GET"> + <service class="Magento\InventoryInStorePickupApi\Api\GetNearbyPickupLocationsInterface" method="execute"/> + <resources> + <resource ref="Magento_InventoryApi::inStorePickup"/> + </resources> + </route> +</routes> From d2600037643f4b7abd255a68078b4dbd8f5651ba Mon Sep 17 00:00:00 2001 From: "al.kravchuk" <al.kravchuk@ism-ukraine.com> Date: Sat, 27 Apr 2019 22:38:30 +0300 Subject: [PATCH 212/231] MSI-2185 Improve GetNearbySourcesByPostcode API. Fix static tests. --- InventoryInStorePickup/composer.json | 4 +++- InventoryInStorePickup/etc/module.xml | 2 ++ .../Api/GetNearbyPickupLocationsInterface.php | 3 +-- InventoryInStorePickupApi/composer.json | 4 +--- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/InventoryInStorePickup/composer.json b/InventoryInStorePickup/composer.json index 8d438f55a232..3194163c4cb8 100644 --- a/InventoryInStorePickup/composer.json +++ b/InventoryInStorePickup/composer.json @@ -5,9 +5,11 @@ "php": "~7.1.3||~7.2.0", "magento/framework": "*", "magento/module-inventory-in-store-pickup-api": "*", + "magento/module-inventory-distance-based-source-selection-api": "*", "magento/module-inventory-api": "*", "magento/module-sales": "*", - "magento/module-store": "*" + "magento/module-store": "*", + "magento/module-inventory-source-selection-api": "*" }, "type": "magento2-module", "license": [ diff --git a/InventoryInStorePickup/etc/module.xml b/InventoryInStorePickup/etc/module.xml index 5e2672c21b74..3d2f7e580ff1 100644 --- a/InventoryInStorePickup/etc/module.xml +++ b/InventoryInStorePickup/etc/module.xml @@ -13,6 +13,8 @@ <module name="Magento_InventoryDistanceBasedSourceSelection" /> <module name="Magento_Sales" /> <module name="Magento_Store" /> + <module name="Magento_InventoryDistanceBasedSourceSelectionApi" /> + <module name="Magento_InventorySourceSelectionApi" /> </sequence> </module> </config> diff --git a/InventoryInStorePickupApi/Api/GetNearbyPickupLocationsInterface.php b/InventoryInStorePickupApi/Api/GetNearbyPickupLocationsInterface.php index ab3b2010745b..8b173575486d 100644 --- a/InventoryInStorePickupApi/Api/GetNearbyPickupLocationsInterface.php +++ b/InventoryInStorePickupApi/Api/GetNearbyPickupLocationsInterface.php @@ -8,7 +8,6 @@ namespace Magento\InventoryInStorePickupApi\Api; use Magento\InventoryInStorePickupApi\Api\Data\AddressInterface; -use Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface; /** * Get nearby sources of a given zip code, based on the given radius in KM. @@ -21,7 +20,7 @@ interface GetNearbyPickupLocationsInterface * @param AddressInterface $address * @param int $radius * @param int $stockId - * @return PickupLocationInterface[] + * @return \Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface[] */ public function execute(AddressInterface $address, int $radius, int $stockId): array; } diff --git a/InventoryInStorePickupApi/composer.json b/InventoryInStorePickupApi/composer.json index ae8815eda36b..604313b67885 100644 --- a/InventoryInStorePickupApi/composer.json +++ b/InventoryInStorePickupApi/composer.json @@ -3,9 +3,7 @@ "description": "N/A", "require": { "php": "~7.1.3||~7.2.0", - "magento/framework": "*", - "magento/module-inventory-api": "*", - "magento/module-inventory-distance-based-source-selection-api": "*" + "magento/framework": "*" }, "type": "magento2-module", "license": [ From dd8cb8cb1750f2dc15e53f7b9a78ca5fd9f0efef Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Sun, 28 Apr 2019 19:41:13 -0500 Subject: [PATCH 213/231] Revert "Merge pull request #2056 from magento-engcom/msi-2027-improve-performance-product-save-operation" This reverts commit 18ee9008f616cfcc82d166ba950dd52359b16eff, reversing changes made to ed9cc93310721ff712573655f8ea228af60c5c70. Conflicts: composer.lock This change had to be made due to anomaly with memory consumption of the solution revealed in preparation for 1.1.2-beta release. Performance improvements will be reintroduced after further investigation and determining the exact impact and root cause --- .../Model/BulkInventoryTransfer.php | 50 +++- InventoryCatalog/Model/BulkSourceUnassign.php | 54 ++++- .../ResourceModel/BulkInventoryTransfer.php | 149 ++++++++---- .../ResourceModel/BulkSourceUnassign.php | 29 ++- .../ResourceModel/BulkZeroLegacyStockItem.php | 6 +- .../SetDataToLegacyStockItem.php | 68 ++++++ .../TransferInventoryPartially.php | 51 ++-- .../SetDataToLegacyCatalogInventory.php | 146 ++++++++++++ ...UpdateSourceItemBasedOnLegacyStockItem.php | 91 ++++++++ ...dateSourceItemAtLegacyQtyCounterPlugin.php | 10 +- ...eSourceItemAtLegacyStockItemSavePlugin.php | 47 ++-- ...atalogInventoryAtSourceItemsSavePlugin.php | 41 ++-- ...alogInventoryAtSourceItemsDeletePlugin.php | 63 +++-- ...ToLegacyStockItemAtSourceItemsSaveTest.php | 70 +----- ...LegacyStockStatusAtSourceItemsSaveTest.php | 6 +- ...gacyStockStatusAtSourceItemsDeleteTest.php | 74 +----- ...LegacyStockItemAtSourceItemsDeleteTest.php | 6 +- ...ultSourceItemAtLegacyStockItemSaveTest.php | 49 +--- ...dateDefaultSourceItemAtProductSaveTest.php | 4 +- InventoryCatalog/etc/di.xml | 40 ++-- .../etc/adminhtml/system.xml | 10 - InventoryLegacySynchronization/LICENSE.txt | 48 ---- .../LICENSE_AFL.txt | 48 ---- .../Model/AsyncConsumer.php | 54 ----- .../Model/GetDefaultSourceItemsBySkus.php | 64 ----- .../Model/GetLegacyStockItemsByProductIds.php | 68 ------ .../Model/IsAsyncLegacyAlignment.php | 41 ---- .../UpdateLegacyStockItemsData.php | 54 ----- .../ResourceModel/UpdateSourceItemsData.php | 54 ----- .../Model/Synchronize.php | 160 ------------- .../Model/SynchronizeInventoryData.php | 55 ----- .../ToLegacy/SetDataToLegacyInventory.php | 156 ------------- .../Model/ToMsi/SetDataToSourceItem.php | 131 ----------- ...ToLegacyCatalogInventoryAtBulkTransfer.php | 98 -------- ...logInventoryAtTransferInventoryPartial.php | 85 ------- .../SetZeroQuantityToLegacyAtBulkUnassign.php | 77 ------ InventoryLegacySynchronization/README.md | 11 - ...gacyCatalogInventoryAtBulkTransferTest.php | 221 ------------------ .../SetZeroToLegacyAtBulkUnassignTest.php | 161 ------------- InventoryLegacySynchronization/composer.json | 29 --- .../etc/communication.xml | 12 - InventoryLegacySynchronization/etc/config.xml | 17 -- InventoryLegacySynchronization/etc/di.xml | 47 ---- InventoryLegacySynchronization/etc/module.xml | 12 - .../etc/queue_consumer.xml | 16 -- .../etc/queue_publisher.xml | 14 -- .../etc/queue_topology.xml | 19 -- .../registration.php | 13 -- .../LICENSE.txt | 48 ---- .../LICENSE_AFL.txt | 48 ---- .../README.md | 11 - .../composer.json | 21 -- .../etc/adminhtml/system.xml | 25 -- .../etc/module.xml | 12 - .../registration.php | 12 - 55 files changed, 663 insertions(+), 2343 deletions(-) rename InventoryLegacySynchronization/Model/ResourceModel/SetZeroQuantityToLegacyStockItems.php => InventoryCatalog/Model/ResourceModel/BulkZeroLegacyStockItem.php (91%) create mode 100644 InventoryCatalog/Model/ResourceModel/SetDataToLegacyStockItem.php create mode 100644 InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php create mode 100644 InventoryCatalog/Model/UpdateSourceItemBasedOnLegacyStockItem.php rename {InventoryLegacySynchronization/Plugin => InventoryCatalog/Plugin/CatalogInventory}/UpdateSourceItemAtLegacyQtyCounterPlugin.php (90%) rename {InventoryLegacySynchronization/Plugin => InventoryCatalog/Plugin/CatalogInventory}/UpdateSourceItemAtLegacyStockItemSavePlugin.php (80%) rename {InventoryLegacySynchronization/Plugin => InventoryCatalog/Plugin/InventoryApi}/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php (68%) rename {InventoryLegacySynchronization/Plugin => InventoryCatalog/Plugin/InventoryApi}/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php (61%) rename {InventoryLegacySynchronization => InventoryCatalog}/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php (57%) rename {InventoryLegacySynchronization => InventoryCatalog}/Test/Integration/SetDataToLegacyStockStatusAtSourceItemsSaveTest.php (96%) rename {InventoryLegacySynchronization => InventoryCatalog}/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php (54%) rename InventoryLegacySynchronization/Test/Integration/SetZeroToLegacyStockItemAtSourceItemsDeleteTest.php => InventoryCatalog/Test/Integration/SetToZeroLegacyStockItemAtSourceItemsDeleteTest.php (95%) rename {InventoryLegacySynchronization => InventoryCatalog}/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php (73%) rename {InventoryLegacySynchronization => InventoryCatalog}/Test/Integration/UpdateDefaultSourceItemAtProductSaveTest.php (95%) delete mode 100644 InventoryLegacySynchronization/LICENSE.txt delete mode 100644 InventoryLegacySynchronization/LICENSE_AFL.txt delete mode 100644 InventoryLegacySynchronization/Model/AsyncConsumer.php delete mode 100644 InventoryLegacySynchronization/Model/GetDefaultSourceItemsBySkus.php delete mode 100644 InventoryLegacySynchronization/Model/GetLegacyStockItemsByProductIds.php delete mode 100644 InventoryLegacySynchronization/Model/IsAsyncLegacyAlignment.php delete mode 100644 InventoryLegacySynchronization/Model/ResourceModel/UpdateLegacyStockItemsData.php delete mode 100644 InventoryLegacySynchronization/Model/ResourceModel/UpdateSourceItemsData.php delete mode 100644 InventoryLegacySynchronization/Model/Synchronize.php delete mode 100644 InventoryLegacySynchronization/Model/SynchronizeInventoryData.php delete mode 100644 InventoryLegacySynchronization/Model/ToLegacy/SetDataToLegacyInventory.php delete mode 100644 InventoryLegacySynchronization/Model/ToMsi/SetDataToSourceItem.php delete mode 100644 InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtBulkTransfer.php delete mode 100644 InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtTransferInventoryPartial.php delete mode 100644 InventoryLegacySynchronization/Plugin/SetZeroQuantityToLegacyAtBulkUnassign.php delete mode 100644 InventoryLegacySynchronization/README.md delete mode 100644 InventoryLegacySynchronization/Test/Integration/SetDataToLegacyCatalogInventoryAtBulkTransferTest.php delete mode 100644 InventoryLegacySynchronization/Test/Integration/SetZeroToLegacyAtBulkUnassignTest.php delete mode 100644 InventoryLegacySynchronization/composer.json delete mode 100644 InventoryLegacySynchronization/etc/communication.xml delete mode 100644 InventoryLegacySynchronization/etc/config.xml delete mode 100644 InventoryLegacySynchronization/etc/di.xml delete mode 100644 InventoryLegacySynchronization/etc/module.xml delete mode 100644 InventoryLegacySynchronization/etc/queue_consumer.xml delete mode 100644 InventoryLegacySynchronization/etc/queue_publisher.xml delete mode 100644 InventoryLegacySynchronization/etc/queue_topology.xml delete mode 100644 InventoryLegacySynchronization/registration.php delete mode 100644 InventoryLegacySynchronizationAdminUi/LICENSE.txt delete mode 100644 InventoryLegacySynchronizationAdminUi/LICENSE_AFL.txt delete mode 100644 InventoryLegacySynchronizationAdminUi/README.md delete mode 100644 InventoryLegacySynchronizationAdminUi/composer.json delete mode 100755 InventoryLegacySynchronizationAdminUi/etc/adminhtml/system.xml delete mode 100644 InventoryLegacySynchronizationAdminUi/etc/module.xml delete mode 100644 InventoryLegacySynchronizationAdminUi/registration.php diff --git a/InventoryCatalog/Model/BulkInventoryTransfer.php b/InventoryCatalog/Model/BulkInventoryTransfer.php index 79f1d7607089..b14c4788ad3b 100644 --- a/InventoryCatalog/Model/BulkInventoryTransfer.php +++ b/InventoryCatalog/Model/BulkInventoryTransfer.php @@ -10,7 +10,10 @@ use Magento\Framework\Validation\ValidationException; use Magento\InventoryCatalog\Model\ResourceModel\BulkInventoryTransfer as BulkInventoryTransferResource; use Magento\InventoryCatalogApi\Api\BulkInventoryTransferInterface; +use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; use Magento\InventoryCatalogApi\Model\BulkInventoryTransferValidatorInterface; +use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface; +use Magento\CatalogInventory\Model\Indexer\Stock as LegacyIndexer; use Magento\InventoryIndexer\Indexer\Source\SourceIndexer; /** @@ -28,34 +31,61 @@ class BulkInventoryTransfer implements BulkInventoryTransferInterface */ private $bulkInventoryTransfer; + /** + * @var GetProductIdsBySkusInterface + */ + private $getProductIdsBySkus; + + /** + * @var LegacyIndexer + */ + private $legacyIndexer; + + /** + * @var DefaultSourceProviderInterface + */ + private $defaultSourceProvider; + /** * @var SourceIndexer */ private $sourceIndexer; /** + * MassProductSourceAssign constructor. * @param BulkInventoryTransferValidatorInterface $inventoryTransferValidator * @param BulkInventoryTransferResource $bulkInventoryTransfer * @param SourceIndexer $sourceIndexer - * @param null $defaultSourceProvider @deprecated - * @param null $getProductIdsBySkus @deprecated - * @param null $legacyIndexer @deprecated + * @param DefaultSourceProviderInterface $defaultSourceProvider + * @param GetProductIdsBySkusInterface $getProductIdsBySkus + * @param LegacyIndexer $legacyIndexer * @SuppressWarnings(PHPMD.LongVariable) - * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( BulkInventoryTransferValidatorInterface $inventoryTransferValidator, BulkInventoryTransferResource $bulkInventoryTransfer, SourceIndexer $sourceIndexer, - $defaultSourceProvider, - $getProductIdsBySkus, - $legacyIndexer + DefaultSourceProviderInterface $defaultSourceProvider, + GetProductIdsBySkusInterface $getProductIdsBySkus, + LegacyIndexer $legacyIndexer ) { $this->bulkInventoryTransferValidator = $inventoryTransferValidator; $this->bulkInventoryTransfer = $bulkInventoryTransfer; + $this->getProductIdsBySkus = $getProductIdsBySkus; + $this->legacyIndexer = $legacyIndexer; + $this->defaultSourceProvider = $defaultSourceProvider; $this->sourceIndexer = $sourceIndexer; } + /** + * Reindex legacy stock (for default source) + * @param array $productIds + */ + private function reindexLegacy(array $productIds): void + { + $this->legacyIndexer->executeList($productIds); + } + /** * @inheritdoc * @throws \Magento\Framework\Exception\NoSuchEntityException @@ -85,6 +115,12 @@ public function execute( $this->sourceIndexer->executeList([$originSource, $destinationSource]); + if (($this->defaultSourceProvider->getCode() === $originSource) || + ($this->defaultSourceProvider->getCode() === $destinationSource)) { + $productIds = array_values($this->getProductIdsBySkus->execute($skus)); + $this->reindexLegacy($productIds); + } + return true; } } diff --git a/InventoryCatalog/Model/BulkSourceUnassign.php b/InventoryCatalog/Model/BulkSourceUnassign.php index e38c71b83950..6b8f63e75d93 100644 --- a/InventoryCatalog/Model/BulkSourceUnassign.php +++ b/InventoryCatalog/Model/BulkSourceUnassign.php @@ -9,9 +9,11 @@ use Magento\Framework\Validation\ValidationException; use Magento\InventoryCatalogApi\Api\BulkSourceUnassignInterface; +use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; use Magento\InventoryCatalogApi\Model\BulkSourceUnassignValidatorInterface; use Magento\InventoryCatalog\Model\ResourceModel\BulkSourceUnassign as BulkSourceUnassignResource; use Magento\InventoryIndexer\Indexer\Source\SourceIndexer; +use Magento\CatalogInventory\Model\Indexer\Stock as LegacyIndexer; /** * @inheritdoc @@ -34,34 +36,60 @@ class BulkSourceUnassign implements BulkSourceUnassignInterface private $sourceIndexer; /** + * @var LegacyIndexer + */ + private $legacyIndexer; + + /** + * @var DefaultSourceProviderInterface + */ + private $defaultSourceProvider; + + /** + * @var GetProductIdsBySkus + */ + private $getProductIdsBySkus; + + /** + * MassProductSourceAssign constructor. * @param BulkSourceUnassignValidatorInterface $unassignValidator * @param BulkSourceUnassignResource $bulkSourceUnassign - * @param null $defaultSourceProvider @deprecated - * @param null $getProductIdsBySkus @deprecated + * @param DefaultSourceProviderInterface $defaultSourceProvider + * @param GetProductIdsBySkus $getProductIdsBySkus * @param SourceIndexer $sourceIndexer - * @param null $legacyIndexer @deprecated + * @param LegacyIndexer $legacyIndexer * @SuppressWarnings(PHPMD.LongVariable) - * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( BulkSourceUnassignValidatorInterface $unassignValidator, BulkSourceUnassignResource $bulkSourceUnassign, - $defaultSourceProvider, - $getProductIdsBySkus, + DefaultSourceProviderInterface $defaultSourceProvider, + GetProductIdsBySkus $getProductIdsBySkus, SourceIndexer $sourceIndexer, - $legacyIndexer + LegacyIndexer $legacyIndexer ) { $this->unassignValidator = $unassignValidator; $this->bulkSourceUnassign = $bulkSourceUnassign; $this->sourceIndexer = $sourceIndexer; + $this->legacyIndexer = $legacyIndexer; + $this->defaultSourceProvider = $defaultSourceProvider; + $this->getProductIdsBySkus = $getProductIdsBySkus; } /** - * @inheritdoc + * Reindex legacy stock (for default source) * @param array $skus - * @param array $sourceCodes - * @return int - * @throws ValidationException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function reindexLegacy(array $skus): void + { + $productIds = array_values($this->getProductIdsBySkus->execute($skus)); + $this->legacyIndexer->executeList($productIds); + } + + /** + * @inheritdoc + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function execute(array $skus, array $sourceCodes): int { @@ -73,6 +101,10 @@ public function execute(array $skus, array $sourceCodes): int $res = $this->bulkSourceUnassign->execute($skus, $sourceCodes); $this->sourceIndexer->executeList($sourceCodes); + if (in_array($this->defaultSourceProvider->getCode(), $sourceCodes, true)) { + $this->reindexLegacy($skus); + } + return $res; } } diff --git a/InventoryCatalog/Model/ResourceModel/BulkInventoryTransfer.php b/InventoryCatalog/Model/ResourceModel/BulkInventoryTransfer.php index 824e37cb1a25..219d5802b541 100644 --- a/InventoryCatalog/Model/ResourceModel/BulkInventoryTransfer.php +++ b/InventoryCatalog/Model/ResourceModel/BulkInventoryTransfer.php @@ -10,6 +10,7 @@ use Magento\Framework\App\ResourceConnection; use Magento\Inventory\Model\ResourceModel\SourceItem; use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; use Magento\InventoryCatalogApi\Model\GetProductTypesBySkusInterface; use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForProductTypeInterface; @@ -36,83 +37,128 @@ class BulkInventoryTransfer */ private $isSourceItemManagementAllowedForProductType; + /** + * @var DefaultSourceProviderInterface + */ + private $defaultSourceProvider; + + /** + * @var SetDataToLegacyStockItem + */ + private $setDataToLegacyStockItem; + + /** + * @var BulkZeroLegacyStockItem + */ + private $bulkZeroLegacyStockItem; + /** * @param ResourceConnection $resourceConnection * @param GetProductTypesBySkusInterface $getProductTypesBySkus * @param IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemManagementAllowedForProductType + * @param DefaultSourceProviderInterface $defaultSourceProvider + * @param SetDataToLegacyStockItem $setDataToLegacyStockItem + * @param BulkZeroLegacyStockItem $bulkZeroLegacyStockItem * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( ResourceConnection $resourceConnection, GetProductTypesBySkusInterface $getProductTypesBySkus, - IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemManagementAllowedForProductType + IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemManagementAllowedForProductType, + DefaultSourceProviderInterface $defaultSourceProvider, + SetDataToLegacyStockItem $setDataToLegacyStockItem, + BulkZeroLegacyStockItem $bulkZeroLegacyStockItem ) { $this->resourceConnection = $resourceConnection; $this->getProductTypesBySkus = $getProductTypesBySkus; $this->isSourceItemManagementAllowedForProductType = $isSourceItemManagementAllowedForProductType; + $this->defaultSourceProvider = $defaultSourceProvider; + $this->setDataToLegacyStockItem = $setDataToLegacyStockItem; + $this->bulkZeroLegacyStockItem = $bulkZeroLegacyStockItem; } /** - * @param array $skus + * @param string $sku + * @param string $source + * @return array|null + */ + private function getSourceItemData(string $sku, string $source): ?array + { + $connection = $this->resourceConnection->getConnection(); + $tableName = $this->resourceConnection->getTableName(SourceItem::TABLE_NAME_SOURCE_ITEM); + + $query = $connection->select()->from($tableName) + ->where(SourceItemInterface::SOURCE_CODE . ' = ?', $source) + ->where(SourceItemInterface::SKU . ' = ?', $sku); + + $res = $connection->fetchRow($query); + if ($res === false) { + return null; + } + + return $res; + } + + /** + * @param string $sku * @param string $originSource * @param string $destinationSource * @return void */ private function transferInventory( - array $skus, + string $sku, string $originSource, string $destinationSource ): void { $connection = $this->resourceConnection->getConnection(); $tableName = $this->resourceConnection->getTableName(SourceItem::TABLE_NAME_SOURCE_ITEM); - $orgSourceItemsData = $connection->fetchAssoc( - $connection->select() - ->from($tableName, [SourceItemInterface::SKU, '*']) - ->where(SourceItemInterface::SOURCE_CODE . ' = ?', $originSource) - ->where(SourceItemInterface::SKU . ' IN (?)', $skus) - ); - $dstSourceItemsData = $connection->fetchAssoc( - $connection->select() - ->from($tableName, [SourceItemInterface::SKU, '*']) - ->where(SourceItemInterface::SOURCE_CODE . ' = ?', $destinationSource) - ->where(SourceItemInterface::SKU . ' IN (?)', $skus) - ); - - $finalSourceItemsData = []; - foreach ($skus as $sku) { - $finalQuantity = (float) - ($orgSourceItemsData[$sku][SourceItemInterface::QUANTITY] ?? 0.0) + - ($dstSourceItemsData[$sku][SourceItemInterface::QUANTITY] ?? 0.0); - - $finalStatus = $orgSourceItemsData[$sku][SourceItemInterface::STATUS] ?? - $dstSourceItemsData[$sku][SourceItemInterface::STATUS] ?? - SourceItemInterface::STATUS_OUT_OF_STOCK; - - $finalSourceItemsData[] = [ - SourceItemInterface::SOURCE_CODE => $destinationSource, - SourceItemInterface::SKU => $sku, - SourceItemInterface::QUANTITY => $finalQuantity, - SourceItemInterface::STATUS => $finalStatus, - ]; + $orgSourceItem = $this->getSourceItemData($sku, $originSource); + $dstSourceItem = $this->getSourceItemData($sku, $destinationSource); + + $orgSourceItemQty = $orgSourceItem === null ? 0.0 : (float) $orgSourceItem[SourceItemInterface::QUANTITY]; + $dstSourceItemQty = $dstSourceItem === null ? 0.0 : (float) $dstSourceItem[SourceItemInterface::QUANTITY]; + + $finalQuantity = $orgSourceItemQty + $dstSourceItemQty; + + if ($orgSourceItem !== null) { + $status = (int) $orgSourceItem[SourceItemInterface::STATUS]; + } elseif ($dstSourceItemQty !== null) { + $status = (int) $dstSourceItem[SourceItemInterface::STATUS]; + } else { + $status = (int) SourceItemInterface::STATUS_OUT_OF_STOCK; } - $connection->insertOnDuplicate( - $tableName, - $finalSourceItemsData, - [ - SourceItemInterface::QUANTITY, - SourceItemInterface::STATUS - ] - ); + $updateOperation = [ + SourceItemInterface::QUANTITY => $finalQuantity, + SourceItemInterface::STATUS => $status, + ]; + + if ($dstSourceItem === null) { + $updateOperation[SourceItemInterface::SOURCE_CODE] = $destinationSource; + $updateOperation[SourceItemInterface::SKU] = $sku; + + $connection->insert($tableName, $updateOperation); + } elseif ($orgSourceItem !== null) { + $connection->update($tableName, $updateOperation, [ + SourceItemInterface::SOURCE_CODE . '=?' => $destinationSource, + SourceItemInterface::SKU . '=?' => $sku, + ]); + } + + // Align legacy stock + if ($destinationSource === $this->defaultSourceProvider->getCode()) { + $this->setDataToLegacyStockItem->execute($sku, $finalQuantity, $status); + } } /** * @param string[] $skus * @param string $source * @param bool $unassign + * @throws \Magento\Framework\Exception\NoSuchEntityException */ - private function clearSource(array $skus, string $source, bool $unassign): void + private function clearSource(array $skus, string $source, bool $unassign) { $connection = $this->resourceConnection->getConnection(); $tableName = $this->resourceConnection->getTableName(SourceItem::TABLE_NAME_SOURCE_ITEM); @@ -131,6 +177,11 @@ private function clearSource(array $skus, string $source, bool $unassign): void SourceItemInterface::SKU . ' IN(?)' => $skus, ]); } + + // Align legacy stock + if ($source === $this->defaultSourceProvider->getCode()) { + $this->bulkZeroLegacyStockItem->execute($skus); + } } /** @@ -140,6 +191,7 @@ private function clearSource(array $skus, string $source, bool $unassign): void * @param string $destinationSource * @param bool $unassignFromOrigin * @return void + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function execute( array $skus, @@ -149,23 +201,20 @@ public function execute( ): void { $connection = $this->resourceConnection->getConnection(); $types = $this->getProductTypesBySkus->execute($skus); + $processedSkus = []; - $filteredSkus = []; + $connection->beginTransaction(); foreach ($types as $sku => $type) { if ($this->isSourceItemManagementAllowedForProductType->execute($type)) { - $filteredSkus[] = $sku; + $this->transferInventory((string)$sku, $originSource, $destinationSource); + $processedSkus[] = $sku; } } - if (empty($filteredSkus)) { - return; + if (!empty($processedSkus)) { + $this->clearSource($processedSkus, $originSource, $unassignFromOrigin); } - $connection->beginTransaction(); - - $this->transferInventory($filteredSkus, $originSource, $destinationSource); - $this->clearSource($filteredSkus, $originSource, $unassignFromOrigin); - $connection->commit(); } } diff --git a/InventoryCatalog/Model/ResourceModel/BulkSourceUnassign.php b/InventoryCatalog/Model/ResourceModel/BulkSourceUnassign.php index 9b4e975b12c2..59cc6c8318dd 100644 --- a/InventoryCatalog/Model/ResourceModel/BulkSourceUnassign.php +++ b/InventoryCatalog/Model/ResourceModel/BulkSourceUnassign.php @@ -10,6 +10,7 @@ use Magento\Framework\App\ResourceConnection; use Magento\Inventory\Model\ResourceModel\SourceItem; use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; /** * Implementation of bulk source assignment @@ -24,14 +25,30 @@ class BulkSourceUnassign */ private $resourceConnection; + /** + * @var BulkZeroLegacyStockItem + */ + private $bulkZeroLegacyStockItem; + + /** + * @var DefaultSourceProviderInterface + */ + private $defaultSourceProvider; + /** * @param ResourceConnection $resourceConnection + * @param DefaultSourceProviderInterface $defaultSourceProvider + * @param BulkZeroLegacyStockItem $bulkZeroLegacyStockItem * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( - ResourceConnection $resourceConnection + ResourceConnection $resourceConnection, + DefaultSourceProviderInterface $defaultSourceProvider, + BulkZeroLegacyStockItem $bulkZeroLegacyStockItem ) { $this->resourceConnection = $resourceConnection; + $this->bulkZeroLegacyStockItem = $bulkZeroLegacyStockItem; + $this->defaultSourceProvider = $defaultSourceProvider; } /** @@ -39,17 +56,27 @@ public function __construct( * @param array $skus * @param array $sourceCodes * @return int + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function execute(array $skus, array $sourceCodes): int { $connection = $this->resourceConnection->getConnection(); $tableName = $this->resourceConnection->getTableName(SourceItem::TABLE_NAME_SOURCE_ITEM); + $connection->beginTransaction(); + $count = (int) $connection->delete($tableName, [ SourceItemInterface::SOURCE_CODE . ' IN (?)' => $sourceCodes, SourceItemInterface::SKU . ' IN (?)' => $skus, ]); + // Legacy stock update + if (in_array($this->defaultSourceProvider->getCode(), $sourceCodes)) { + $this->bulkZeroLegacyStockItem->execute($skus); + } + + $connection->commit(); + return $count; } } diff --git a/InventoryLegacySynchronization/Model/ResourceModel/SetZeroQuantityToLegacyStockItems.php b/InventoryCatalog/Model/ResourceModel/BulkZeroLegacyStockItem.php similarity index 91% rename from InventoryLegacySynchronization/Model/ResourceModel/SetZeroQuantityToLegacyStockItems.php rename to InventoryCatalog/Model/ResourceModel/BulkZeroLegacyStockItem.php index e525e574e0d4..ad2d99609061 100644 --- a/InventoryLegacySynchronization/Model/ResourceModel/SetZeroQuantityToLegacyStockItems.php +++ b/InventoryCatalog/Model/ResourceModel/BulkZeroLegacyStockItem.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryLegacySynchronization\Model\ResourceModel; +namespace Magento\InventoryCatalog\Model\ResourceModel; use Magento\CatalogInventory\Api\Data\StockItemInterface; use Magento\Framework\App\ResourceConnection; @@ -14,7 +14,7 @@ /** * Set quantity=0 to legacy cataloginventory_stock_item table for a set of skus via plain MySql query */ -class SetZeroQuantityToLegacyStockItems +class BulkZeroLegacyStockItem { /** * @var ResourceConnection @@ -43,7 +43,7 @@ public function __construct( * @return void * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function execute(array $skus): void + public function execute(array $skus) { $productIds = array_values($this->getProductIdsBySkus->execute($skus)); diff --git a/InventoryCatalog/Model/ResourceModel/SetDataToLegacyStockItem.php b/InventoryCatalog/Model/ResourceModel/SetDataToLegacyStockItem.php new file mode 100644 index 000000000000..fe256204ce73 --- /dev/null +++ b/InventoryCatalog/Model/ResourceModel/SetDataToLegacyStockItem.php @@ -0,0 +1,68 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalog\Model\ResourceModel; + +use Magento\CatalogInventory\Api\Data\StockItemInterface; +use Magento\Framework\App\ResourceConnection; +use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface; + +/** + * Set data to legacy cataloginventory_stock_item table via plain MySql query + */ +class SetDataToLegacyStockItem +{ + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @var GetProductIdsBySkusInterface + */ + private $getProductIdsBySkus; + + /** + * @param ResourceConnection $resourceConnection + * @param GetProductIdsBySkusInterface $getProductIdsBySkus + */ + public function __construct( + ResourceConnection $resourceConnection, + GetProductIdsBySkusInterface $getProductIdsBySkus + ) { + $this->resourceConnection = $resourceConnection; + $this->getProductIdsBySkus = $getProductIdsBySkus; + } + + /** + * @param string $sku + * @param float $quantity + * @param int $status + * @return void + */ + public function execute(string $sku, float $quantity, int $status) + { + $productIds = $this->getProductIdsBySkus->execute([$sku]); + + if (isset($productIds[$sku])) { + $productId = $productIds[$sku]; + + $connection = $this->resourceConnection->getConnection(); + $connection->update( + $this->resourceConnection->getTableName('cataloginventory_stock_item'), + [ + StockItemInterface::QTY => $quantity, + StockItemInterface::IS_IN_STOCK => $status, + ], + [ + StockItemInterface::PRODUCT_ID . ' = ?' => $productId, + 'website_id = ?' => 0, + ] + ); + } + } +} diff --git a/InventoryCatalog/Model/ResourceModel/TransferInventoryPartially.php b/InventoryCatalog/Model/ResourceModel/TransferInventoryPartially.php index cc017f2268f3..de7582f593d7 100644 --- a/InventoryCatalog/Model/ResourceModel/TransferInventoryPartially.php +++ b/InventoryCatalog/Model/ResourceModel/TransferInventoryPartially.php @@ -8,22 +8,33 @@ use Magento\Framework\App\ResourceConnection; use Magento\Inventory\Model\ResourceModel\SourceItem; use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferItemInterface; class TransferInventoryPartially { - /** - * @var ResourceConnection - */ + /** @var ResourceConnection */ private $resourceConnection; + /** @var DefaultSourceProviderInterface */ + private $defaultSourceProvider; + + /** @var SetDataToLegacyStockItem */ + private $setDataToLegacyStockItemCommand; + /** * @param ResourceConnection $resourceConnection + * @param DefaultSourceProviderInterface $defaultSourceProvider + * @param SetDataToLegacyStockItem $setDataToLegacyCatalogInventoryCommand */ public function __construct( - ResourceConnection $resourceConnection + ResourceConnection $resourceConnection, + DefaultSourceProviderInterface $defaultSourceProvider, + SetDataToLegacyStockItem $setDataToLegacyCatalogInventoryCommand ) { $this->resourceConnection = $resourceConnection; + $this->defaultSourceProvider = $defaultSourceProvider; + $this->setDataToLegacyStockItemCommand = $setDataToLegacyCatalogInventoryCommand; } /** @@ -31,11 +42,8 @@ public function __construct( * @param string $originSourceCode * @param string $destinationSourceCode */ - public function execute( - PartialInventoryTransferItemInterface $transfer, - string $originSourceCode, - string $destinationSourceCode - ): void { + public function execute(PartialInventoryTransferItemInterface $transfer, string $originSourceCode, string $destinationSourceCode): void + { $tableName = $this->resourceConnection->getTableName(SourceItem::TABLE_NAME_SOURCE_ITEM); $connection = $this->resourceConnection->getConnection(); $connection->beginTransaction(); @@ -43,20 +51,11 @@ public function execute( $originSourceItemData = $this->getSourceItemData($transfer->getSku(), $originSourceCode); $destSourceItemData = $this->getSourceItemData($transfer->getSku(), $destinationSourceCode); - $updatedQtyAtOrigin = $originSourceItemData === null ? - 0.0 : - (float) $originSourceItemData[SourceItemInterface::QUANTITY] - $transfer->getQty(); - $updatedQtyAtDest = $destSourceItemData === null ? - 0.0 : - (float) $destSourceItemData[SourceItemInterface::QUANTITY] + $transfer->getQty(); - - $originUpdate = [ - SourceItemInterface::QUANTITY => $updatedQtyAtOrigin - ]; - $destUpdate = [ - SourceItemInterface::QUANTITY => $updatedQtyAtDest, - SourceItemInterface::STATUS => SourceItemInterface::STATUS_IN_STOCK - ]; + $updatedQtyAtOrigin = $originSourceItemData === null ? 0.0 : (float) $originSourceItemData[SourceItemInterface::QUANTITY] - $transfer->getQty(); + $updatedQtyAtDest = $destSourceItemData === null ? 0.0 : (float) $destSourceItemData[SourceItemInterface::QUANTITY] + $transfer->getQty(); + + $originUpdate = [SourceItemInterface::QUANTITY => $updatedQtyAtOrigin]; + $destUpdate = [SourceItemInterface::QUANTITY => $updatedQtyAtDest, SourceItemInterface::STATUS => SourceItemInterface::STATUS_IN_STOCK]; $connection->update($tableName, $originUpdate, [ SourceItemInterface::SOURCE_CODE . '=?' => $originSourceCode, @@ -67,6 +66,12 @@ public function execute( SourceItemInterface::SKU . '=?' => $transfer->getSku(), ]); + if ($originSourceCode === $this->defaultSourceProvider->getCode()) { + $this->setDataToLegacyStockItemCommand->execute($transfer->getSku(), $updatedQtyAtOrigin, $originSourceItemData[SourceItemInterface::STATUS]); + } elseif ($destinationSourceCode === $this->defaultSourceProvider->getCode()) { + $this->setDataToLegacyStockItemCommand->execute($transfer->getSku(), $updatedQtyAtDest, SourceItemInterface::STATUS_IN_STOCK); + } + $connection->commit(); } diff --git a/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php b/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php new file mode 100644 index 000000000000..90ccd780947a --- /dev/null +++ b/InventoryCatalog/Model/SourceItemsSaveSynchronization/SetDataToLegacyCatalogInventory.php @@ -0,0 +1,146 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalog\Model\SourceItemsSaveSynchronization; + +use Magento\CatalogInventory\Api\Data\StockItemInterface; +use Magento\CatalogInventory\Api\StockItemRepositoryInterface; +use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory; +use Magento\CatalogInventory\Model\Indexer\Stock\Processor; +use Magento\CatalogInventory\Model\Spi\StockStateProviderInterface; +use Magento\CatalogInventory\Model\Stock; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface; +use Magento\InventoryCatalog\Model\ResourceModel\SetDataToLegacyStockItem; +use Magento\InventoryCatalogApi\Model\SourceItemsSaveSynchronizationInterface; + +/** + * Set Qty and status for legacy CatalogInventory Stock Item table + */ +class SetDataToLegacyCatalogInventory +{ + /** + * @var SetDataToLegacyStockItem + */ + private $setDataToLegacyStockItem; + + /** + * @var StockItemCriteriaInterfaceFactory + */ + private $legacyStockItemCriteriaFactory; + + /** + * @var StockItemRepositoryInterface + */ + private $legacyStockItemRepository; + + /** + * @var GetProductIdsBySkusInterface + */ + private $getProductIdsBySkus; + + /** + * @var StockStateProviderInterface + */ + private $stockStateProvider; + + /** + * @var Processor + */ + private $indexerProcessor; + + /** + * @param SetDataToLegacyStockItem $setDataToLegacyStockItem + * @param StockItemCriteriaInterfaceFactory $legacyStockItemCriteriaFactory + * @param StockItemRepositoryInterface $legacyStockItemRepository + * @param GetProductIdsBySkusInterface $getProductIdsBySkus + * @param StockStateProviderInterface $stockStateProvider + * @param Processor $indexerProcessor + */ + public function __construct( + SetDataToLegacyStockItem $setDataToLegacyStockItem, + StockItemCriteriaInterfaceFactory $legacyStockItemCriteriaFactory, + StockItemRepositoryInterface $legacyStockItemRepository, + GetProductIdsBySkusInterface $getProductIdsBySkus, + StockStateProviderInterface $stockStateProvider, + Processor $indexerProcessor + ) { + $this->setDataToLegacyStockItem = $setDataToLegacyStockItem; + $this->legacyStockItemCriteriaFactory = $legacyStockItemCriteriaFactory; + $this->legacyStockItemRepository = $legacyStockItemRepository; + $this->getProductIdsBySkus = $getProductIdsBySkus; + $this->stockStateProvider = $stockStateProvider; + $this->indexerProcessor = $indexerProcessor; + } + + /** + * @param array $sourceItems + * @return void + */ + public function execute(array $sourceItems): void + { + $productIds = []; + foreach ($sourceItems as $sourceItem) { + $sku = $sourceItem->getSku(); + + try { + $productId = (int)$this->getProductIdsBySkus->execute([$sku])[$sku]; + } catch (NoSuchEntityException $e) { + // Skip synchronization of for not existed product + continue; + } + + $legacyStockItem = $this->getLegacyStockItem($productId); + if (null === $legacyStockItem) { + continue; + } + + $isInStock = (int)$sourceItem->getStatus(); + + if ($legacyStockItem->getManageStock()) { + $legacyStockItem->setIsInStock($isInStock); + $legacyStockItem->setQty((float)$sourceItem->getQuantity()); + + if (false === $this->stockStateProvider->verifyStock($legacyStockItem)) { + $isInStock = 0; + } + } + + $this->setDataToLegacyStockItem->execute( + (string)$sourceItem->getSku(), + (float)$sourceItem->getQuantity(), + $isInStock + ); + $productIds[] = $productId; + } + + if ($productIds) { + $this->indexerProcessor->reindexList($productIds); + } + } + + /** + * @param int $productId + * @return null|StockItemInterface + */ + private function getLegacyStockItem(int $productId): ?StockItemInterface + { + $searchCriteria = $this->legacyStockItemCriteriaFactory->create(); + + $searchCriteria->addFilter(StockItemInterface::PRODUCT_ID, StockItemInterface::PRODUCT_ID, $productId); + $searchCriteria->addFilter(StockItemInterface::STOCK_ID, StockItemInterface::STOCK_ID, Stock::DEFAULT_STOCK_ID); + + $stockItemCollection = $this->legacyStockItemRepository->getList($searchCriteria); + if ($stockItemCollection->getTotalCount() === 0) { + return null; + } + + $stockItems = $stockItemCollection->getItems(); + $stockItem = reset($stockItems); + return $stockItem; + } +} diff --git a/InventoryCatalog/Model/UpdateSourceItemBasedOnLegacyStockItem.php b/InventoryCatalog/Model/UpdateSourceItemBasedOnLegacyStockItem.php new file mode 100644 index 000000000000..a90c60298eac --- /dev/null +++ b/InventoryCatalog/Model/UpdateSourceItemBasedOnLegacyStockItem.php @@ -0,0 +1,91 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryCatalog\Model; + +use Magento\CatalogInventory\Model\Stock\Item; +use Magento\InventoryApi\Api\Data\SourceItemInterface; +use Magento\InventoryApi\Api\Data\SourceItemInterfaceFactory; +use Magento\InventoryApi\Api\SourceItemsSaveInterface; +use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; +use Magento\InventoryCatalogApi\Model\GetSkusByProductIdsInterface; + +class UpdateSourceItemBasedOnLegacyStockItem +{ + /** + * @var SourceItemInterfaceFactory + */ + private $sourceItemFactory; + + /** + * @var SourceItemsSaveInterface + */ + private $sourceItemsSave; + + /** + * @var DefaultSourceProviderInterface + */ + private $defaultSourceProvider; + + /** + * @var GetSkusByProductIdsInterface + */ + private $getSkusByProductIds; + + /** + * @var GetDefaultSourceItemBySku + */ + private $getDefaultSourceItemBySku; + + /** + * @param SourceItemInterfaceFactory $sourceItemFactory + * @param SourceItemsSaveInterface $sourceItemsSave + * @param DefaultSourceProviderInterface $defaultSourceProvider + * @param GetSkusByProductIdsInterface $getSkusByProductIds + * @param GetDefaultSourceItemBySku $getDefaultSourceItemBySku + * @SuppressWarnings(PHPMD.LongVariable) + */ + public function __construct( + SourceItemInterfaceFactory $sourceItemFactory, + SourceItemsSaveInterface $sourceItemsSave, + DefaultSourceProviderInterface $defaultSourceProvider, + GetSkusByProductIdsInterface $getSkusByProductIds, + GetDefaultSourceItemBySku $getDefaultSourceItemBySku + ) { + $this->sourceItemFactory = $sourceItemFactory; + $this->sourceItemsSave = $sourceItemsSave; + $this->getSkusByProductIds = $getSkusByProductIds; + $this->getDefaultSourceItemBySku = $getDefaultSourceItemBySku; + $this->defaultSourceProvider = $defaultSourceProvider; + } + + /** + * @param Item $legacyStockItem + * @return void + * @throws \Magento\Framework\Exception\CouldNotSaveException + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Validation\ValidationException + */ + public function execute(Item $legacyStockItem) + { + $productSku = $this->getSkusByProductIds + ->execute([$legacyStockItem->getProductId()])[$legacyStockItem->getProductId()]; + + $sourceItem = $this->getDefaultSourceItemBySku->execute($productSku); + if ($sourceItem === null) { + /** @var SourceItemInterface $sourceItem */ + $sourceItem = $this->sourceItemFactory->create(); + $sourceItem->setSourceCode($this->defaultSourceProvider->getCode()); + $sourceItem->setSku($productSku); + } + + $sourceItem->setQuantity((float)$legacyStockItem->getQty()); + $sourceItem->setStatus((int)$legacyStockItem->getIsInStock()); + + $this->sourceItemsSave->execute([$sourceItem]); + } +} diff --git a/InventoryLegacySynchronization/Plugin/UpdateSourceItemAtLegacyQtyCounterPlugin.php b/InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyQtyCounterPlugin.php similarity index 90% rename from InventoryLegacySynchronization/Plugin/UpdateSourceItemAtLegacyQtyCounterPlugin.php rename to InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyQtyCounterPlugin.php index 4149ae51fa9f..9cd4062709e3 100644 --- a/InventoryLegacySynchronization/Plugin/UpdateSourceItemAtLegacyQtyCounterPlugin.php +++ b/InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyQtyCounterPlugin.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryLegacySynchronization\Plugin; +namespace Magento\InventoryCatalog\Plugin\CatalogInventory; use Magento\CatalogInventory\Model\ResourceModel\QtyCounterInterface; use Magento\Framework\App\ResourceConnection; @@ -59,7 +59,6 @@ class UpdateSourceItemAtLegacyQtyCounterPlugin * @param SearchCriteriaBuilder $searchCriteriaBuilder * @param ResourceConnection $resourceConnection * @param GetSkusByProductIdsInterface $getSkusByProductIds - * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( SourceItemRepositoryInterface $sourceItemRepository, @@ -116,11 +115,6 @@ public function aroundCorrectItemsQty( * @param int[] $productQuantitiesByProductId * @param string $operator * @return void - * @throws \Magento\Framework\Exception\CouldNotSaveException - * @throws \Magento\Framework\Exception\InputException - * @throws \Magento\Framework\Validation\ValidationException - * @throws \Magento\Framework\Exception\NoSuchEntityException - * @SuppressWarnings(PHPMD.LongVariable) */ private function updateSourceItemAtLegacyCatalogInventoryQtyCounter( array $productQuantitiesByProductId, @@ -149,8 +143,6 @@ private function updateSourceItemAtLegacyCatalogInventoryQtyCounter( /** * @param int[] $productQuantitiesByProductId * @return array - * @throws \Magento\Framework\Exception\NoSuchEntityException - * @SuppressWarnings(PHPMD.LongVariable) */ private function getProductQuantitiesBySku(array $productQuantitiesByProductId): array { diff --git a/InventoryLegacySynchronization/Plugin/UpdateSourceItemAtLegacyStockItemSavePlugin.php b/InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyStockItemSavePlugin.php similarity index 80% rename from InventoryLegacySynchronization/Plugin/UpdateSourceItemAtLegacyStockItemSavePlugin.php rename to InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyStockItemSavePlugin.php index 1bc170a3d0ef..a9de68821e3e 100755 --- a/InventoryLegacySynchronization/Plugin/UpdateSourceItemAtLegacyStockItemSavePlugin.php +++ b/InventoryCatalog/Plugin/CatalogInventory/UpdateSourceItemAtLegacyStockItemSavePlugin.php @@ -5,16 +5,16 @@ */ declare(strict_types=1); -namespace Magento\InventoryLegacySynchronization\Plugin; +namespace Magento\InventoryCatalog\Plugin\CatalogInventory; use Magento\CatalogInventory\Model\ResourceModel\Stock\Item as ItemResourceModel; use Magento\CatalogInventory\Model\Stock\Item; use Magento\Framework\App\ResourceConnection; use Magento\Framework\Model\AbstractModel; use Magento\InventoryCatalog\Model\GetDefaultSourceItemBySku; -use Magento\InventoryLegacySynchronization\Model\Synchronize; use Magento\InventoryCatalogApi\Model\GetProductTypesBySkusInterface; use Magento\InventoryCatalogApi\Model\GetSkusByProductIdsInterface; +use Magento\InventoryCatalog\Model\UpdateSourceItemBasedOnLegacyStockItem; use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForProductTypeInterface; /** @@ -33,6 +33,11 @@ class UpdateSourceItemAtLegacyStockItemSavePlugin */ private $isSourceItemManagementAllowedForProductType; + /** + * @var UpdateSourceItemBasedOnLegacyStockItem + */ + private $updateSourceItemBasedOnLegacyStockItem; + /** * @var GetProductTypesBySkusInterface */ @@ -49,32 +54,27 @@ class UpdateSourceItemAtLegacyStockItemSavePlugin private $getDefaultSourceItemBySku; /** - * @var Synchronize - */ - private $synchronize; - - /** + * @param UpdateSourceItemBasedOnLegacyStockItem $updateSourceItemBasedOnLegacyStockItem * @param ResourceConnection $resourceConnection * @param IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemManagementAllowedForProductType * @param GetProductTypesBySkusInterface $getProductTypeBySku * @param GetSkusByProductIdsInterface $getSkusByProductIds * @param GetDefaultSourceItemBySku $getDefaultSourceItemBySku - * @param Synchronize $synchronize */ public function __construct( + UpdateSourceItemBasedOnLegacyStockItem $updateSourceItemBasedOnLegacyStockItem, ResourceConnection $resourceConnection, IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemManagementAllowedForProductType, GetProductTypesBySkusInterface $getProductTypeBySku, GetSkusByProductIdsInterface $getSkusByProductIds, - GetDefaultSourceItemBySku $getDefaultSourceItemBySku, - Synchronize $synchronize + GetDefaultSourceItemBySku $getDefaultSourceItemBySku ) { + $this->updateSourceItemBasedOnLegacyStockItem = $updateSourceItemBasedOnLegacyStockItem; $this->resourceConnection = $resourceConnection; $this->isSourceItemManagementAllowedForProductType = $isSourceItemManagementAllowedForProductType; $this->getProductTypeBySku = $getProductTypeBySku; $this->getSkusByProductIds = $getSkusByProductIds; $this->getDefaultSourceItemBySku = $getDefaultSourceItemBySku; - $this->synchronize = $synchronize; } /** @@ -97,12 +97,7 @@ public function aroundSave(ItemResourceModel $subject, callable $proceed, Abstra $typeId = $this->getTypeId($legacyStockItem); if ($this->isSourceItemManagementAllowedForProductType->execute($typeId)) { if ($this->shouldAlignDefaultSourceWithLegacy($legacyStockItem)) { - $this->synchronize->execute( - Synchronize::LEGACY_TO_MSI, - [ - $legacyStockItem->getData() - ] - ); + $this->updateSourceItemBasedOnLegacyStockItem->execute($legacyStockItem); } } @@ -115,26 +110,16 @@ public function aroundSave(ItemResourceModel $subject, callable $proceed, Abstra } } - /** - * @param int $productId - * @return string - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ - private function getProductSkuById(int $productId): string - { - return $this->getSkusByProductIds - ->execute([$productId])[$productId]; - } - /** * Return true if legacy stock item should update default source (if existing) * @param Item $legacyStockItem * @return bool - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws \Magento\Framework\Exception\InputException */ private function shouldAlignDefaultSourceWithLegacy(Item $legacyStockItem): bool { - $productSku = $this->getProductSkuById((int) $legacyStockItem->getProductId()); + $productSku = $this->getSkusByProductIds + ->execute([$legacyStockItem->getProductId()])[$legacyStockItem->getProductId()]; $result = $legacyStockItem->getIsInStock() || ((float) $legacyStockItem->getQty() !== (float) 0) || @@ -146,7 +131,7 @@ private function shouldAlignDefaultSourceWithLegacy(Item $legacyStockItem): bool /** * @param Item $legacyStockItem * @return string - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws \Magento\Framework\Exception\InputException */ private function getTypeId(Item $legacyStockItem): string { diff --git a/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php b/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php similarity index 68% rename from InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php rename to InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php index 23927a62a7df..7132ed3da850 100644 --- a/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php +++ b/InventoryCatalog/Plugin/InventoryApi/SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin.php @@ -5,11 +5,11 @@ */ declare(strict_types=1); -namespace Magento\InventoryLegacySynchronization\Plugin; +namespace Magento\InventoryCatalog\Plugin\InventoryApi; use Magento\InventoryApi\Api\Data\SourceItemInterface; use Magento\InventoryApi\Api\SourceItemsSaveInterface; -use Magento\InventoryLegacySynchronization\Model\Synchronize; +use Magento\InventoryCatalog\Model\SourceItemsSaveSynchronization\SetDataToLegacyCatalogInventory; use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; use Magento\InventoryCatalogApi\Model\GetProductTypesBySkusInterface; use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForProductTypeInterface; @@ -35,28 +35,26 @@ class SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin private $getProductTypeBySku; /** - * @var Synchronize|null + * @var SetDataToLegacyCatalogInventory */ - private $synchronize; + private $setDataToLegacyCatalogInventory; /** * @param DefaultSourceProviderInterface $defaultSourceProvider * @param IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemsAllowedForProductType * @param GetProductTypesBySkusInterface $getProductTypeBySku - * @param Synchronize|null $synchronize - * @SuppressWarnings(PHPMD.LongVariable) - * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @param SetDataToLegacyCatalogInventory $setDataToLegacyCatalogInventory */ public function __construct( DefaultSourceProviderInterface $defaultSourceProvider, IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemsAllowedForProductType, GetProductTypesBySkusInterface $getProductTypeBySku, - Synchronize $synchronize + SetDataToLegacyCatalogInventory $setDataToLegacyCatalogInventory ) { $this->defaultSourceProvider = $defaultSourceProvider; $this->isSourceItemsAllowedForProductType = $isSourceItemsAllowedForProductType; $this->getProductTypeBySku = $getProductTypeBySku; - $this->synchronize = $synchronize; + $this->setDataToLegacyCatalogInventory = $setDataToLegacyCatalogInventory; } /** @@ -65,38 +63,31 @@ public function __construct( * @param SourceItemInterface[] $sourceItems * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * @throws \Magento\Framework\Exception\LocalizedException */ public function afterExecute(SourceItemsSaveInterface $subject, $result, array $sourceItems): void { - $sourceItemsData = []; - $skus = []; + $sourceItemsForSynchronization = []; foreach ($sourceItems as $sourceItem) { - $skus[] = $sourceItem->getSku(); - } - - $productTypes = $this->getProductTypeBySku->execute($skus); - $defaultSourceCode = $this->defaultSourceProvider->getCode(); - - foreach ($sourceItems as $sourceItem) { - if ($sourceItem->getSourceCode() !== $defaultSourceCode) { + if ($sourceItem->getSourceCode() !== $this->defaultSourceProvider->getCode()) { continue; } $sku = $sourceItem->getSku(); - if (!isset($productTypes[$sku])) { + + $productTypes = $this->getProductTypeBySku->execute([$sku]); + if (isset($productTypes[$sku])) { + $typeId = $productTypes[$sku]; + } else { continue; } - $typeId = $productTypes[$sku]; - if (false === $this->isSourceItemsAllowedForProductType->execute($typeId)) { continue; } - $sourceItemsData[] = $sourceItem->getData(); + $sourceItemsForSynchronization[] = $sourceItem; } - $this->synchronize->execute(Synchronize::MSI_TO_LEGACY, $sourceItemsData); + $this->setDataToLegacyCatalogInventory->execute($sourceItemsForSynchronization); } } diff --git a/InventoryLegacySynchronization/Plugin/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php b/InventoryCatalog/Plugin/InventoryApi/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php similarity index 61% rename from InventoryLegacySynchronization/Plugin/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php rename to InventoryCatalog/Plugin/InventoryApi/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php index caedbf36ccdf..cda7741d1c62 100644 --- a/InventoryLegacySynchronization/Plugin/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php +++ b/InventoryCatalog/Plugin/InventoryApi/SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin.php @@ -5,14 +5,17 @@ */ declare(strict_types=1); -namespace Magento\InventoryLegacySynchronization\Plugin; +namespace Magento\InventoryCatalog\Plugin\InventoryApi; +use Magento\CatalogInventory\Model\Indexer\Stock\Processor; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\InventoryApi\Api\Data\SourceItemInterface; use Magento\InventoryApi\Api\SourceItemsDeleteInterface; use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; +use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface; use Magento\InventoryCatalogApi\Model\GetProductTypesBySkusInterface; +use Magento\InventoryCatalog\Model\ResourceModel\SetDataToLegacyStockItem; use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForProductTypeInterface; -use Magento\InventoryLegacySynchronization\Model\Synchronize; /** * Set to zero Qty and status to ‘Out of Stock’ for legacy CatalogInventory Stock Status and Stock Item DB tables, @@ -25,6 +28,21 @@ class SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin */ private $defaultSourceProvider; + /** + * @var SetDataToLegacyStockItem + */ + private $setDataToLegacyStockItem; + + /** + * @var GetProductIdsBySkusInterface + */ + private $getProductIdsBySkus; + + /** + * @var Processor + */ + private $indexerProcessor; + /** * @var IsSourceItemManagementAllowedForProductTypeInterface */ @@ -35,28 +53,28 @@ class SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin */ private $getProductTypeBySku; - /** - * @var Synchronize - */ - private $synchronize; - /** * @param DefaultSourceProviderInterface $defaultSourceProvider + * @param SetDataToLegacyStockItem $setDataToLegacyStockItem + * @param GetProductIdsBySkusInterface $getProductIdsBySkus + * @param Processor $indexerProcessor * @param IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemsAllowedForProductType * @param GetProductTypesBySkusInterface $getProductTypeBySku - * @param Synchronize $synchronize - * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( DefaultSourceProviderInterface $defaultSourceProvider, + SetDataToLegacyStockItem $setDataToLegacyStockItem, + GetProductIdsBySkusInterface $getProductIdsBySkus, + Processor $indexerProcessor, IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemsAllowedForProductType, - GetProductTypesBySkusInterface $getProductTypeBySku, - Synchronize $synchronize + GetProductTypesBySkusInterface $getProductTypeBySku ) { $this->defaultSourceProvider = $defaultSourceProvider; + $this->setDataToLegacyStockItem = $setDataToLegacyStockItem; + $this->getProductIdsBySkus = $getProductIdsBySkus; + $this->indexerProcessor = $indexerProcessor; $this->isSourceItemsAllowedForProductType = $isSourceItemsAllowedForProductType; $this->getProductTypeBySku = $getProductTypeBySku; - $this->synchronize = $synchronize; } /** @@ -64,12 +82,11 @@ public function __construct( * @param void $result * @param SourceItemInterface[] $sourceItems * @return void - * @throws \Magento\Framework\Exception\LocalizedException * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function afterExecute(SourceItemsDeleteInterface $subject, $result, array $sourceItems) { - $sourceItemsData = []; + $productIds = []; foreach ($sourceItems as $sourceItem) { if ($sourceItem->getSourceCode() !== $this->defaultSourceProvider->getCode()) { continue; @@ -77,20 +94,24 @@ public function afterExecute(SourceItemsDeleteInterface $subject, $result, array $sku = $sourceItem->getSku(); + try { + $productId = (int)$this->getProductIdsBySkus->execute([$sku])[$sku]; + } catch (NoSuchEntityException $e) { + // Delete source item data for not existed product + continue; + } + $typeId = $this->getProductTypeBySku->execute([$sku])[$sku]; if (false === $this->isSourceItemsAllowedForProductType->execute($typeId)) { continue; } - $sourceItemData = $sourceItem->getData(); - $sourceItemData[SourceItemInterface::STATUS] = SourceItemInterface::STATUS_OUT_OF_STOCK; - $sourceItemData[SourceItemInterface::QUANTITY] = 0; - - $sourceItemsData[] = $sourceItemData; + $this->setDataToLegacyStockItem->execute($sourceItem->getSku(), 0, 0); + $productIds[] = $productId; } - if (!empty($sourceItemsData)) { - $this->synchronize->execute(Synchronize::MSI_TO_LEGACY, $sourceItemsData); + if ($productIds) { + $this->indexerProcessor->reindexList($productIds); } } } diff --git a/InventoryLegacySynchronization/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php b/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php similarity index 57% rename from InventoryLegacySynchronization/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php rename to InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php index 80306d1d2701..8cf028ca56cc 100644 --- a/InventoryLegacySynchronization/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php +++ b/InventoryCatalog/Test/Integration/SetDataToLegacyStockItemAtSourceItemsSaveTest.php @@ -5,12 +5,10 @@ */ declare(strict_types=1); -namespace Magento\InventoryLegacySynchronization\Test\Integration; +namespace Magento\InventoryCatalog\Test\Integration; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\MessageQueue\ConsumerFactory; -use Magento\Framework\MessageQueue\ConsumerInterface; use Magento\InventoryApi\Api\Data\SourceItemInterface; use Magento\InventoryApi\Api\SourceItemRepositoryInterface; use Magento\InventoryApi\Api\SourceItemsSaveInterface; @@ -58,11 +56,6 @@ class SetDataToLegacyStockItemAtSourceItemsSaveTest extends TestCase */ private $defaultSourceProvider; - /** - * @var ConsumerInterface - */ - private $consumer; - protected function setUp() { $this->productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); @@ -77,63 +70,14 @@ protected function setUp() $this->sourceItemsSave = Bootstrap::getObjectManager()->get(SourceItemsSaveInterface::class); $this->defaultSourceProvider = Bootstrap::getObjectManager()->get(DefaultSourceProviderInterface::class); - - $this->consumer = Bootstrap::getObjectManager()->create(ConsumerFactory::class) - ->get('legacyInventorySynchronization', 100); - } - - /** - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php - * @SuppressWarnings(PHPMD.LongVariable) - */ - public function testShouldSynchronizeLegacyStock(): void - { - $productSku = 'SKU-1'; - $product = $this->productRepository->get($productSku); - $productId = $product->getId(); - $websiteId = 0; - - /** @var StockItemCriteriaInterface $legacyStockItemCriteria */ - $legacyStockItemCriteria = $this->legacyStockItemCriteriaFactory->create(); - $legacyStockItemCriteria->setProductsFilter($productId); - $legacyStockItemCriteria->setScopeFilter($websiteId); - $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); - self::assertCount(1, $legacyStockItems); - - $legacyStockItem = reset($legacyStockItems); - self::assertTrue($legacyStockItem->getIsInStock()); - self::assertEquals(5.5, $legacyStockItem->getQty()); - - $searchCriteria = $this->searchCriteriaBuilder - ->addFilter(SourceItemInterface::SKU, $productSku) - ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) - ->create(); - $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); - self::assertCount(1, $sourceItems); - - $sourceItem = reset($sourceItems); - $sourceItem->setQuantity(20.0); - $sourceItem->setStatus(SourceItemInterface::STATUS_OUT_OF_STOCK); - $this->sourceItemsSave->execute($sourceItems); - - $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); - self::assertCount(1, $legacyStockItems); - - $legacyStockItem = current($legacyStockItems); - self::assertFalse($legacyStockItem->getIsInStock()); - self::assertEquals(20, $legacyStockItem->getQty()); } /** * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php - * @magentoAdminConfigFixture cataloginventory/legacy_stock/async 1 - * @SuppressWarnings(PHPMD.LongVariable) */ - public function testShouldSynchronizeLegacyStockAsynchronously(): void + public function testSetData() { $productSku = 'SKU-1'; $product = $this->productRepository->get($productSku); @@ -159,21 +103,13 @@ public function testShouldSynchronizeLegacyStockAsynchronously(): void self::assertCount(1, $sourceItems); $sourceItem = reset($sourceItems); - $sourceItem->setQuantity(20.0); + $sourceItem->setQuantity(20); $sourceItem->setStatus(SourceItemInterface::STATUS_OUT_OF_STOCK); $this->sourceItemsSave->execute($sourceItems); - // Make sure we did not yet synchronized it $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); - $legacyStockItem = current($legacyStockItems); self::assertCount(1, $legacyStockItems); - self::assertTrue($legacyStockItem->getIsInStock()); - self::assertEquals(5.5, $legacyStockItem->getQty()); - - $this->consumer->process(1); - // Check after asynchrnous consumer call - $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); $legacyStockItem = current($legacyStockItems); self::assertFalse($legacyStockItem->getIsInStock()); self::assertEquals(20, $legacyStockItem->getQty()); diff --git a/InventoryLegacySynchronization/Test/Integration/SetDataToLegacyStockStatusAtSourceItemsSaveTest.php b/InventoryCatalog/Test/Integration/SetDataToLegacyStockStatusAtSourceItemsSaveTest.php similarity index 96% rename from InventoryLegacySynchronization/Test/Integration/SetDataToLegacyStockStatusAtSourceItemsSaveTest.php rename to InventoryCatalog/Test/Integration/SetDataToLegacyStockStatusAtSourceItemsSaveTest.php index 44eb375469fa..b3af298aec6d 100644 --- a/InventoryLegacySynchronization/Test/Integration/SetDataToLegacyStockStatusAtSourceItemsSaveTest.php +++ b/InventoryCatalog/Test/Integration/SetDataToLegacyStockStatusAtSourceItemsSaveTest.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryLegacySynchronization\Test\Integration; +namespace Magento\InventoryCatalog\Test\Integration; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\CatalogInventory\Api\StockStatusCriteriaInterface; @@ -78,7 +78,7 @@ protected function setUp() * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php */ - public function testShouldSetDataToLegacyOnSourceItemSave(): void + public function testSetData() { $productSku = 'SKU-1'; $product = $this->productRepository->get($productSku); @@ -104,7 +104,7 @@ public function testShouldSetDataToLegacyOnSourceItemSave(): void self::assertCount(1, $sourceItems); $sourceItem = reset($sourceItems); - $sourceItem->setQuantity(20.0); + $sourceItem->setQuantity(20); $sourceItem->setStatus(SourceItemInterface::STATUS_OUT_OF_STOCK); $this->sourceItemsSave->execute($sourceItems); diff --git a/InventoryLegacySynchronization/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php b/InventoryCatalog/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php similarity index 54% rename from InventoryLegacySynchronization/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php rename to InventoryCatalog/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php index 55fd8392d5fb..443f503c471d 100644 --- a/InventoryLegacySynchronization/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php +++ b/InventoryCatalog/Test/Integration/SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryLegacySynchronization\Test\Integration; +namespace Magento\InventoryCatalog\Test\Integration; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\CatalogInventory\Api\StockStatusCriteriaInterface; @@ -13,8 +13,6 @@ use Magento\CatalogInventory\Api\StockStatusRepositoryInterface; use Magento\CatalogInventory\Model\Stock\Status; use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\MessageQueue\Consumer; -use Magento\Framework\MessageQueue\ConsumerFactory; use Magento\InventoryApi\Api\Data\SourceItemInterface; use Magento\InventoryApi\Api\SourceItemRepositoryInterface; use Magento\InventoryApi\Api\SourceItemsDeleteInterface; @@ -59,11 +57,6 @@ class SetOutOfStockToLegacyStockStatusAtSourceItemsDeleteTest extends TestCase */ private $defaultSourceProvider; - /** - * @var Consumer - */ - private $consumer; - protected function setUp() { $this->productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); @@ -78,9 +71,6 @@ protected function setUp() $this->sourceItemsDelete = Bootstrap::getObjectManager()->get(SourceItemsDeleteInterface::class); $this->defaultSourceProvider = Bootstrap::getObjectManager()->get(DefaultSourceProviderInterface::class); - - $this->consumer = Bootstrap::getObjectManager()->create(ConsumerFactory::class) - ->get('legacyInventorySynchronization', 100); } /** @@ -88,7 +78,7 @@ protected function setUp() * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php */ - public function testShouldSetOutOfStockOnDelete(): void + public function testSetOutOfStock() { $productSku = 'SKU-1'; $product = $this->productRepository->get($productSku); @@ -103,8 +93,8 @@ public function testShouldSetOutOfStockOnDelete(): void self::assertCount(1, $legacyStockStatuses); $legacyStockStatus = reset($legacyStockStatuses); - self::assertSame(Status::STATUS_IN_STOCK, $legacyStockStatus->getStockStatus()); - self::assertSame(5.5, (float) $legacyStockStatus->getQty()); + self::assertEquals(Status::STATUS_IN_STOCK, $legacyStockStatus->getStockStatus()); + self::assertEquals(5.5, $legacyStockStatus->getQty()); $searchCriteria = $this->searchCriteriaBuilder ->addFilter(SourceItemInterface::SKU, $productSku) @@ -119,59 +109,7 @@ public function testShouldSetOutOfStockOnDelete(): void self::assertCount(1, $legacyStockStatuses); $legacyStockStatus = reset($legacyStockStatuses); - self::assertSame(Status::STATUS_OUT_OF_STOCK, $legacyStockStatus->getStockStatus()); - self::assertSame(0.0, (float) $legacyStockStatus->getQty()); - } - - /** - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php - * @magentoAdminConfigFixture cataloginventory/legacy_stock/async 1 - */ - public function testShouldSetOutOfStockOnDeleteAsynchronously(): void - { - $productSku = 'SKU-1'; - $product = $this->productRepository->get($productSku); - $productId = $product->getId(); - $websiteId = 0; - - /** @var StockStatusCriteriaInterface $legacyStockStatusCriteria */ - $legacyStockStatusCriteria = $this->legacyStockStatusCriteriaFactory->create(); - $legacyStockStatusCriteria->setProductsFilter($productId); - $legacyStockStatusCriteria->setScopeFilter($websiteId); - $legacyStockStatuses = $this->legacyStockStatusRepository->getList($legacyStockStatusCriteria)->getItems(); - self::assertCount(1, $legacyStockStatuses); - - $legacyStockStatus = reset($legacyStockStatuses); - self::assertSame(Status::STATUS_IN_STOCK, $legacyStockStatus->getStockStatus()); - self::assertSame(5.5, (float) $legacyStockStatus->getQty()); - - $searchCriteria = $this->searchCriteriaBuilder - ->addFilter(SourceItemInterface::SKU, $productSku) - ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) - ->create(); - $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); - self::assertCount(1, $sourceItems); - - $this->sourceItemsDelete->execute($sourceItems); - - // Before sync - $legacyStockStatuses = $this->legacyStockStatusRepository->getList($legacyStockStatusCriteria)->getItems(); - self::assertCount(1, $legacyStockStatuses); - - $legacyStockStatus = reset($legacyStockStatuses); - self::assertSame(Status::STATUS_IN_STOCK, $legacyStockStatus->getStockStatus()); - self::assertSame(5.5, (float) $legacyStockStatus->getQty()); - - $this->consumer->process(1); - - // After sync - $legacyStockStatuses = $this->legacyStockStatusRepository->getList($legacyStockStatusCriteria)->getItems(); - self::assertCount(1, $legacyStockStatuses); - - $legacyStockStatus = reset($legacyStockStatuses); - self::assertSame(Status::STATUS_OUT_OF_STOCK, $legacyStockStatus->getStockStatus()); - self::assertSame(0.0, (float) $legacyStockStatus->getQty()); + self::assertEquals(Status::STATUS_OUT_OF_STOCK, $legacyStockStatus->getStockStatus()); + self::assertEquals(0, $legacyStockStatus->getQty()); } } diff --git a/InventoryLegacySynchronization/Test/Integration/SetZeroToLegacyStockItemAtSourceItemsDeleteTest.php b/InventoryCatalog/Test/Integration/SetToZeroLegacyStockItemAtSourceItemsDeleteTest.php similarity index 95% rename from InventoryLegacySynchronization/Test/Integration/SetZeroToLegacyStockItemAtSourceItemsDeleteTest.php rename to InventoryCatalog/Test/Integration/SetToZeroLegacyStockItemAtSourceItemsDeleteTest.php index 2f7b30659f67..b42984a2dc9f 100644 --- a/InventoryLegacySynchronization/Test/Integration/SetZeroToLegacyStockItemAtSourceItemsDeleteTest.php +++ b/InventoryCatalog/Test/Integration/SetToZeroLegacyStockItemAtSourceItemsDeleteTest.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryLegacySynchronization\Test\Integration; +namespace Magento\InventoryCatalog\Test\Integration; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Framework\Api\SearchCriteriaBuilder; @@ -19,7 +19,7 @@ use Magento\CatalogInventory\Api\StockItemCriteriaInterface; use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory; -class SetZeroToLegacyStockItemAtSourceItemsDeleteTest extends TestCase +class SetToZeroLegacyStockItemAtSourceItemsDeleteTest extends TestCase { /** * @var ProductRepositoryInterface @@ -77,7 +77,7 @@ protected function setUp() * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php */ - public function testShouldSetLegacyQuantityToZeroOnSourceItemDelete(): void + public function testSetToZero() { $productSku = 'SKU-1'; $product = $this->productRepository->get($productSku); diff --git a/InventoryLegacySynchronization/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php b/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php similarity index 73% rename from InventoryLegacySynchronization/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php rename to InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php index 1fbaa646ac62..4d8c4b21bd70 100644 --- a/InventoryLegacySynchronization/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php +++ b/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtLegacyStockItemSaveTest.php @@ -5,11 +5,9 @@ */ declare(strict_types=1); -namespace Magento\InventoryLegacySynchronization\Test\Integration; +namespace Magento\InventoryCatalog\Test\Integration; use Magento\CatalogInventory\Api\StockRegistryInterface; -use Magento\Framework\MessageQueue\ConsumerFactory; -use Magento\Framework\MessageQueue\ConsumerInterface; use Magento\InventoryCatalog\Model\GetDefaultSourceItemBySku; use PHPUnit\Framework\TestCase; use Magento\TestFramework\Helper\Bootstrap; @@ -26,19 +24,12 @@ class UpdateDefaultSourceItemAtLegacyStockItemSaveTest extends TestCase */ private $getDefaultSourceItemBySku; - /** - * @var ConsumerInterface - */ - private $consumer; - protected function setUp() { parent::setUp(); $this->stockRegistry = Bootstrap::getObjectManager()->create(StockRegistryInterface::class); $this->getDefaultSourceItemBySku = Bootstrap::getObjectManager()->get(GetDefaultSourceItemBySku::class); - $this->consumer = Bootstrap::getObjectManager()->create(ConsumerFactory::class) - ->get('legacyInventorySynchronization', 100); } /** @@ -49,7 +40,7 @@ protected function setUp() * @magentoDbIsolation enabled * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function testSaveLegacyStockItemAssignedToDefaultSource(): void + public function testSaveLegacyStockItemAssignedToDefaultSource() { $stockItem = $this->stockRegistry->getStockItemBySku('SKU-1'); $stockItem->setQty(10); @@ -64,38 +55,6 @@ public function testSaveLegacyStockItemAssignedToDefaultSource(): void ); } - /** - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php - * @magentoAdminConfigFixture cataloginventory/legacy_stock/async 1 - * @magentoDbIsolation enabled - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ - public function testSaveLegacyStockItemAssignedToDefaultSourceAsynchronously(): void - { - $stockItem = $this->stockRegistry->getStockItemBySku('SKU-1'); - $stockItem->setQty(10); - $this->stockRegistry->updateStockItemBySku('SKU-1', $stockItem); - - $defaultSourceItem = $this->getDefaultSourceItemBySku->execute('SKU-1'); - self::assertEquals( - 5.5, - $defaultSourceItem->getQuantity(), - 'Source item was update synchronously even if asynchronous operation was requested' - ); - - $this->consumer->process(1); - - $defaultSourceItem = $this->getDefaultSourceItemBySku->execute('SKU-1'); - self::assertEquals( - 10, - $defaultSourceItem->getQuantity(), - 'Asynchronous source item update failed' - ); - } - /** * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php @@ -104,7 +63,7 @@ public function testSaveLegacyStockItemAssignedToDefaultSourceAsynchronously(): * @magentoDbIsolation enabled * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function testSaveLegacyStockItemNotAssignedToDefaultSource(): void + public function testSaveLegacyStockItemNotAssignedToDefaultSource() { $stockItem = $this->stockRegistry->getStockItemBySku('SKU-2'); $stockItem->setQty(10); @@ -138,7 +97,7 @@ public function testSaveLegacyStockItemNotAssignedToDefaultSource(): void * @magentoDbIsolation enabled * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function testSaveLegacyStockItemWithoutDefaultSourceAssignment(): void + public function testSaveLegacyStockItemWithoutDefaultSourceAssignment() { // SKU-3 is out of stock and not assigned to default source $stockItem = $this->stockRegistry->getStockItemBySku('SKU-3'); diff --git a/InventoryLegacySynchronization/Test/Integration/UpdateDefaultSourceItemAtProductSaveTest.php b/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtProductSaveTest.php similarity index 95% rename from InventoryLegacySynchronization/Test/Integration/UpdateDefaultSourceItemAtProductSaveTest.php rename to InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtProductSaveTest.php index 117488aa65b4..49e4398bcd6f 100644 --- a/InventoryLegacySynchronization/Test/Integration/UpdateDefaultSourceItemAtProductSaveTest.php +++ b/InventoryCatalog/Test/Integration/UpdateDefaultSourceItemAtProductSaveTest.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\InventoryLegacySynchronization\Test\Integration; +namespace Magento\InventoryCatalog\Test\Integration; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\InventoryCatalog\Model\GetDefaultSourceItemBySku; @@ -38,7 +38,7 @@ protected function setUp() * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php * @magentoDbIsolation enabled */ - public function testSaveOutOfStockProductNotAssignedToDefaultSource(): void + public function testSaveOutOfStockProductNotAssignedToDefaultSource() { // SKU-3 is out of stock $product = $this->productRepository->get('SKU-3'); diff --git a/InventoryCatalog/etc/di.xml b/InventoryCatalog/etc/di.xml index f0b40969f677..a6f836c38351 100644 --- a/InventoryCatalog/etc/di.xml +++ b/InventoryCatalog/etc/di.xml @@ -16,6 +16,10 @@ <plugin name="prevent_default_stock_deleting" type="Magento\InventoryCatalog\Plugin\InventoryApi\StockRepository\PreventDeleting\DefaultStockPlugin"/> </type> + <type name="Magento\InventoryApi\Api\SourceItemsSaveInterface"> + <plugin name="set_data_to_legacy_catalog_inventory_at_source_items_save" + type="Magento\InventoryCatalog\Plugin\InventoryApi\SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin"/> + </type> <type name="Magento\InventoryIndexer\Indexer\SourceItem\SourceItemIndexer"> <plugin name="priceIndexUpdater" type="Magento\InventoryCatalog\Plugin\InventoryIndexer\Indexer\SourceItem\PriceIndexUpdater" sortOrder="20"/> </type> @@ -25,7 +29,16 @@ <type name="Magento\CatalogInventory\Model\Indexer\ProductPriceIndexFilter"> <plugin name="change_select_for_price_modifier" type="Magento\InventoryCatalog\Plugin\CatalogInventory\Model\Indexer\ModifySelectInProductPriceIndexFilter"/> </type> + <type name="Magento\InventoryApi\Api\SourceItemsDeleteInterface"> + <plugin name="set_to_zero_legacy_catalog_inventory_at_source_items_delete" + type="Magento\InventoryCatalog\Plugin\InventoryApi\SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin"/> + </type> + <type name="Magento\CatalogInventory\Model\ResourceModel\QtyCounterInterface"> + <plugin name="update_source_item_at_legacy_qty_counter" type="Magento\InventoryCatalog\Plugin\CatalogInventory\UpdateSourceItemAtLegacyQtyCounterPlugin"/> + </type> <type name="Magento\CatalogInventory\Model\ResourceModel\Stock\Item"> + <plugin name="update_source_item_at_legacy_stock_item_save" + type="Magento\InventoryCatalog\Plugin\CatalogInventory\UpdateSourceItemAtLegacyStockItemSavePlugin"/> <plugin name="priceIndexUpdater" disabled="true"/> </type> <type name="Magento\CatalogInventory\Model\ResourceModel\Stock\Status"> @@ -69,6 +82,11 @@ <argument name="tableNameSourceItem" xsi:type="const">Magento\Inventory\Model\ResourceModel\SourceItem::TABLE_NAME_SOURCE_ITEM</argument> </arguments> </type> + <type name="Magento\InventoryCatalog\Model\UpdateSourceItemBasedOnLegacyStockItem"> + <arguments> + <argument name="sourceItemsSave" xsi:type="object">Magento\Inventory\Model\SourceItem\Command\SourceItemsSaveWithoutLegacySynchronization</argument> + </arguments> + </type> <type name="Magento\Inventory\Model\SourceItem\Command\SourceItemsSaveWithoutLegacySynchronization"> <plugin name="set_data_to_legacy_catalog_inventory_at_source_items_save" disabled="true"/> </type> @@ -90,28 +108,6 @@ <preference for="Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferItemInterface" type="Magento\InventoryCatalog\Model\PartialInventoryTransferItem"/> - <type name="Magento\InventoryCatalog\Model\BulkInventoryTransfer"> - <arguments> - <!-- @deprecated parameter defaultSourceProvider has been deprecated and not in use now --> - <argument name="defaultSourceProvider" xsi:type="null" /> - <!-- @deprecated parameter defaultSourceProvider has been deprecated and not in use now --> - <argument name="getProductIdsBySkus" xsi:type="null" /> - <!-- @deprecated parameter legacyIndexer has been deprecated and not in use now --> - <argument name="legacyIndexer" xsi:type="null" /> - </arguments> - </type> - - <type name="Magento\InventoryCatalog\Model\BulkSourceUnassign"> - <arguments> - <!-- @deprecated parameter defaultSourceProvider has been deprecated and not in use now --> - <argument name="defaultSourceProvider" xsi:type="null" /> - <!-- @deprecated parameter defaultSourceProvider has been deprecated and not in use now --> - <argument name="getProductIdsBySkus" xsi:type="null" /> - <!-- @deprecated parameter legacyIndexer has been deprecated and not in use now --> - <argument name="legacyIndexer" xsi:type="null" /> - </arguments> - </type> - <type name="\Magento\InventoryCatalogApi\Model\BulkSourceAssignValidatorChain"> <arguments> <argument name="validators" xsi:type="array"> diff --git a/InventoryCatalogAdminUi/etc/adminhtml/system.xml b/InventoryCatalogAdminUi/etc/adminhtml/system.xml index 61b5ae912cf2..81c8b669f201 100755 --- a/InventoryCatalogAdminUi/etc/adminhtml/system.xml +++ b/InventoryCatalogAdminUi/etc/adminhtml/system.xml @@ -10,16 +10,6 @@ <system> <section id="cataloginventory"> - <group id="legacy_stock" translate="label" type="text" sortOrder="600" showInDefault="1" - showInWebsite="1" showInStore="1"> - <label>Legacy stock alignment</label> - <field id="async" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" - showInStore="0" canRestore="1"> - <label>Run asynchronously</label> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <comment>An asynchronous queue manager must be configured</comment> - </field> - </group> <group id="bulk_operations" translate="label" type="text" sortOrder="600" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Admin bulk operations</label> diff --git a/InventoryLegacySynchronization/LICENSE.txt b/InventoryLegacySynchronization/LICENSE.txt deleted file mode 100644 index 49525fd99da9..000000000000 --- a/InventoryLegacySynchronization/LICENSE.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Open Software License ("OSL") v. 3.0 - -This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Open Software License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/InventoryLegacySynchronization/LICENSE_AFL.txt b/InventoryLegacySynchronization/LICENSE_AFL.txt deleted file mode 100644 index f39d641b18a1..000000000000 --- a/InventoryLegacySynchronization/LICENSE_AFL.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Academic Free License ("AFL") v. 3.0 - -This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Academic Free License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/InventoryLegacySynchronization/Model/AsyncConsumer.php b/InventoryLegacySynchronization/Model/AsyncConsumer.php deleted file mode 100644 index bd6eddc80bef..000000000000 --- a/InventoryLegacySynchronization/Model/AsyncConsumer.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryLegacySynchronization\Model; - -use Magento\AsynchronousOperations\Api\Data\OperationInterface; -use Magento\Framework\Serialize\SerializerInterface; - -/** - * Consumer class for asynchronous legacy synchronization. - * Works bot from MSI to legacy and vice versa. - */ -class AsyncConsumer -{ - /** - * @var SerializerInterface - */ - private $serializer; - - /** - * @var SynchronizeInventoryData - */ - private $synchronizeInventoryData; - - /** - * @param SerializerInterface $serializer - * @param SynchronizeInventoryData $synchronizeInventoryData - * @SuppressWarnings(PHPMD.LongVariable) - */ - public function __construct( - SerializerInterface $serializer, - SynchronizeInventoryData $synchronizeInventoryData - ) { - $this->serializer = $serializer; - $this->synchronizeInventoryData = $synchronizeInventoryData; - } - - /** - * Processing batch operations for legacy stock synchronization - * - * @param OperationInterface $operation - * @return void - * @throws \Magento\Framework\Exception\LocalizedException - */ - public function processOperations(OperationInterface $operation): void - { - $data = $this->serializer->unserialize($operation->getSerializedData()); - $this->synchronizeInventoryData->execute($data['destination'], $data['items']); - } -} diff --git a/InventoryLegacySynchronization/Model/GetDefaultSourceItemsBySkus.php b/InventoryLegacySynchronization/Model/GetDefaultSourceItemsBySkus.php deleted file mode 100644 index e831f87a2940..000000000000 --- a/InventoryLegacySynchronization/Model/GetDefaultSourceItemsBySkus.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryLegacySynchronization\Model; - -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\InventoryApi\Api\Data\SourceItemInterface; -use Magento\InventoryApi\Api\SourceItemRepositoryInterface; -use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; - -/** - * Get a list of default source items for a given SKU list - */ -class GetDefaultSourceItemsBySkus -{ - /** - * @var SearchCriteriaBuilder - */ - private $searchCriteriaBuilder; - - /** - * @var DefaultSourceProviderInterface - */ - private $defaultSourceProvider; - - /** - * @var SourceItemRepositoryInterface - */ - private $sourceItemRepository; - - /** - * @param SearchCriteriaBuilder $searchCriteriaBuilder - * @param DefaultSourceProviderInterface $defaultSourceProvider - * @param SourceItemRepositoryInterface $sourceItemRepository - * @SuppressWarnings(PHPMD.LongVariable) - */ - public function __construct( - SearchCriteriaBuilder $searchCriteriaBuilder, - DefaultSourceProviderInterface $defaultSourceProvider, - SourceItemRepositoryInterface $sourceItemRepository - ) { - $this->searchCriteriaBuilder = $searchCriteriaBuilder; - $this->defaultSourceProvider = $defaultSourceProvider; - $this->sourceItemRepository = $sourceItemRepository; - } - - /** - * @param array $skus - * @return SourceItemInterface[] - */ - public function execute(array $skus): array - { - $searchCriteria = $this->searchCriteriaBuilder - ->addFilter(SourceItemInterface::SKU, $skus, 'in') - ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) - ->create(); - - return $this->sourceItemRepository->getList($searchCriteria)->getItems(); - } -} diff --git a/InventoryLegacySynchronization/Model/GetLegacyStockItemsByProductIds.php b/InventoryLegacySynchronization/Model/GetLegacyStockItemsByProductIds.php deleted file mode 100644 index df7cdd01b37e..000000000000 --- a/InventoryLegacySynchronization/Model/GetLegacyStockItemsByProductIds.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryLegacySynchronization\Model; - -use Magento\CatalogInventory\Api\Data\StockItemInterface; -use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory; -use Magento\CatalogInventory\Api\StockItemRepositoryInterface; -use Magento\CatalogInventory\Model\Stock; -use Magento\Framework\Exception\LocalizedException; - -/** - * Get a list of legacy stock items by products ids - */ -class GetLegacyStockItemsByProductIds -{ - /** - * @var StockItemRepositoryInterface - */ - private $stockItemRepository; - - /** - * @var StockItemCriteriaInterfaceFactory - */ - private $stockItemCriteriaFactory; - - /** - * GetLegacyStockItemsByProductIds constructor. - * @param StockItemRepositoryInterface $stockItemRepository - * @param StockItemCriteriaInterfaceFactory $stockItemCriteriaFactory - * @SuppressWarnings(PHPMD.LongVariable) - */ - public function __construct( - StockItemRepositoryInterface $stockItemRepository, - StockItemCriteriaInterfaceFactory $stockItemCriteriaFactory - ) { - $this->stockItemRepository = $stockItemRepository; - $this->stockItemCriteriaFactory = $stockItemCriteriaFactory; - } - - /** - * @param array $productIds - * @return StockItemInterface[] - * @throws LocalizedException - */ - public function execute(array $productIds): array - { - $searchCriteria = $this->stockItemCriteriaFactory->create(); - $searchCriteria->setProductsFilter($productIds); - $searchCriteria->addFilter( - StockItemInterface::STOCK_ID, - StockItemInterface::STOCK_ID, - Stock::DEFAULT_STOCK_ID - ); - - $stockItems = $this->stockItemRepository->getList($searchCriteria)->getItems(); - $productIdsIndex = []; - foreach ($stockItems as $stockItem) { - $productIdsIndex[] = (int) $stockItem->getProductId(); - } - - return array_combine($productIdsIndex, $stockItems); - } -} diff --git a/InventoryLegacySynchronization/Model/IsAsyncLegacyAlignment.php b/InventoryLegacySynchronization/Model/IsAsyncLegacyAlignment.php deleted file mode 100644 index 13705c557082..000000000000 --- a/InventoryLegacySynchronization/Model/IsAsyncLegacyAlignment.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryLegacySynchronization\Model; - -use Magento\Framework\App\Config\ScopeConfigInterface; - -class IsAsyncLegacyAlignment -{ - /** - * Configuration path for legacy stock asynchronous operation enabling status - */ - private const XML_PATH = 'cataloginventory/legacy_stock/async'; - - /** - * @var ScopeConfigInterface - */ - private $scopeConfig; - - /** - * IsAsyncLegacyAlignment constructor. - * @param ScopeConfigInterface $scopeConfig - */ - public function __construct(ScopeConfigInterface $scopeConfig) - { - $this->scopeConfig = $scopeConfig; - } - - /** - * Return true if legacy inventory is aligned asynchronously - * @return bool - */ - public function execute(): bool - { - return (bool) $this->scopeConfig->getValue(self::XML_PATH); - } -} diff --git a/InventoryLegacySynchronization/Model/ResourceModel/UpdateLegacyStockItemsData.php b/InventoryLegacySynchronization/Model/ResourceModel/UpdateLegacyStockItemsData.php deleted file mode 100644 index b292551dbda1..000000000000 --- a/InventoryLegacySynchronization/Model/ResourceModel/UpdateLegacyStockItemsData.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\InventoryLegacySynchronization\Model\ResourceModel; - -use Magento\CatalogInventory\Api\Data\StockItemInterface; -use Magento\Framework\App\ResourceConnection; - -/** - * Update legacy stock items data in one single database operation - */ -class UpdateLegacyStockItemsData -{ - /** - * @var ResourceConnection - */ - private $resourceConnection; - - /** - * @param ResourceConnection $resourceConnection - */ - public function __construct( - ResourceConnection $resourceConnection - ) { - $this->resourceConnection = $resourceConnection; - } - - /** - * @param array $legacyItemsData - */ - public function execute(array $legacyItemsData): void - { - if (empty($legacyItemsData)) { - return; - } - - $tableName = $this->resourceConnection->getTableName('cataloginventory_stock_item'); - - $connection = $this->resourceConnection->getConnection(); - $connection->insertOnDuplicate( - $tableName, - $legacyItemsData, - [ - StockItemInterface::IS_IN_STOCK, - StockItemInterface::QTY, - ] - ); - } -} diff --git a/InventoryLegacySynchronization/Model/ResourceModel/UpdateSourceItemsData.php b/InventoryLegacySynchronization/Model/ResourceModel/UpdateSourceItemsData.php deleted file mode 100644 index 98411334e8ba..000000000000 --- a/InventoryLegacySynchronization/Model/ResourceModel/UpdateSourceItemsData.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\InventoryLegacySynchronization\Model\ResourceModel; - -use Magento\Framework\App\ResourceConnection; -use Magento\InventoryApi\Api\Data\SourceItemInterface; - -/** - * Update source items data in one single database operation - */ -class UpdateSourceItemsData -{ - /** - * @var ResourceConnection - */ - private $resourceConnection; - - /** - * @param ResourceConnection $resourceConnection - */ - public function __construct( - ResourceConnection $resourceConnection - ) { - $this->resourceConnection = $resourceConnection; - } - - /** - * @param array $sourceItemsData - */ - public function execute(array $sourceItemsData): void - { - if (empty($sourceItemsData)) { - return; - } - - $tableName = $this->resourceConnection->getTableName('inventory_source_item'); - - $connection = $this->resourceConnection->getConnection(); - $connection->insertOnDuplicate( - $tableName, - $sourceItemsData, - [ - SourceItemInterface::QUANTITY, - SourceItemInterface::STATUS, - ] - ); - } -} diff --git a/InventoryLegacySynchronization/Model/Synchronize.php b/InventoryLegacySynchronization/Model/Synchronize.php deleted file mode 100644 index 7c46be8728aa..000000000000 --- a/InventoryLegacySynchronization/Model/Synchronize.php +++ /dev/null @@ -1,160 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryLegacySynchronization\Model; - -use Magento\AsynchronousOperations\Api\Data\OperationInterface; -use Magento\AsynchronousOperations\Api\Data\OperationInterfaceFactory; -use Magento\Framework\Bulk\BulkManagementInterface; -use Magento\Framework\DataObject\IdentityGeneratorInterface; -use Magento\Framework\Serialize\SerializerInterface; - -/** - * Set Qty and status for legacy CatalogInventory Stock Item table - */ -class Synchronize -{ - /** - * Asynchronous operation topic name - */ - private const TOPIC_NAME = 'inventory.legacy_synchronization.set_data'; - - /** - * Synchronization from MSI source items to legacy catalog inventory - */ - public const MSI_TO_LEGACY = 'synchronize-msi-to-legacy'; - - /** - * Synchronization from legacy catalog inventory to MSI source items - */ - public const LEGACY_TO_MSI = 'synchronize-legacy-to-msi'; - - /** - * @var BulkManagementInterface - */ - private $bulkManagement; - - /** - * @var IdentityGeneratorInterface - */ - private $identityService; - - /** - * @var SerializerInterface - */ - private $serializer; - - /** - * @var OperationInterfaceFactory - */ - private $operationInterfaceFactory; - - /** - * @var IsAsyncLegacyAlignment - */ - private $isAsyncLegacyAlignment; - - /** - * @var SynchronizeInventoryData - */ - private $synchronizeInventoryData; - - /** - * @var int - */ - private $batchSize; - - /** - * @param BulkManagementInterface $bulkManagement - * @param SerializerInterface $serializer - * @param IdentityGeneratorInterface $identityService - * @param OperationInterfaceFactory $operationInterfaceFactory - * @param IsAsyncLegacyAlignment $isAsyncLegacyAlignment - * @param SynchronizeInventoryData $synchronizeInventoryData - * @param int $batchSize - */ - public function __construct( - BulkManagementInterface $bulkManagement, - SerializerInterface $serializer, - IdentityGeneratorInterface $identityService, - OperationInterfaceFactory $operationInterfaceFactory, - IsAsyncLegacyAlignment $isAsyncLegacyAlignment, - SynchronizeInventoryData $synchronizeInventoryData, - int $batchSize - ) { - $this->bulkManagement = $bulkManagement; - $this->identityService = $identityService; - $this->serializer = $serializer; - $this->operationInterfaceFactory = $operationInterfaceFactory; - $this->batchSize = $batchSize; - $this->isAsyncLegacyAlignment = $isAsyncLegacyAlignment; - $this->synchronizeInventoryData = $synchronizeInventoryData; - } - - /** - * @param string $destination - * @param array $items - */ - private function executeAsync(string $destination, array $items): void - { - $asyncOperations = []; - - $bulkUuid = $this->identityService->generateId(); - - $chunks = array_chunk($items, $this->batchSize); - foreach ($chunks as $chunk) { - $data = [ - 'data' => [ - 'bulk_uuid' => $bulkUuid, - 'topic_name' => self::TOPIC_NAME, - 'serialized_data' => $this->serializer->serialize( - [ - 'destination' => $destination, - 'items' => $chunk - ] - ), - 'status' => OperationInterface::STATUS_TYPE_OPEN, - ] - ]; - - /** @var OperationInterface $asyncOperation */ - $asyncOperation = $this->operationInterfaceFactory->create($data); - $asyncOperations[] = $asyncOperation; - } - - $this->bulkManagement->scheduleBulk($bulkUuid, $asyncOperations, __('Synchronize legacy stock')); - } - - /** - * @param string $destination - * @param array $items - * @throws \Magento\Framework\Exception\LocalizedException - */ - private function executeSync(string $destination, array $items): void - { - $this->synchronizeInventoryData->execute($destination, $items); - } - - /** - * @param string $destination - * @param array $items - * @return void - * @throws \Magento\Framework\Exception\LocalizedException - */ - public function execute(string $destination, array $items): void - { - if (empty($items)) { - return; - } - - if ($this->isAsyncLegacyAlignment->execute()) { - $this->executeAsync($destination, $items); - } else { - $this->executeSync($destination, $items); - } - } -} diff --git a/InventoryLegacySynchronization/Model/SynchronizeInventoryData.php b/InventoryLegacySynchronization/Model/SynchronizeInventoryData.php deleted file mode 100644 index 3f5a28c7cdb2..000000000000 --- a/InventoryLegacySynchronization/Model/SynchronizeInventoryData.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryLegacySynchronization\Model; - -use Magento\InventoryLegacySynchronization\Model\ToMsi\SetDataToSourceItem; -use Magento\InventoryLegacySynchronization\Model\ToLegacy\SetDataToLegacyInventory; - -/** - * Set Qty and status for legacy CatalogInventory Stock Item table - */ -class SynchronizeInventoryData -{ - /** - * @var SetDataToLegacyInventory - */ - private $setDataToLegacyInventory; - - /** - * @var SetDataToSourceItem - */ - private $setDataToSourceItem; - - /** - * SetDataToDestination constructor. - * @param SetDataToLegacyInventory $setDataToLegacyInventory - * @param SetDataToSourceItem $setDataToSourceItem - * @SuppressWarnings(PHPMD.LongVariable) - */ - public function __construct( - SetDataToLegacyInventory $setDataToLegacyInventory, - SetDataToSourceItem $setDataToSourceItem - ) { - $this->setDataToLegacyInventory = $setDataToLegacyInventory; - $this->setDataToSourceItem = $setDataToSourceItem; - } - - /** - * @param string $destination - * @param array $items - * @throws \Magento\Framework\Exception\LocalizedException - */ - public function execute(string $destination, array $items): void - { - if ($destination === Synchronize::MSI_TO_LEGACY) { - $this->setDataToLegacyInventory->execute($items); - } elseif ($destination === Synchronize::LEGACY_TO_MSI) { - $this->setDataToSourceItem->execute($items); - } - } -} diff --git a/InventoryLegacySynchronization/Model/ToLegacy/SetDataToLegacyInventory.php b/InventoryLegacySynchronization/Model/ToLegacy/SetDataToLegacyInventory.php deleted file mode 100644 index 7e2251950d4a..000000000000 --- a/InventoryLegacySynchronization/Model/ToLegacy/SetDataToLegacyInventory.php +++ /dev/null @@ -1,156 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryLegacySynchronization\Model\ToLegacy; - -use Magento\Catalog\Model\ResourceModel\Product as ProductResourceModel; -use Magento\CatalogInventory\Api\Data\StockItemInterface; -use Magento\CatalogInventory\Model\Indexer\Stock\Processor; -use Magento\CatalogInventory\Model\Spi\StockStateProviderInterface; -use Magento\Framework\Exception\LocalizedException; -use Magento\InventoryApi\Api\Data\SourceItemInterface; -use Magento\InventoryCatalog\Model\GetProductIdsBySkus; -use Magento\InventoryIndexer\Indexer\SourceItem\SourceItemIndexer; -use Magento\InventoryLegacySynchronization\Model\GetLegacyStockItemsByProductIds; -use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; -use Magento\InventoryLegacySynchronization\Model\ResourceModel\UpdateLegacyStockItemsData; - -/** - * Copy source item information to legacy stock - */ -class SetDataToLegacyInventory -{ - /** - * @var GetLegacyStockItemsByProductIds - */ - private $getLegacyStockItemsByProductIds; - - /** - * @var StockStateProviderInterface - */ - private $stockStateProvider; - - /** - * @var Processor - */ - private $indexerProcessor; - - /** - * @var GetProductIdsBySkus - */ - private $productResourceModel; - - /** - * @var DefaultSourceProviderInterface - */ - private $defaultSourceProvider; - - /** - * @var UpdateLegacyStockItemsData - */ - private $updateLegacyStockItemsData; - - /** - * @var SourceItemIndexer - */ - private $sourceItemIndexer; - - /** - * @param UpdateLegacyStockItemsData $updateLegacyStockItemsData - * @param DefaultSourceProviderInterface $defaultSourceProvider - * @param GetLegacyStockItemsByProductIds $getLegacyStockItemsByProductIds - * @param SourceItemIndexer $sourceItemIndexer - * @param StockStateProviderInterface $stockStateProvider - * @param Processor $indexerProcessor - * @param ProductResourceModel $productResourceModel - * @SuppressWarnings(PHPMD.LongVariable) - */ - public function __construct( - UpdateLegacyStockItemsData $updateLegacyStockItemsData, - DefaultSourceProviderInterface $defaultSourceProvider, - GetLegacyStockItemsByProductIds $getLegacyStockItemsByProductIds, - SourceItemIndexer $sourceItemIndexer, - StockStateProviderInterface $stockStateProvider, - Processor $indexerProcessor, - ProductResourceModel $productResourceModel - ) { - $this->getLegacyStockItemsByProductIds = $getLegacyStockItemsByProductIds; - $this->stockStateProvider = $stockStateProvider; - $this->indexerProcessor = $indexerProcessor; - $this->productResourceModel = $productResourceModel; - $this->defaultSourceProvider = $defaultSourceProvider; - $this->updateLegacyStockItemsData = $updateLegacyStockItemsData; - $this->sourceItemIndexer = $sourceItemIndexer; - } - - /** - * Synchronously execute legacy alignment - * - * @param array $sourceItemsData - * @throws LocalizedException - * @SuppressWarnings(PHPMD.LongVariable) - */ - public function execute(array $sourceItemsData): void - { - $productSkus = array_column($sourceItemsData, SourceItemInterface::SKU); - $productIdsBySku = $this->productResourceModel->getProductsIdsBySkus($productSkus); - - $legacyStockItemsByProductsIds = - $this->getLegacyStockItemsByProductIds->execute(array_values($productIdsBySku)); - - $legacyItemsToUpdateData = []; - foreach ($sourceItemsData as $sourceItemData) { - $sku = (string) $sourceItemData[SourceItemInterface::SKU]; - - if ($sourceItemData[SourceItemInterface::SOURCE_CODE] !== $this->defaultSourceProvider->getCode()) { - throw new LocalizedException(__('Only default source can synchronize legacy stock')); - } - - if (!isset($productIdsBySku[$sku])) { - continue; - } - - $productId = $productIdsBySku[$sku]; - - if (!isset($legacyStockItemsByProductsIds[$productId])) { - continue; - } - - $legacyStockItem = $legacyStockItemsByProductsIds[$productId]; - $isInStock = (int) $sourceItemData[SourceItemInterface::STATUS]; - $quantity = (float) $sourceItemData[SourceItemInterface::QUANTITY]; - - if ($legacyStockItem->getManageStock()) { - $legacyStockItem->setIsInStock($isInStock); - $legacyStockItem->setQty($quantity); - - if (false === $this->stockStateProvider->verifyStock($legacyStockItem)) { - $isInStock = 0; - } - } - - $legacyItemsToUpdateData[] = [ - StockItemInterface::QTY => $quantity, - StockItemInterface::IS_IN_STOCK => $isInStock, - StockItemInterface::PRODUCT_ID => $productId, - 'stock_id' => 1, - 'website_id' => 0, - ]; - - $productIds[] = $productId; - } - - if (!empty($legacyItemsToUpdateData)) { - $this->updateLegacyStockItemsData->execute($legacyItemsToUpdateData); - } - - if (!empty($productIds)) { - $this->indexerProcessor->reindexList($productIds); - $this->sourceItemIndexer->executeList(array_column($sourceItemsData, 'source_item_id')); - } - } -} diff --git a/InventoryLegacySynchronization/Model/ToMsi/SetDataToSourceItem.php b/InventoryLegacySynchronization/Model/ToMsi/SetDataToSourceItem.php deleted file mode 100644 index 31095c0713ac..000000000000 --- a/InventoryLegacySynchronization/Model/ToMsi/SetDataToSourceItem.php +++ /dev/null @@ -1,131 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryLegacySynchronization\Model\ToMsi; - -use Magento\Catalog\Model\ResourceModel\Product as ProductResourceModel; -use Magento\CatalogInventory\Api\Data\StockItemInterface; -use Magento\InventoryApi\Api\Data\SourceItemInterface; -use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; -use Magento\InventoryIndexer\Indexer\SourceItem\SourceItemIndexer; -use Magento\InventoryLegacySynchronization\Model\GetDefaultSourceItemsBySkus; -use Magento\InventoryLegacySynchronization\Model\GetLegacyStockItemsByProductIds; -use Magento\InventoryLegacySynchronization\Model\ResourceModel\UpdateSourceItemsData; - -/** - * Copy legacy stock item information to MSI source items - */ -class SetDataToSourceItem -{ - /** - * @var UpdateSourceItemsData - */ - private $updateSourceItemsData; - - /** - * @var DefaultSourceProviderInterface - */ - private $defaultSourceProvider; - - /** - * @var GetLegacyStockItemsByProductIds - */ - private $getLegacyStockItemsByProductIds; - - /** - * @var SourceItemIndexer - */ - private $sourceItemIndexer; - - /** - * @var GetDefaultSourceItemsBySkus - */ - private $getDefaultSourceItemsBySkus; - - /** - * @var ProductResourceModel - */ - private $productResourceModel; - - /** - * @param UpdateSourceItemsData $updateSourceItemsData - * @param GetDefaultSourceItemsBySkus $getDefaultSourceItemsBySkus - * @param GetLegacyStockItemsByProductIds $getLegacyStockItemsByProductIds - * @param DefaultSourceProviderInterface $defaultSourceProvider - * @param SourceItemIndexer $sourceItemIndexer - * @param ProductResourceModel $productResourceModel - * @SuppressWarnings(PHPMD.LongVariable) - */ - public function __construct( - UpdateSourceItemsData $updateSourceItemsData, - GetDefaultSourceItemsBySkus $getDefaultSourceItemsBySkus, - GetLegacyStockItemsByProductIds $getLegacyStockItemsByProductIds, - DefaultSourceProviderInterface $defaultSourceProvider, - SourceItemIndexer $sourceItemIndexer, - ProductResourceModel $productResourceModel - ) { - $this->updateSourceItemsData = $updateSourceItemsData; - $this->defaultSourceProvider = $defaultSourceProvider; - $this->getLegacyStockItemsByProductIds = $getLegacyStockItemsByProductIds; - $this->sourceItemIndexer = $sourceItemIndexer; - $this->getDefaultSourceItemsBySkus = $getDefaultSourceItemsBySkus; - $this->productResourceModel = $productResourceModel; - } - - /** - * @param array $legacyItemsData - * @throws \Magento\Framework\Exception\LocalizedException - * @SuppressWarnings(PHPMD.LongVariable) - */ - public function execute(array $legacyItemsData): void - { - $sourceItemsData = []; - $productIds = array_column($legacyItemsData, StockItemInterface::PRODUCT_ID); - $defaultSourceCode = $this->defaultSourceProvider->getCode(); - - $legacyStockItemsByProductsIds = - $this->getLegacyStockItemsByProductIds->execute(array_values($productIds)); - - $productSkus = $this->productResourceModel->getProductsSku($productIds); - $productSkusById = array_combine( - array_column($productSkus, 'entity_id'), - array_column($productSkus, 'sku') - ); - - foreach ($legacyItemsData as $legacyItemData) { - $productId = (int) $legacyItemData[StockItemInterface::PRODUCT_ID]; - - if (!isset($productSkusById[$productId], $legacyStockItemsByProductsIds[$productId])) { - continue; - } - - $productSku = $productSkusById[$productId]; - - /** @var StockItemInterface $legacyStockItem */ - $legacyStockItem = $legacyStockItemsByProductsIds[$productId]; - - $sourceItemsData[] = [ - SourceItemInterface::SOURCE_CODE => $defaultSourceCode, - SourceItemInterface::SKU => $productSku, - SourceItemInterface::QUANTITY => (float) $legacyStockItem->getQty(), - SourceItemInterface::STATUS => (int) $legacyStockItem->getIsInStock(), - ]; - } - - if (!empty($sourceItemsData)) { - $this->updateSourceItemsData->execute($sourceItemsData); - - $sourceItemsToReindex = $this->getDefaultSourceItemsBySkus->execute(array_values($productSkusById)); - $sourceItemsIdsToReindex = []; - foreach ($sourceItemsToReindex as $sourceItemToReindex) { - $sourceItemsIdsToReindex[] = (int)$sourceItemToReindex->getId(); - } - - $this->sourceItemIndexer->executeList($sourceItemsIdsToReindex); - } - } -} diff --git a/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtBulkTransfer.php b/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtBulkTransfer.php deleted file mode 100644 index 02c8c2f7b685..000000000000 --- a/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtBulkTransfer.php +++ /dev/null @@ -1,98 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryLegacySynchronization\Plugin; - -use Magento\InventoryApi\Api\Data\SourceItemInterface; -use Magento\InventoryCatalogApi\Api\BulkInventoryTransferInterface; -use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; -use Magento\InventoryLegacySynchronization\Model\GetDefaultSourceItemsBySkus; -use Magento\InventoryLegacySynchronization\Model\Synchronize; - -class SetDataToLegacyCatalogInventoryAtBulkTransfer -{ - /** - * @var GetDefaultSourceItemsBySkus - */ - private $getDefaultSourceItemsBySkus; - - /** - * @var DefaultSourceProviderInterface - */ - private $defaultSourceProvider; - - /** - * @var Synchronize - */ - private $synchronize; - - /** - * @param GetDefaultSourceItemsBySkus $getDefaultSourceItemsBySkus - * @param DefaultSourceProviderInterface $defaultSourceProvider - * @param Synchronize $synchronize - * @SuppressWarnings(PHPMD.LongVariable) - */ - public function __construct( - GetDefaultSourceItemsBySkus $getDefaultSourceItemsBySkus, - DefaultSourceProviderInterface $defaultSourceProvider, - Synchronize $synchronize - ) { - $this->getDefaultSourceItemsBySkus = $getDefaultSourceItemsBySkus; - $this->defaultSourceProvider = $defaultSourceProvider; - $this->synchronize = $synchronize; - } - - /** - * @param BulkInventoryTransferInterface $subject - * @param $result - * @param array $skus - * @param string $originSource - * @param string $destinationSource - * @param bool $unassignFromOrigin - * @return bool - * @throws \Magento\Framework\Exception\LocalizedException - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function afterExecute( - BulkInventoryTransferInterface $subject, - bool $result, - array $skus, - string $originSource, - string $destinationSource, - bool $unassignFromOrigin - ): bool { - $defaultSourceCode = $this->defaultSourceProvider->getCode(); - - $sourceItemsData = []; - if ($unassignFromOrigin && ($originSource === $defaultSourceCode)) { - foreach ($skus as $sku) { - $sourceItemsData[] = [ - SourceItemInterface::QUANTITY => 0.0, - SourceItemInterface::STATUS => SourceItemInterface::STATUS_OUT_OF_STOCK, - SourceItemInterface::SOURCE_CODE => $defaultSourceCode, - SourceItemInterface::SKU => $sku - ]; - } - } elseif (($destinationSource === $defaultSourceCode) || ($originSource === $defaultSourceCode)) { - $sourceItems = $this->getDefaultSourceItemsBySkus->execute($skus); - foreach ($sourceItems as $sourceItem) { - $sourceItemsData[] = [ - SourceItemInterface::QUANTITY => $sourceItem->getQuantity(), - SourceItemInterface::STATUS => $sourceItem->getStatus(), - SourceItemInterface::SOURCE_CODE => $defaultSourceCode, - SourceItemInterface::SKU => $sourceItem->getSku() - ]; - } - } - - if (!empty($sourceItemsData)) { - $this->synchronize->execute(Synchronize::MSI_TO_LEGACY, $sourceItemsData); - } - - return $result; - } -} diff --git a/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtTransferInventoryPartial.php b/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtTransferInventoryPartial.php deleted file mode 100644 index 0db4b098f9c7..000000000000 --- a/InventoryLegacySynchronization/Plugin/SetDataToLegacyCatalogInventoryAtTransferInventoryPartial.php +++ /dev/null @@ -1,85 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryLegacySynchronization\Plugin; - -use Magento\Framework\Exception\LocalizedException; -use Magento\InventoryApi\Api\Data\SourceItemInterface; -use Magento\InventoryCatalog\Model\GetDefaultSourceItemBySku; -use Magento\InventoryCatalog\Model\ResourceModel\TransferInventoryPartially; -use Magento\InventoryCatalogApi\Api\Data\PartialInventoryTransferItemInterface; -use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; -use Magento\InventoryLegacySynchronization\Model\Synchronize; - -class SetDataToLegacyCatalogInventoryAtTransferInventoryPartial -{ - /** - * @var Synchronize - */ - private $synchronize; - - /** - * @var DefaultSourceProviderInterface - */ - private $defaultSourceProvider; - - /** - * @var GetDefaultSourceItemBySku - */ - private $getDefaultSourceItemBySku; - - /** - * @param Synchronize $synchronize - * @param DefaultSourceProviderInterface $defaultSourceProvider - * @param GetDefaultSourceItemBySku $getDefaultSourceItemBySku - * @SuppressWarnings(PHPMD.LongVariable) - */ - public function __construct( - Synchronize $synchronize, - DefaultSourceProviderInterface $defaultSourceProvider, - GetDefaultSourceItemBySku $getDefaultSourceItemBySku - ) { - $this->synchronize = $synchronize; - $this->defaultSourceProvider = $defaultSourceProvider; - $this->getDefaultSourceItemBySku = $getDefaultSourceItemBySku; - } - - /** - * @param TransferInventoryPartially $subject - * @param $result - * @param PartialInventoryTransferItemInterface $transfer - * @param string $originSourceCode - * @param string $destinationSourceCode - * @throws LocalizedException - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * @SuppressWarnings(PHPMD.LongVariable) - */ - public function afterExecute( - TransferInventoryPartially $subject, - $result, - PartialInventoryTransferItemInterface $transfer, - string $originSourceCode, - string $destinationSourceCode - ): void { - $defaultSourceCode = $this->defaultSourceProvider->getCode(); - - if ($originSourceCode === $defaultSourceCode || $destinationSourceCode === $defaultSourceCode) { - $sourceItem = $this->getDefaultSourceItemBySku->execute($transfer->getSku()); - if ($sourceItem !== null) { - $this->synchronize->execute( - Synchronize::MSI_TO_LEGACY, - [ - SourceItemInterface::QUANTITY => $sourceItem->getQuantity(), - SourceItemInterface::SKU => $transfer->getSku(), - SourceItemInterface::SOURCE_CODE => $defaultSourceCode, - SourceItemInterface::STATUS => $sourceItem->getStatus() - ] - ); - } - } - } -} diff --git a/InventoryLegacySynchronization/Plugin/SetZeroQuantityToLegacyAtBulkUnassign.php b/InventoryLegacySynchronization/Plugin/SetZeroQuantityToLegacyAtBulkUnassign.php deleted file mode 100644 index 06da8078ae21..000000000000 --- a/InventoryLegacySynchronization/Plugin/SetZeroQuantityToLegacyAtBulkUnassign.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - - -namespace Magento\InventoryLegacySynchronization\Plugin; - -use Magento\InventoryApi\Api\Data\SourceItemInterface; -use Magento\InventoryCatalogApi\Api\BulkSourceUnassignInterface; -use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; -use Magento\InventoryLegacySynchronization\Model\Synchronize; - -/** - * Set zero quantity plugin on bulk default source unassign - */ -class SetZeroQuantityToLegacyAtBulkUnassign -{ - /** - * @var DefaultSourceProviderInterface - */ - private $defaultSourceProvider; - - /** - * @var Synchronize - */ - private $synchronize; - - /** - * @param DefaultSourceProviderInterface $defaultSourceProvider - * @param Synchronize $synchronize - * @SuppressWarnings(PHPMD.LongVariable) - */ - public function __construct( - DefaultSourceProviderInterface $defaultSourceProvider, - Synchronize $synchronize - ) { - $this->defaultSourceProvider = $defaultSourceProvider; - $this->synchronize = $synchronize; - } - - /** - * @param BulkSourceUnassignInterface $subject - * @param int $result - * @param array $skus - * @param array $sourceCodes - * @return int - * @throws \Magento\Framework\Exception\LocalizedException - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function afterExecute( - BulkSourceUnassignInterface $subject, - int $result, - array $skus, - array $sourceCodes - ): int { - $defaultSourceCode = $this->defaultSourceProvider->getCode(); - - if (\in_array($this->defaultSourceProvider->getCode(), $sourceCodes, true)) { - $sourceItemsData = []; - foreach ($skus as $sku) { - $sourceItemsData[] = [ - SourceItemInterface::QUANTITY => 0.0, - SourceItemInterface::STATUS => SourceItemInterface::STATUS_OUT_OF_STOCK, - SourceItemInterface::SOURCE_CODE => $defaultSourceCode, - SourceItemInterface::SKU => $sku, - ]; - } - - $this->synchronize->execute(Synchronize::MSI_TO_LEGACY, $sourceItemsData); - } - - return $result; - } -} \ No newline at end of file diff --git a/InventoryLegacySynchronization/README.md b/InventoryLegacySynchronization/README.md deleted file mode 100644 index caaaedf9fe5c..000000000000 --- a/InventoryLegacySynchronization/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# InventoryLegacySynchronization module - -The `InventoryLegacySynchronization` module provides a backward compatible mechanism with Magento legacy inventory - -This module is part of the new inventory infrastructure. The -[Inventory Management overview](https://devdocs.magento.com/guides/v2.3/inventory/index.html) -describes the MSI (Multi-Source Inventory) project in more detail. - -## Installation details - -This module is installed as part of Magento Open Source. It cannot be deleted or disabled. diff --git a/InventoryLegacySynchronization/Test/Integration/SetDataToLegacyCatalogInventoryAtBulkTransferTest.php b/InventoryLegacySynchronization/Test/Integration/SetDataToLegacyCatalogInventoryAtBulkTransferTest.php deleted file mode 100644 index f8dd1909e1d9..000000000000 --- a/InventoryLegacySynchronization/Test/Integration/SetDataToLegacyCatalogInventoryAtBulkTransferTest.php +++ /dev/null @@ -1,221 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryLegacySynchronization\Test\Integration; - -use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\InventoryApi\Api\Data\SourceItemInterface; -use Magento\InventoryApi\Api\SourceItemRepositoryInterface; -use Magento\InventoryCatalogApi\Api\BulkInventoryTransferInterface; -use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; -use PHPUnit\Framework\TestCase; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\CatalogInventory\Api\StockItemRepositoryInterface; -use Magento\CatalogInventory\Api\StockItemCriteriaInterface; -use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory; - -class SetDataToLegacyCatalogInventoryAtBulkTransferTest extends TestCase -{ - /** - * @var ProductRepositoryInterface - */ - private $productRepository; - - /** - * @var StockItemCriteriaInterfaceFactory - */ - private $legacyStockItemCriteriaFactory; - - /** - * @var StockItemRepositoryInterface - */ - private $legacyStockItemRepository; - - /** - * @var SearchCriteriaBuilder - */ - private $searchCriteriaBuilder; - - /** - * @var SourceItemRepositoryInterface - */ - private $sourceItemRepository; - - /** - * @var DefaultSourceProviderInterface - */ - private $defaultSourceProvider; - - /** - * @var BulkInventoryTransferInterface - */ - private $bulkInventoryTransfer; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); - - $this->legacyStockItemCriteriaFactory = Bootstrap::getObjectManager()->get( - StockItemCriteriaInterfaceFactory::class - ); - $this->legacyStockItemRepository = Bootstrap::getObjectManager()->get(StockItemRepositoryInterface::class); - - $this->searchCriteriaBuilder = Bootstrap::getObjectManager()->get(SearchCriteriaBuilder::class); - $this->sourceItemRepository = Bootstrap::getObjectManager()->get(SourceItemRepositoryInterface::class); - - $this->bulkInventoryTransfer = Bootstrap::getObjectManager()->get(BulkInventoryTransferInterface::class); - $this->defaultSourceProvider = Bootstrap::getObjectManager()->get(DefaultSourceProviderInterface::class); - } - - /** - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php - * @SuppressWarnings(PHPMD.LongVariable) - */ - public function testShouldSetLegacyQuantityToZeroOnInventoryTransferAndDefaultSourceUnassign(): void - { - $productSku = 'SKU-1'; - $product = $this->productRepository->get($productSku); - $productId = $product->getId(); - $websiteId = 0; - - /** @var StockItemCriteriaInterface $legacyStockItemCriteria */ - $legacyStockItemCriteria = $this->legacyStockItemCriteriaFactory->create(); - $legacyStockItemCriteria->setProductsFilter($productId); - $legacyStockItemCriteria->setScopeFilter($websiteId); - $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); - self::assertCount(1, $legacyStockItems); - - $legacyStockItem = reset($legacyStockItems); - self::assertTrue($legacyStockItem->getIsInStock()); - self::assertSame(5.5, (float) $legacyStockItem->getQty()); - - $searchCriteria = $this->searchCriteriaBuilder - ->addFilter(SourceItemInterface::SKU, $productSku) - ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) - ->create(); - $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); - self::assertCount(1, $sourceItems); - - $this->bulkInventoryTransfer->execute( - ['SKU-1'], - $this->defaultSourceProvider->getCode(), - 'eu-1', - true - ); - - $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); - self::assertCount(1, $legacyStockItems); - - $legacyStockItem = reset($legacyStockItems); - self::assertFalse($legacyStockItem->getIsInStock()); - self::assertSame(0.0, (float) $legacyStockItem->getQty()); - } - - /** - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php - * @SuppressWarnings(PHPMD.LongVariable) - */ - public function testShouldSetLegacyQuantityToZeroOnInventoryTransferToDefaultSource(): void - { - $productSku = 'SKU-1'; - $product = $this->productRepository->get($productSku); - $productId = $product->getId(); - $websiteId = 0; - - /** @var StockItemCriteriaInterface $legacyStockItemCriteria */ - $legacyStockItemCriteria = $this->legacyStockItemCriteriaFactory->create(); - $legacyStockItemCriteria->setProductsFilter($productId); - $legacyStockItemCriteria->setScopeFilter($websiteId); - $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); - self::assertCount(1, $legacyStockItems); - - $legacyStockItem = reset($legacyStockItems); - self::assertTrue($legacyStockItem->getIsInStock()); - self::assertEquals(5.5, $legacyStockItem->getQty()); - - $searchCriteria = $this->searchCriteriaBuilder - ->addFilter(SourceItemInterface::SKU, $productSku) - ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) - ->create(); - $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); - self::assertCount(1, $sourceItems); - - $this->bulkInventoryTransfer->execute( - ['SKU-1'], - 'eu-1', - $this->defaultSourceProvider->getCode(), - true - ); - - $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); - self::assertCount(1, $legacyStockItems); - - $legacyStockItem = reset($legacyStockItems); - self::assertTrue($legacyStockItem->getIsInStock()); - self::assertEquals(11.0, (float) $legacyStockItem->getQty()); - } - - /** - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php - * @SuppressWarnings(PHPMD.LongVariable) - */ - public function testShouldSetLegacyQuantityToZeroOnInventoryTransferFromDefaultSource(): void - { - $productSku = 'SKU-1'; - $product = $this->productRepository->get($productSku); - $productId = $product->getId(); - $websiteId = 0; - - /** @var StockItemCriteriaInterface $legacyStockItemCriteria */ - $legacyStockItemCriteria = $this->legacyStockItemCriteriaFactory->create(); - $legacyStockItemCriteria->setProductsFilter($productId); - $legacyStockItemCriteria->setScopeFilter($websiteId); - $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); - self::assertCount(1, $legacyStockItems); - - $legacyStockItem = reset($legacyStockItems); - self::assertTrue($legacyStockItem->getIsInStock()); - self::assertSame(5.5, (float) $legacyStockItem->getQty()); - - $searchCriteria = $this->searchCriteriaBuilder - ->addFilter(SourceItemInterface::SKU, $productSku) - ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) - ->create(); - $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); - self::assertCount(1, $sourceItems); - - $this->bulkInventoryTransfer->execute( - ['SKU-1'], - $this->defaultSourceProvider->getCode(), - 'eu-1', - false - ); - - $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); - self::assertCount(1, $legacyStockItems); - - $legacyStockItem = reset($legacyStockItems); - self::assertFalse($legacyStockItem->getIsInStock()); - self::assertSame(0.0, (float) $legacyStockItem->getQty()); - } -} diff --git a/InventoryLegacySynchronization/Test/Integration/SetZeroToLegacyAtBulkUnassignTest.php b/InventoryLegacySynchronization/Test/Integration/SetZeroToLegacyAtBulkUnassignTest.php deleted file mode 100644 index d75f6a485494..000000000000 --- a/InventoryLegacySynchronization/Test/Integration/SetZeroToLegacyAtBulkUnassignTest.php +++ /dev/null @@ -1,161 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\InventoryLegacySynchronization\Test\Integration; - -use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\InventoryApi\Api\Data\SourceItemInterface; -use Magento\InventoryApi\Api\SourceItemRepositoryInterface; -use Magento\InventoryCatalogApi\Api\BulkSourceUnassignInterface; -use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; -use PHPUnit\Framework\TestCase; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\CatalogInventory\Api\StockItemRepositoryInterface; -use Magento\CatalogInventory\Api\StockItemCriteriaInterface; -use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory; - -class SetZeroToLegacyAtBulkUnassignTest extends TestCase -{ - /** - * @var ProductRepositoryInterface - */ - private $productRepository; - - /** - * @var StockItemCriteriaInterfaceFactory - */ - private $legacyStockItemCriteriaFactory; - - /** - * @var StockItemRepositoryInterface - */ - private $legacyStockItemRepository; - - /** - * @var SearchCriteriaBuilder - */ - private $searchCriteriaBuilder; - - /** - * @var SourceItemRepositoryInterface - */ - private $sourceItemRepository; - - /** - * @var DefaultSourceProviderInterface - */ - private $defaultSourceProvider; - - /** - * @var BulkSourceUnassignInterface - */ - private $bulkSourceUnassign; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); - - $this->legacyStockItemCriteriaFactory = Bootstrap::getObjectManager()->get( - StockItemCriteriaInterfaceFactory::class - ); - $this->legacyStockItemRepository = Bootstrap::getObjectManager()->get(StockItemRepositoryInterface::class); - - $this->searchCriteriaBuilder = Bootstrap::getObjectManager()->get(SearchCriteriaBuilder::class); - $this->sourceItemRepository = Bootstrap::getObjectManager()->get(SourceItemRepositoryInterface::class); - - $this->bulkSourceUnassign = Bootstrap::getObjectManager()->get(BulkSourceUnassignInterface::class); - $this->defaultSourceProvider = Bootstrap::getObjectManager()->get(DefaultSourceProviderInterface::class); - } - - /** - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php - * @SuppressWarnings(PHPMD.LongVariable) - */ - public function testShouldSetLegacyQuantityToZeroOnBulkUnassign(): void - { - $productSku = 'SKU-1'; - $product = $this->productRepository->get($productSku); - $productId = $product->getId(); - $websiteId = 0; - - /** @var StockItemCriteriaInterface $legacyStockItemCriteria */ - $legacyStockItemCriteria = $this->legacyStockItemCriteriaFactory->create(); - $legacyStockItemCriteria->setProductsFilter($productId); - $legacyStockItemCriteria->setScopeFilter($websiteId); - $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); - self::assertCount(1, $legacyStockItems); - - $legacyStockItem = reset($legacyStockItems); - self::assertTrue($legacyStockItem->getIsInStock()); - self::assertSame(5.5, (float) $legacyStockItem->getQty()); - - $searchCriteria = $this->searchCriteriaBuilder - ->addFilter(SourceItemInterface::SKU, $productSku) - ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) - ->create(); - $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); - self::assertCount(1, $sourceItems); - - $this->bulkSourceUnassign->execute(['SKU-1'], [$this->defaultSourceProvider->getCode()]); - - $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); - self::assertCount(1, $legacyStockItems); - - $legacyStockItem = reset($legacyStockItems); - self::assertFalse($legacyStockItem->getIsInStock()); - self::assertSame(0.0, (float) $legacyStockItem->getQty()); - } - - /** - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/products.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryCatalog/Test/_files/source_items_on_default_source.php - * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php - * @SuppressWarnings(PHPMD.LongVariable) - */ - public function testShouldNotSetLegacyQuantityToZeroOnBulkUnassignNotInvolvingDefaultSource(): void - { - $productSku = 'SKU-1'; - $product = $this->productRepository->get($productSku); - $productId = $product->getId(); - $websiteId = 0; - - /** @var StockItemCriteriaInterface $legacyStockItemCriteria */ - $legacyStockItemCriteria = $this->legacyStockItemCriteriaFactory->create(); - $legacyStockItemCriteria->setProductsFilter($productId); - $legacyStockItemCriteria->setScopeFilter($websiteId); - $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); - self::assertCount(1, $legacyStockItems); - - $legacyStockItem = reset($legacyStockItems); - self::assertTrue($legacyStockItem->getIsInStock()); - self::assertSame(5.5, (float) $legacyStockItem->getQty()); - - $searchCriteria = $this->searchCriteriaBuilder - ->addFilter(SourceItemInterface::SKU, $productSku) - ->addFilter(SourceItemInterface::SOURCE_CODE, $this->defaultSourceProvider->getCode()) - ->create(); - $sourceItems = $this->sourceItemRepository->getList($searchCriteria)->getItems(); - self::assertCount(1, $sourceItems); - - $this->bulkSourceUnassign->execute(['SKU-1'], ['eu-1']); - - $legacyStockItems = $this->legacyStockItemRepository->getList($legacyStockItemCriteria)->getItems(); - self::assertCount(1, $legacyStockItems); - - $legacyStockItem = reset($legacyStockItems); - self::assertTrue($legacyStockItem->getIsInStock()); - self::assertSame(5.5, (float) $legacyStockItem->getQty()); - } -} diff --git a/InventoryLegacySynchronization/composer.json b/InventoryLegacySynchronization/composer.json deleted file mode 100644 index e7e73f6d605c..000000000000 --- a/InventoryLegacySynchronization/composer.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "magento/module-inventory-legacy-synchronization", - "description": "N/A", - "require": { - "php": "~7.1.3||~7.2.0", - "magento/framework": "*", - "magento/module-catalog": "*", - "magento/module-inventory-indexer": "*", - "magento/module-inventory-api": "*", - "magento/module-catalog-inventory": "*", - "magento/module-inventory-catalog": "*", - "magento/module-inventory-catalog-api": "*", - "magento/module-inventory-configuration-api": "*", - "magento/module-asynchronous-operations": "*" - }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\InventoryLegacySynchronization\\": "" - } - } -} diff --git a/InventoryLegacySynchronization/etc/communication.xml b/InventoryLegacySynchronization/etc/communication.xml deleted file mode 100644 index 74fee2a4edb9..000000000000 --- a/InventoryLegacySynchronization/etc/communication.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?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:Communication/etc/communication.xsd"> - <topic name="inventory.legacy_synchronization.set_data" - request="Magento\AsynchronousOperations\Api\Data\OperationInterface" /> -</config> diff --git a/InventoryLegacySynchronization/etc/config.xml b/InventoryLegacySynchronization/etc/config.xml deleted file mode 100644 index 02f43010dcf2..000000000000 --- a/InventoryLegacySynchronization/etc/config.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?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> - <cataloginventory> - <legacy_stock> - <async>0</async> - </legacy_stock> - </cataloginventory> - </default> -</config> diff --git a/InventoryLegacySynchronization/etc/di.xml b/InventoryLegacySynchronization/etc/di.xml deleted file mode 100644 index 3569a596aa27..000000000000 --- a/InventoryLegacySynchronization/etc/di.xml +++ /dev/null @@ -1,47 +0,0 @@ -<?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\InventoryLegacySynchronization\Model\Synchronize"> - <arguments> - <argument name="batchSize" xsi:type="number">100</argument> - </arguments> - </type> - - <type name="Magento\CatalogInventory\Model\ResourceModel\QtyCounterInterface"> - <plugin name="update_source_item_at_legacy_qty_counter" - type="Magento\InventoryLegacySynchronization\Plugin\UpdateSourceItemAtLegacyQtyCounterPlugin"/> - </type> - <type name="Magento\CatalogInventory\Model\ResourceModel\Stock\Item"> - <plugin name="update_source_item_at_legacy_stock_item_save" - type="Magento\InventoryLegacySynchronization\Plugin\UpdateSourceItemAtLegacyStockItemSavePlugin"/> - <plugin name="priceIndexUpdater" disabled="true"/> - </type> - <type name="Magento\InventoryApi\Api\SourceItemsSaveInterface"> - <plugin name="set_data_to_legacy_catalog_inventory_at_source_items_save" - type="Magento\InventoryLegacySynchronization\Plugin\SetDataToLegacyCatalogInventoryAtSourceItemsSavePlugin"/> - </type> - <type name="Magento\InventoryApi\Api\SourceItemsDeleteInterface"> - <plugin name="set_to_zero_legacy_catalog_inventory_at_source_items_delete" - type="Magento\InventoryLegacySynchronization\Plugin\SetToZeroLegacyCatalogInventoryAtSourceItemsDeletePlugin"/> - </type> - - <type name="Magento\InventoryCatalogApi\Api\BulkSourceUnassignInterface"> - <plugin name="set_zero_quantity_to_legacy_at_bulk_unassign" - type="Magento\InventoryLegacySynchronization\Plugin\SetZeroQuantityToLegacyAtBulkUnassign"/> - </type> - <type name="Magento\InventoryCatalogApi\Api\BulkInventoryTransferInterface"> - <plugin name="set_data_to_legacy_catalog_inventory_at_bulk_unassign" - type="Magento\InventoryLegacySynchronization\Plugin\SetDataToLegacyCatalogInventoryAtBulkTransfer"/> - </type> - <type name="Magento\InventoryCatalog\Model\ResourceModel\TransferInventoryPartially"> - <plugin name="set_data_to_legacy_catalog_inventory_at_transfer_inventory_partial" - type="Magento\InventoryLegacySynchronization\Plugin\SetDataToLegacyCatalogInventoryAtTransferInventoryPartial"/> - </type> -</config> diff --git a/InventoryLegacySynchronization/etc/module.xml b/InventoryLegacySynchronization/etc/module.xml deleted file mode 100644 index 3f0bba258b17..000000000000 --- a/InventoryLegacySynchronization/etc/module.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?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:Module/etc/module.xsd"> - <module name="Magento_InventoryLegacySynchronization" setup_version="1.0.0"> - </module> -</config> diff --git a/InventoryLegacySynchronization/etc/queue_consumer.xml b/InventoryLegacySynchronization/etc/queue_consumer.xml deleted file mode 100644 index 0030f14e704f..000000000000 --- a/InventoryLegacySynchronization/etc/queue_consumer.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?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-message-queue:etc/consumer.xsd"> - <consumer - name="legacyInventorySynchronization" - queue="inventory_legacy_synchronization_set_data_queue" - connection="amqp" - handler="Magento\InventoryLegacySynchronization\Model\AsyncConsumer::processOperations"/> -</config> \ No newline at end of file diff --git a/InventoryLegacySynchronization/etc/queue_publisher.xml b/InventoryLegacySynchronization/etc/queue_publisher.xml deleted file mode 100644 index 9ab3c5385262..000000000000 --- a/InventoryLegacySynchronization/etc/queue_publisher.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?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-message-queue:etc/publisher.xsd"> - <publisher topic="inventory.legacy_synchronization.set_data"> - <connection name="amqp" exchange="inventory.legacy_synchronization.set_data.exchange" /> - </publisher> -</config> \ No newline at end of file diff --git a/InventoryLegacySynchronization/etc/queue_topology.xml b/InventoryLegacySynchronization/etc/queue_topology.xml deleted file mode 100644 index 91c9b72a71f0..000000000000 --- a/InventoryLegacySynchronization/etc/queue_topology.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?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-message-queue:etc/topology.xsd"> - - <exchange name="inventory.legacy_synchronization.set_data.exchange" type="topic" connection="amqp"> - <binding - id="legacyInventorySynchronizationExchangeBinding" - topic="inventory.legacy_synchronization.set_data" - destinationType="queue" - destination="inventory_legacy_synchronization_set_data_queue"/> - </exchange> -</config> \ No newline at end of file diff --git a/InventoryLegacySynchronization/registration.php b/InventoryLegacySynchronization/registration.php deleted file mode 100644 index 06eb9f828e1e..000000000000 --- a/InventoryLegacySynchronization/registration.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -\Magento\Framework\Component\ComponentRegistrar::register( - \Magento\Framework\Component\ComponentRegistrar::MODULE, - 'Magento_InventoryLegacySynchronization', - __DIR__ -); diff --git a/InventoryLegacySynchronizationAdminUi/LICENSE.txt b/InventoryLegacySynchronizationAdminUi/LICENSE.txt deleted file mode 100644 index 49525fd99da9..000000000000 --- a/InventoryLegacySynchronizationAdminUi/LICENSE.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Open Software License ("OSL") v. 3.0 - -This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Open Software License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/InventoryLegacySynchronizationAdminUi/LICENSE_AFL.txt b/InventoryLegacySynchronizationAdminUi/LICENSE_AFL.txt deleted file mode 100644 index f39d641b18a1..000000000000 --- a/InventoryLegacySynchronizationAdminUi/LICENSE_AFL.txt +++ /dev/null @@ -1,48 +0,0 @@ - -Academic Free License ("AFL") v. 3.0 - -This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: - -Licensed under the Academic Free License version 3.0 - - 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: - - 1. to reproduce the Original Work in copies, either alone or as part of a collective work; - - 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; - - 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; - - 4. to perform the Original Work publicly; and - - 5. to display the Original Work publicly. - - 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. - - 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. - - 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. - - 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). - - 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. - - 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. - - 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. - - 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). - - 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. - - 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. - - 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. - - 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. - - 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. - - 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/InventoryLegacySynchronizationAdminUi/README.md b/InventoryLegacySynchronizationAdminUi/README.md deleted file mode 100644 index 7d98cc8ee2b6..000000000000 --- a/InventoryLegacySynchronizationAdminUi/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# InventoryLegacySynchronizationAdminUi module - -The `InventoryLegacySynchronizationAdminUi` module provides admin UI integration for `InventoryLegacySynchronization` - -This module is part of the new inventory infrastructure. The -[Inventory Management overview](https://devdocs.magento.com/guides/v2.3/inventory/index.html) -describes the MSI (Multi-Source Inventory) project in more detail. - -## Installation details - -This module is installed as part of Magento Open Source. It cannot be deleted or disabled. diff --git a/InventoryLegacySynchronizationAdminUi/composer.json b/InventoryLegacySynchronizationAdminUi/composer.json deleted file mode 100644 index f291adc36ccc..000000000000 --- a/InventoryLegacySynchronizationAdminUi/composer.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "magento/module-inventory-legacy-synchronization-admin-ui", - "description": "N/A", - "require": { - "php": "~7.1.3||~7.2.0", - "magento/framework": "*" - }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\InventoryLegacySynchronizationAdminUi\\": "" - } - } -} diff --git a/InventoryLegacySynchronizationAdminUi/etc/adminhtml/system.xml b/InventoryLegacySynchronizationAdminUi/etc/adminhtml/system.xml deleted file mode 100755 index 92414b14c51c..000000000000 --- a/InventoryLegacySynchronizationAdminUi/etc/adminhtml/system.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?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_Config:etc/system_file.xsd"> - - <system> - <section id="cataloginventory"> - <group id="legacy_stock" translate="label" type="text" sortOrder="600" showInDefault="1" - showInWebsite="1" showInStore="1"> - <label>Legacy stock alignment</label> - <field id="async" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" - showInStore="0" canRestore="1"> - <label>Run asynchronously</label> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <comment>An asynchronous queue manager must be configured</comment> - </field> - </group> - </section> - </system> -</config> diff --git a/InventoryLegacySynchronizationAdminUi/etc/module.xml b/InventoryLegacySynchronizationAdminUi/etc/module.xml deleted file mode 100644 index 1f51943a3ebb..000000000000 --- a/InventoryLegacySynchronizationAdminUi/etc/module.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?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:Module/etc/module.xsd"> - <module name="Magento_InventoryLegacySynchronizationAdminUi" setup_version="1.0.0"> - </module> -</config> \ No newline at end of file diff --git a/InventoryLegacySynchronizationAdminUi/registration.php b/InventoryLegacySynchronizationAdminUi/registration.php deleted file mode 100644 index 23c0be07216f..000000000000 --- a/InventoryLegacySynchronizationAdminUi/registration.php +++ /dev/null @@ -1,12 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -\Magento\Framework\Component\ComponentRegistrar::register( - \Magento\Framework\Component\ComponentRegistrar::MODULE, - 'Magento_InventoryLegacySynchronizationAdminUi', - __DIR__ -); From fafa829babf96044bd5474fab0f2d61d19ef1bbf Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Mon, 29 Apr 2019 12:00:57 -0500 Subject: [PATCH 214/231] #2172: Rework manage stock condition --- .../IsAnySourceItemInStockCondition.php | 17 ++++++++++++++--- .../ManageStockConditionTest.php | 4 ++-- .../IsProductSalable/MinQtyConditionTest.php | 4 ++-- .../ManageStockConditionTest.php | 4 ++-- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/InventorySales/Model/IsProductSalableCondition/IsAnySourceItemInStockCondition.php b/InventorySales/Model/IsProductSalableCondition/IsAnySourceItemInStockCondition.php index 7862b101d501..a9952d66d38a 100644 --- a/InventorySales/Model/IsProductSalableCondition/IsAnySourceItemInStockCondition.php +++ b/InventorySales/Model/IsProductSalableCondition/IsAnySourceItemInStockCondition.php @@ -39,31 +39,42 @@ class IsAnySourceItemInStockCondition implements IsProductSalableInterface */ private $isSourceItemManagementAllowedForSku; + /** + * @var ManageStockCondition + */ + private $manageStockCondition; + /** * @param SourceItemRepositoryInterface $sourceItemRepository * @param SearchCriteriaBuilder $searchCriteriaBuilder * @param GetSourcesAssignedToStockOrderedByPriorityInterface $getSourcesAssignedToStockOrderedByPriority * @param IsSourceItemManagementAllowedForSkuInterface $isSourceItemManagementAllowedForSku + * @param ManageStockCondition $manageStockCondition */ public function __construct( SourceItemRepositoryInterface $sourceItemRepository, SearchCriteriaBuilder $searchCriteriaBuilder, GetSourcesAssignedToStockOrderedByPriorityInterface $getSourcesAssignedToStockOrderedByPriority, - IsSourceItemManagementAllowedForSkuInterface $isSourceItemManagementAllowedForSku + IsSourceItemManagementAllowedForSkuInterface $isSourceItemManagementAllowedForSku, + ManageStockCondition $manageStockCondition ) { $this->sourceItemRepository = $sourceItemRepository; $this->searchCriteriaBuilder = $searchCriteriaBuilder; $this->getSourcesAssignedToStockOrderedByPriority = $getSourcesAssignedToStockOrderedByPriority; $this->isSourceItemManagementAllowedForSku = $isSourceItemManagementAllowedForSku; + $this->manageStockCondition = $manageStockCondition; } /** * @inheritdoc - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function execute(string $sku, int $stockId): bool { + // TODO Must be removed once MSI-2131 is complete. + if ($this->manageStockCondition->execute($sku, $stockId)) { + return true; + } + if (!$this->isSourceItemManagementAllowedForSku->execute($sku)) { return true; } diff --git a/InventorySales/Test/Integration/IsProductSalable/ManageStockConditionTest.php b/InventorySales/Test/Integration/IsProductSalable/ManageStockConditionTest.php index 6def12f4936e..a6dcb6ec2bf6 100644 --- a/InventorySales/Test/Integration/IsProductSalable/ManageStockConditionTest.php +++ b/InventorySales/Test/Integration/IsProductSalable/ManageStockConditionTest.php @@ -64,9 +64,9 @@ public function executeWithManageStockFalseDataProvider(): array ['SKU-2', 10, false], ['SKU-2', 20, true], ['SKU-2', 30, true], - ['SKU-3', 10, false], + ['SKU-3', 10, true], ['SKU-3', 20, false], - ['SKU-3', 30, false], + ['SKU-3', 30, true], ]; } } diff --git a/InventorySales/Test/Integration/IsProductSalable/MinQtyConditionTest.php b/InventorySales/Test/Integration/IsProductSalable/MinQtyConditionTest.php index a951a63ebc63..618bce340166 100644 --- a/InventorySales/Test/Integration/IsProductSalable/MinQtyConditionTest.php +++ b/InventorySales/Test/Integration/IsProductSalable/MinQtyConditionTest.php @@ -108,9 +108,9 @@ public function executeWithManageStockFalseAndMinQty(): array ['SKU-2', 10, false], ['SKU-2', 20, true], ['SKU-2', 30, true], - ['SKU-3', 10, false], + ['SKU-3', 10, true], ['SKU-3', 20, false], - ['SKU-3', 30, false], + ['SKU-3', 30, true], ['SKU-6', 10, true], ]; } diff --git a/InventorySales/Test/Integration/IsProductSalableForRequestedQty/ManageStockConditionTest.php b/InventorySales/Test/Integration/IsProductSalableForRequestedQty/ManageStockConditionTest.php index b7562d580173..bb366064e28c 100644 --- a/InventorySales/Test/Integration/IsProductSalableForRequestedQty/ManageStockConditionTest.php +++ b/InventorySales/Test/Integration/IsProductSalableForRequestedQty/ManageStockConditionTest.php @@ -65,9 +65,9 @@ public function executeWithManageStockFalseDataProvider(): array ['SKU-2', 10, 1, false], ['SKU-2', 20, 1, true], ['SKU-2', 30, 1, true], - ['SKU-3', 10, 1, false], + ['SKU-3', 10, 1, true], ['SKU-3', 20, 1, false], - ['SKU-3', 30, 1, false], + ['SKU-3', 30, 1, true], ]; } } From 736dc2a6ecbc1f221bc850c197889e662378a0bb Mon Sep 17 00:00:00 2001 From: "al.kravchuk" <al.kravchuk@ism-ukraine.com> Date: Mon, 29 Apr 2019 23:07:59 +0300 Subject: [PATCH 215/231] MSI-2185 Improve GetNearbySourcesByPostcode API. - Make field 'country' obligatory. - Add Pickup Locations attributes mapping for webapi area. --- InventoryInStorePickup/Model/Address.php | 14 ++++----- InventoryInStorePickup/etc/webapi_rest/di.xml | 29 +++++++++++++++++++ InventoryInStorePickup/etc/webapi_soap/di.xml | 29 +++++++++++++++++++ .../Api/Data/AddressInterface.php | 4 +-- 4 files changed, 67 insertions(+), 9 deletions(-) create mode 100644 InventoryInStorePickup/etc/webapi_rest/di.xml create mode 100644 InventoryInStorePickup/etc/webapi_soap/di.xml diff --git a/InventoryInStorePickup/Model/Address.php b/InventoryInStorePickup/Model/Address.php index 74167b390cf5..a82655ebd556 100644 --- a/InventoryInStorePickup/Model/Address.php +++ b/InventoryInStorePickup/Model/Address.php @@ -16,7 +16,7 @@ class Address implements AddressInterface { /** - * @var string|null + * @var string */ private $country; @@ -36,16 +36,16 @@ class Address implements AddressInterface private $city; /*** - * @param string|null $country + * @param string $country * @param string|null $postcode * @param string|null $region * @param string|null $city */ public function __construct( - ?string $country, - ?string $postcode, - ?string $region, - ?string $city + string $country, + ?string $postcode = null, + ?string $region = null, + ?string $city = null ) { $this->country = $country; $this->postcode = $postcode; @@ -56,7 +56,7 @@ public function __construct( /** * @inheritdoc */ - public function getCountry(): ?string + public function getCountry(): string { return $this->country; } diff --git a/InventoryInStorePickup/etc/webapi_rest/di.xml b/InventoryInStorePickup/etc/webapi_rest/di.xml new file mode 100644 index 000000000000..e0e66c4a259a --- /dev/null +++ b/InventoryInStorePickup/etc/webapi_rest/di.xml @@ -0,0 +1,29 @@ +<?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\InventoryInStorePickup\Model\PickupLocation\Mapper"> + <arguments> + <argument name="map" xsi:type="array"> + <item name="source_code" xsi:type="string">source_code</item> + <item name="email" xsi:type="string">email</item> + <item name="fax" xsi:type="string">fax</item> + <item name="contact_name" xsi:type="string">contact_name</item> + <item name="description" xsi:type="string">description</item> + <item name="latitude" xsi:type="string">latitude</item> + <item name="longitude" xsi:type="string">longitude</item> + <item name="country_id" xsi:type="string">country_id</item> + <item name="region_id" xsi:type="string">region_id</item> + <item name="region" xsi:type="string">region</item> + <item name="city" xsi:type="string">city</item> + <item name="street" xsi:type="string">street</item> + <item name="postcode" xsi:type="string">postcode</item> + <item name="phone" xsi:type="string">phone</item> + </argument> + </arguments> + </type> +</config> diff --git a/InventoryInStorePickup/etc/webapi_soap/di.xml b/InventoryInStorePickup/etc/webapi_soap/di.xml new file mode 100644 index 000000000000..e0e66c4a259a --- /dev/null +++ b/InventoryInStorePickup/etc/webapi_soap/di.xml @@ -0,0 +1,29 @@ +<?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\InventoryInStorePickup\Model\PickupLocation\Mapper"> + <arguments> + <argument name="map" xsi:type="array"> + <item name="source_code" xsi:type="string">source_code</item> + <item name="email" xsi:type="string">email</item> + <item name="fax" xsi:type="string">fax</item> + <item name="contact_name" xsi:type="string">contact_name</item> + <item name="description" xsi:type="string">description</item> + <item name="latitude" xsi:type="string">latitude</item> + <item name="longitude" xsi:type="string">longitude</item> + <item name="country_id" xsi:type="string">country_id</item> + <item name="region_id" xsi:type="string">region_id</item> + <item name="region" xsi:type="string">region</item> + <item name="city" xsi:type="string">city</item> + <item name="street" xsi:type="string">street</item> + <item name="postcode" xsi:type="string">postcode</item> + <item name="phone" xsi:type="string">phone</item> + </argument> + </arguments> + </type> +</config> diff --git a/InventoryInStorePickupApi/Api/Data/AddressInterface.php b/InventoryInStorePickupApi/Api/Data/AddressInterface.php index 192fddaffb9c..2e007c2f8fbd 100644 --- a/InventoryInStorePickupApi/Api/Data/AddressInterface.php +++ b/InventoryInStorePickupApi/Api/Data/AddressInterface.php @@ -17,9 +17,9 @@ interface AddressInterface /** * Requested country * - * @return string|null + * @return string */ - public function getCountry(): ?string; + public function getCountry(): string; /** * Requested postcode From a60ee8eae458569c18bb106ee00143150aac57e4 Mon Sep 17 00:00:00 2001 From: "al.kravchuk" <al.kravchuk@ism-ukraine.com> Date: Tue, 30 Apr 2019 11:12:07 +0300 Subject: [PATCH 216/231] MSI-2185 Improve GetNearbySourcesByPostcode API. Add endpoint for receiving Pickup Locations, assigned to requested stock and sorted by corresponded sources priority. --- .../Model/GetPickupLocations.php | 60 ++++++++++++++++ .../GetNearbyPickupLocationsOfflineTest.php | 4 +- .../Test/Integration/GetPickupLocations.php | 69 +++++++++++++++++++ InventoryInStorePickup/etc/di.xml | 1 + .../Api/GetPickupLocationsInterface.php | 22 ++++++ InventoryInStorePickupApi/etc/webapi.xml | 6 ++ 6 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 InventoryInStorePickup/Model/GetPickupLocations.php create mode 100644 InventoryInStorePickup/Test/Integration/GetPickupLocations.php create mode 100644 InventoryInStorePickupApi/Api/GetPickupLocationsInterface.php diff --git a/InventoryInStorePickup/Model/GetPickupLocations.php b/InventoryInStorePickup/Model/GetPickupLocations.php new file mode 100644 index 000000000000..a7f34f5b1d3b --- /dev/null +++ b/InventoryInStorePickup/Model/GetPickupLocations.php @@ -0,0 +1,60 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickup\Model; + +use Magento\InventoryApi\Api\GetSourcesAssignedToStockOrderedByPriorityInterface; +use Magento\InventoryInStorePickup\Model\PickupLocation\Mapper; +use Magento\InventoryInStorePickupApi\Api\GetPickupLocationsInterface; + +/** + * @inheritdoc + */ +class GetPickupLocations implements GetPickupLocationsInterface +{ + /** + * @var GetSourcesAssignedToStockOrderedByPriorityInterface + */ + private $getSourcesAssignedToStockOrderedByPriority; + + /** + * @var Mapper + */ + private $mapper; + + /** + * GetPickupLocationsAssignedToStockOrderedByPriority constructor. + * + * @param GetSourcesAssignedToStockOrderedByPriorityInterface $getSourcesAssignedToStockOrderedByPriority + * @param Mapper $mapper + */ + public function __construct( + GetSourcesAssignedToStockOrderedByPriorityInterface $getSourcesAssignedToStockOrderedByPriority, + Mapper $mapper + ) { + $this->getSourcesAssignedToStockOrderedByPriority = $getSourcesAssignedToStockOrderedByPriority; + $this->mapper = $mapper; + } + + /** + * @inheritdoc + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function execute(int $stockId): array + { + $sources = $this->getSourcesAssignedToStockOrderedByPriority->execute($stockId); + + $result = []; + foreach ($sources as $source) { + if ($source->getExtensionAttributes() && $source->getExtensionAttributes()->getIsPickupLocationActive()) { + $result[] = $this->mapper->map($source); + } + } + + return $result; + } +} diff --git a/InventoryInStorePickup/Test/Integration/GetNearbyPickupLocationsOfflineTest.php b/InventoryInStorePickup/Test/Integration/GetNearbyPickupLocationsOfflineTest.php index 16a82eb25cbb..e07555885212 100644 --- a/InventoryInStorePickup/Test/Integration/GetNearbyPickupLocationsOfflineTest.php +++ b/InventoryInStorePickup/Test/Integration/GetNearbyPickupLocationsOfflineTest.php @@ -43,7 +43,7 @@ protected function setUp() * @param array $addressData * @param int $radius * @param int $stockId - * @param array $sortedSourceCodes + * @param string[] $sortedSourceCodes * * @dataProvider executeDataProvider * @magentoAppArea frontend @@ -77,7 +77,7 @@ public function testExecute( * ] * Radius (in KM), * Stock Id, - * Expected Pickup Locations Codes[] + * Expected Source Codes[] * ] * * @return array diff --git a/InventoryInStorePickup/Test/Integration/GetPickupLocations.php b/InventoryInStorePickup/Test/Integration/GetPickupLocations.php new file mode 100644 index 000000000000..f66e062d443a --- /dev/null +++ b/InventoryInStorePickup/Test/Integration/GetPickupLocations.php @@ -0,0 +1,69 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickup\Test\Integration; + +use Magento\InventoryInStorePickup\Model\GetPickupLocations as GetPickupLocationsService; +use Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +class GetPickupLocations extends TestCase +{ + /** + * @var GetPickupLocationsService + */ + private $getPickupLocations; + + protected function setUp() + { + $this->getPickupLocations = Bootstrap::getObjectManager()->get(GetPickupLocationsService::class); + } + + /** + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/sources.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryInStorePickup/Test/_files/source_addresses.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryInStorePickup/Test/_files/source_pickup_location_attributes.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stocks.php + * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/stock_source_links.php + * + * @param int $stockId + * @param string[] $sortedSourceCodes + * + * @throws \Magento\Framework\Exception\LocalizedException + * @dataProvider executeDataProvider + * @magentoAppArea frontend + * + * @magentoDbIsolation disabled + */ + public function testExecute(int $stockId, array $sortedSourceCodes) + { + /** @var PickupLocationInterface[] $sources */ + $pickupLocations = $this->getPickupLocations->execute($stockId); + + $this->assertCount(count($sortedSourceCodes), $pickupLocations); + foreach ($sortedSourceCodes as $key => $code) { + $this->assertEquals($code, $pickupLocations[$key]->getSourceCode()); + } + } + + /** + * [ + * Stock Id, + * Expected Source Codes[] + * ] + * @return array + */ + public function executeDataProvider(): array + { + return [ + [10, ['eu-1', 'eu-3']], + [20, ['us-1']], + [30, ['us-1', 'eu-3', 'eu-1']] + ]; + } +} diff --git a/InventoryInStorePickup/etc/di.xml b/InventoryInStorePickup/etc/di.xml index 5b36a39dd2d2..ea02f9c7b988 100644 --- a/InventoryInStorePickup/etc/di.xml +++ b/InventoryInStorePickup/etc/di.xml @@ -20,6 +20,7 @@ <preference for="Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface" type="Magento\InventoryInStorePickup\Model\PickupLocation" /> <preference for="Magento\InventoryInStorePickupApi\Api\GetNearbyPickupLocationsInterface" type="Magento\InventoryInStorePickup\Model\GetNearbyPickupLocations"/> <preference for="Magento\InventoryInStorePickupApi\Api\Data\AddressInterface" type="Magento\InventoryInStorePickup\Model\Address" /> + <preference for="Magento\InventoryInStorePickupApi\Api\GetPickupLocationsInterface" type="Magento\InventoryInStorePickup\Model\GetPickupLocations" /> <preference for="Magento\InventoryInStorePickupApi\Api\NotifyOrderIsReadyForPickupInterface" type="Magento\InventoryInStorePickup\Model\NotifyOrderIsReadyForPickup"/> diff --git a/InventoryInStorePickupApi/Api/GetPickupLocationsInterface.php b/InventoryInStorePickupApi/Api/GetPickupLocationsInterface.php new file mode 100644 index 000000000000..6dfb94937f4f --- /dev/null +++ b/InventoryInStorePickupApi/Api/GetPickupLocationsInterface.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickupApi\Api; + +/** + * Get Pickup Locations for requested scope, ordered by corresponded Source sort priority. + * + * @api + */ +interface GetPickupLocationsInterface +{ + /** + * @param int $stockId + * @return \Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface[] + */ + public function execute(int $stockId): array; +} diff --git a/InventoryInStorePickupApi/etc/webapi.xml b/InventoryInStorePickupApi/etc/webapi.xml index c9d2f47c52bf..57c75f773a40 100644 --- a/InventoryInStorePickupApi/etc/webapi.xml +++ b/InventoryInStorePickupApi/etc/webapi.xml @@ -13,4 +13,10 @@ <resource ref="Magento_InventoryApi::inStorePickup"/> </resources> </route> + <route url="/V1/inventory/in-store-pickup/get-pickup-locations" method="GET"> + <service class="Magento\InventoryInStorePickupApi\Api\GetPickupLocationsInterface" method="execute"/> + <resources> + <resource ref="Magento_InventoryApi::inStorePickup"/> + </resources> + </route> </routes> From f451b41c029d80f35d694d298bf84ae733205390 Mon Sep 17 00:00:00 2001 From: "al.kravchuk" <al.kravchuk@ism-ukraine.com> Date: Tue, 30 Apr 2019 18:06:44 +0300 Subject: [PATCH 217/231] MSI-2185 Improve GetNearbySourcesByPostcode API. Improve consistency. --- .../AddressToSourceSelectionAddress.php | 2 +- .../GetNearbyPickupLocationsOfflineTest.php | 20 ++++--------------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/InventoryInStorePickup/Model/Convert/AddressToSourceSelectionAddress.php b/InventoryInStorePickup/Model/Convert/AddressToSourceSelectionAddress.php index e4c3c6581a9b..dc684795cb26 100644 --- a/InventoryInStorePickup/Model/Convert/AddressToSourceSelectionAddress.php +++ b/InventoryInStorePickup/Model/Convert/AddressToSourceSelectionAddress.php @@ -39,7 +39,7 @@ public function __construct(AddressInterfaceFactory $addressInterfaceFactory) public function execute(AddressInterface $address): SourceSelectionAddressInterface { $data = [ - 'country' => $address->getCountry() ?? '', + 'country' => $address->getCountry(), 'postcode' => $address->getPostcode() ?? '', 'region' => $address->getRegion() ?? '', 'city' => $address->getCity() ??'', diff --git a/InventoryInStorePickup/Test/Integration/GetNearbyPickupLocationsOfflineTest.php b/InventoryInStorePickup/Test/Integration/GetNearbyPickupLocationsOfflineTest.php index e07555885212..255be6044c74 100644 --- a/InventoryInStorePickup/Test/Integration/GetNearbyPickupLocationsOfflineTest.php +++ b/InventoryInStorePickup/Test/Integration/GetNearbyPickupLocationsOfflineTest.php @@ -88,9 +88,7 @@ public function executeDataProvider(): array [ [ 'country' => 'DE', - 'postcode' => '81671', - 'region' => null, - 'city' => null, + 'postcode' => '81671' ], 500, 10, @@ -99,9 +97,7 @@ public function executeDataProvider(): array [ [ 'country' => 'FR', - 'postcode' => null, - 'region' => 'Bretagne', - 'city' => null + 'region' => 'Bretagne' ], 1000, 10, @@ -110,8 +106,6 @@ public function executeDataProvider(): array [ [ 'country' => 'FR', - 'postcode' => null, - 'region' => null, 'city' => 'Saint-Saturnin-lès-Apt' ], 1000, @@ -121,9 +115,7 @@ public function executeDataProvider(): array [ [ 'country' => 'IT', - 'postcode' => '12022', - 'region' => null, - 'city' => null + 'postcode' => '12022' ], 350, 10, @@ -144,8 +136,6 @@ public function executeDataProvider(): array [ 'country' => 'DE', 'postcode' => '86559', - 'region' => null, - 'city' => null ], 750, 30, @@ -154,9 +144,7 @@ public function executeDataProvider(): array [ [ 'country' => 'US', - 'postcode' => null, - 'region' => 'Kansas', - 'city' => null + 'region' => 'Kansas' ], 1000, 20, From 92482bfeef7a6885c44ccf14af8b031e5aa74221 Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Thu, 2 May 2019 15:14:31 -0500 Subject: [PATCH 218/231] remove dbRollback from suite --- InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml b/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml index a8eb4b6ba8df..136fc517bf7f 100644 --- a/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml +++ b/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml @@ -24,7 +24,6 @@ <group name="multi_mode"/> </exclude> <after> - <magentoCLI stepKey="dbRollback" command="setup:rollback" arguments="--db-file%3D%24%28ls%20..%2F..%2F..%2F..%2Fvar%2Fbackups%29%20-n" /> <magentoCLI stepKey="maintenanceDisable" command="maintenance:disable"/> <magentoCLI stepKey="disableBackup" command = "config:set system/backup/functionality_enabled 0" /> </after> @@ -44,7 +43,6 @@ <group name="single_mode"/> </exclude> <after> - <magentoCLI stepKey="dbRollback" command="setup:rollback" arguments="--db-file%3D%24%28ls%20..%2F..%2F..%2F..%2Fvar%2Fbackups%29%20-n" /> <magentoCLI stepKey="maintenanceDisable" command="maintenance:disable"/> <magentoCLI stepKey="disableBackup" command = "config:set system/backup/functionality_enabled 0" /> </after> @@ -65,7 +63,6 @@ <group name="multi_mode"/> </exclude> <after> - <magentoCLI stepKey="dbRollback" command="setup:rollback" arguments="--db-file%3D%24%28ls%20..%2F..%2F..%2F..%2Fvar%2Fbackups%29%20-n" /> <magentoCLI stepKey="maintenanceDisable" command="maintenance:disable"/> <magentoCLI stepKey="disableBackup" command = "config:set system/backup/functionality_enabled 0" /> </after> From a858e99d08fdcca7f5953f769f728aa377e87a3a Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Fri, 3 May 2019 09:29:50 -0500 Subject: [PATCH 219/231] AG AdminGoToProductGridFilterResultsByInput changes ScrollTo to ScrollToTopOfPage --- .../AdminGoToProductGridFilterResultsByInputActionGroup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InventoryAdminUi/Test/Mftf/ActionGroup/AdminGoToProductGridFilterResultsByInputActionGroup.xml b/InventoryAdminUi/Test/Mftf/ActionGroup/AdminGoToProductGridFilterResultsByInputActionGroup.xml index d1b8a75885f8..a6bf9afb4c52 100644 --- a/InventoryAdminUi/Test/Mftf/ActionGroup/AdminGoToProductGridFilterResultsByInputActionGroup.xml +++ b/InventoryAdminUi/Test/Mftf/ActionGroup/AdminGoToProductGridFilterResultsByInputActionGroup.xml @@ -53,7 +53,7 @@ <conditionalClick selector="{{AdminGridFilterControls.clearAll}}" dependentSelector=".admin__data-grid-header[data-bind='afterRender: \$data.setToolbarNode'] .admin__data-grid-filters-current._show" visible="true" stepKey="clearTheFiltersIfPresent"/> <waitForPageLoad stepKey="waitForPageLoad" time="5"/> - <scrollTo selector="{{AdminGridFilterControls.filters}}" stepKey="scrollToGridFilterControls"/> + <scrollToTopOfPage stepKey="scrollToGridFilterControls"/> <click selector="{{AdminGridFilterControls.filters}}" stepKey="clickOnFilters1"/> <fillField userInput="{{filter_value}}" selector="{{filter_selector}}" stepKey="fillCodeField2"/> <click selector="{{AdminGridFilterControls.applyFilters}}" stepKey="clickOnApplyFilters1"/> From f91a3c67c32cd70d7bea40ecd4014480df1071b2 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Mon, 6 May 2019 10:10:50 -0500 Subject: [PATCH 220/231] Fix CreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipment Test --- ...tStockAfterFullInvoiceAndPartialShipment.xml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipment.xml b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipment.xml index 8a6f0354f798..b748c8693941 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipment.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterFullInvoiceAndPartialShipment.xml @@ -27,13 +27,26 @@ <requiredEntity createDataKey="createStock"/> <requiredEntity createDataKey="createSource"/> </createData> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <waitForPageLoad stepKey="waitForDashboardLoad"/> + + <comment userInput="Assign main website to default stock" stepKey="assignChannelToStockComment"/> + <amOnPage url="{{AdminManageStockPage.url}}" stepKey="navigateToStockListPageToAssignMainWebsiteToDefaultStock"/> + <waitForPageLoad time="30" stepKey="waitForStockListPageLoad"/> + <actionGroup ref="searchAdminDataGridByKeyword" stepKey="searchDefaultStockByNameForAssignMainWebsiteChannel"> + <argument name="keyword" value="_defaultStock.name"/> + </actionGroup> + <click selector="{{AdminGridRow.editByValue(_defaultStock.name)}}" stepKey="clickEditDefaultStock"/> + <waitForPageLoad time="30" stepKey="waitForDefaultStockPageLoaded"/> + <selectOption selector="{{AdminEditStockSalesChannelsSection.websites}}" userInput="Main Website" stepKey="selectDefaultWebsiteAsSalesChannelForDefaultStock"/> + <click selector="{{AdminGridMainControls.saveAndContinue}}" stepKey="saveDefaultStock"/> + <createData entity="SimpleSubCategory" stepKey="simpleCategory"/> <createData entity="SimpleProduct" stepKey="simpleProduct"> <field key="qty">100.00</field> <requiredEntity createDataKey="simpleCategory"/> </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <waitForPageLoad stepKey="waitForDashboardLoad"/> </before> <after> <actionGroup ref="logout" stepKey="logoutOfAdmin"/> From 0ebf760d43e11fa6387a965eda228dede5f040a9 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Mon, 6 May 2019 13:11:05 -0500 Subject: [PATCH 221/231] Fix MSI-1300 and MSI-968 --- InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml | 1 + ...inConfigurableProductDisabledManageStockOnCustomStockTest.xml | 1 + ...erOrderedDownloadableProductOnCustomStockFromHomepageTest.xml | 1 + 3 files changed, 3 insertions(+) diff --git a/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml b/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml index 136fc517bf7f..13dba9b1f62e 100644 --- a/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml +++ b/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml @@ -34,6 +34,7 @@ <magentoCLI stepKey="dbBackup" command="setup:backup" arguments="--db" /> <magentoCLI stepKey="maintenanceDisable" command="maintenance:disable"/> <magentoCLI stepKey="disableWYSYWYG" command="config:set cms/wysiwyg/enabled disabled"/> + <magentoCLI stepKey="enableStockManagement" command="config:set cataloginventory/item_options/manage_stock 1"/> </before> <include> <group name="multi_mode"/> diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminConfigurableProductDisabledManageStockOnCustomStockTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminConfigurableProductDisabledManageStockOnCustomStockTest.xml index c8a1c73b8c9f..b82eda5de010 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminConfigurableProductDisabledManageStockOnCustomStockTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminConfigurableProductDisabledManageStockOnCustomStockTest.xml @@ -45,6 +45,7 @@ <click selector="{{AdminGridMainControls.saveAndContinue}}" stepKey="saveCustomStock"/> </before> <after> + <magentoCLI command="config:set cataloginventory/item_options/manage_stock 1" stepKey="reenableManageStock"/> <deleteData createDataKey="category" stepKey="deleteCategory"/> <actionGroup ref="logout" stepKey="logoutFromAdminArea"/> </after> diff --git a/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnCustomStockFromHomepageTest.xml b/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnCustomStockFromHomepageTest.xml index 39e4a6b019b3..904e069a95f7 100644 --- a/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnCustomStockFromHomepageTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/GuestCustomerOrderedDownloadableProductOnCustomStockFromHomepageTest.xml @@ -21,6 +21,7 @@ <before> <magentoCLI stepKey="enableGuestCheckoutForDownloadable" command="config:set catalog/downloadable/disable_guest_checkout 0" /> + <magentoCLI stepKey="enableStockManagement" command="config:set cataloginventory/item_options/manage_stock 1"/> <createData entity="FullSource1" stepKey="createSource"/> <createData entity="BasicMsiStockWithMainWebsite1" stepKey="createStock"/> <createData entity="SourceStockLinked1" stepKey="linkSourceStock"> From 2eb3ab3841d15cd4486dca3742a2f71c0b876db9 Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Thu, 9 May 2019 11:14:09 -0500 Subject: [PATCH 222/231] MSI-1977 Changes made to msi-suite Each suite enables stock management via CLI config DB backup/restore functionality removed from all suites --- InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml b/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml index 13dba9b1f62e..a8e643ce6728 100644 --- a/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml +++ b/InventoryAdminUi/Test/Mftf/Suite/msi-suite.xml @@ -10,8 +10,6 @@ xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Suite/etc/suiteSchema.xsd"> <suite name="MSI_Single_Mode"> <before> - <magentoCLI stepKey="enableBackup" command = "config:set system/backup/functionality_enabled 1" /> - <magentoCLI stepKey="dbBackup" command="setup:backup" arguments="--db" /> <magentoCLI stepKey="maintenanceDisable" command="maintenance:disable"/> <magentoCLI stepKey="disableWYSYWYG" command="config:set cms/wysiwyg/enabled disabled"/> <magentoCLI stepKey="enableStockManagement" command="config:set cataloginventory/item_options/manage_stock 1"/> @@ -25,13 +23,10 @@ </exclude> <after> <magentoCLI stepKey="maintenanceDisable" command="maintenance:disable"/> - <magentoCLI stepKey="disableBackup" command = "config:set system/backup/functionality_enabled 0" /> </after> </suite> <suite name="MSI_Multi_Mode"> <before> - <magentoCLI stepKey="enableBackup" command = "config:set system/backup/functionality_enabled 1" /> - <magentoCLI stepKey="dbBackup" command="setup:backup" arguments="--db" /> <magentoCLI stepKey="maintenanceDisable" command="maintenance:disable"/> <magentoCLI stepKey="disableWYSYWYG" command="config:set cms/wysiwyg/enabled disabled"/> <magentoCLI stepKey="enableStockManagement" command="config:set cataloginventory/item_options/manage_stock 1"/> @@ -45,15 +40,13 @@ </exclude> <after> <magentoCLI stepKey="maintenanceDisable" command="maintenance:disable"/> - <magentoCLI stepKey="disableBackup" command = "config:set system/backup/functionality_enabled 0" /> </after> </suite> <suite name="MSI_Sort_Order_Tests"> <before> - <magentoCLI stepKey="enableBackup" command = "config:set system/backup/functionality_enabled 1" /> - <magentoCLI stepKey="dbBackup" command="setup:backup" arguments="--db" /> <magentoCLI stepKey="maintenanceDisable" command="maintenance:disable"/> <magentoCLI stepKey="disableWYSYWYG" command="config:set cms/wysiwyg/enabled disabled"/> + <magentoCLI stepKey="enableStockManagement" command="config:set cataloginventory/item_options/manage_stock 1"/> </before> <include> <group name="sort_order_test"/> @@ -65,7 +58,6 @@ </exclude> <after> <magentoCLI stepKey="maintenanceDisable" command="maintenance:disable"/> - <magentoCLI stepKey="disableBackup" command = "config:set system/backup/functionality_enabled 0" /> </after> </suite> </suites> From 3f4381cfb8dcdf7b05cb198244ad5c4ea1ac1bc2 Mon Sep 17 00:00:00 2001 From: Tetiana Blindaruk <t.blindaruk@gmail.com> Date: Thu, 9 May 2019 21:32:47 +0300 Subject: [PATCH 223/231] [store pickup] fixed fax getter --- InventoryInStorePickup/Model/PickupLocation.php | 2 +- .../Test/Integration/PickupLocation/MapperTest.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/InventoryInStorePickup/Model/PickupLocation.php b/InventoryInStorePickup/Model/PickupLocation.php index 00ef0e11df1e..02c4d475ba66 100644 --- a/InventoryInStorePickup/Model/PickupLocation.php +++ b/InventoryInStorePickup/Model/PickupLocation.php @@ -187,7 +187,7 @@ public function getEmail(): ?string */ public function getFax(): ?string { - return $this->name; + return $this->fax; } /** diff --git a/InventoryInStorePickup/Test/Integration/PickupLocation/MapperTest.php b/InventoryInStorePickup/Test/Integration/PickupLocation/MapperTest.php index 9671b542b9ce..b2a76b8c05aa 100644 --- a/InventoryInStorePickup/Test/Integration/PickupLocation/MapperTest.php +++ b/InventoryInStorePickup/Test/Integration/PickupLocation/MapperTest.php @@ -107,6 +107,7 @@ public function testMapPickupLocation() $this->assertEquals($source->getStreet(), $pickupLocation->getStreet()); $this->assertEquals($source->getPostcode(), $pickupLocation->getPostcode()); $this->assertEquals($source->getPhone(), $pickupLocation->getPhone()); + $this->assertEquals($source->getFax(), $pickupLocation->getFax()); $this->assertInstanceOf(PickupLocationExtensionInterface::class, $pickupLocation->getExtensionAttributes()); } @@ -173,6 +174,7 @@ public function testMapPickupLocationWithExtensionAttributes() $this->assertEquals($source->getStreet(), $pickupLocation->getStreet()); $this->assertEquals($source->getPostcode(), $pickupLocation->getPostcode()); $this->assertEquals($source->getPhone(), $pickupLocation->getPhone()); + $this->assertEquals($source->getFax(), $pickupLocation->getFax()); $this->assertEquals(['open', 'hours'], $pickupLocation->getOpenHours()); } From 14924a3924d57e6f161d5e50f4fbeff008a84f04 Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Thu, 9 May 2019 14:17:13 -0500 Subject: [PATCH 224/231] MSI-1977 Work in Progress --- ...erPartialInvoiceAndPartialShipmentTest.xml | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterPartialInvoiceAndPartialShipmentTest.xml diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterPartialInvoiceAndPartialShipmentTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterPartialInvoiceAndPartialShipmentTest.xml new file mode 100644 index 000000000000..9fa5ddcef9af --- /dev/null +++ b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterPartialInvoiceAndPartialShipmentTest.xml @@ -0,0 +1,182 @@ +<?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="AdminCreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterPartialInvoiceAndPartialShipment"> + <annotations> + <stories value="MSI Credit Memo"/> + <title value="Credit Memo created with partial refund with Simple product on Default stock after partial invoice and partial shipment"/> + <description value="Credit Memo created with partial refund with Simple product on Default stock after partial invoice and partial shipment"/> + <testCaseId value="MSI-1977"/> + <severity value="BLOCKER"/> + <group value = "1977"/> + <!--<group value="msi"/>--> + <!--<group value="multi_mode"/>--> + </annotations> + + <before> + <createData entity="BasicMsiStock1" stepKey="createStock"/> + <createData entity="FullSource1" stepKey="createSource"/> + <createData entity="SourceStockLinked1" stepKey="linkSourceStock"> + <requiredEntity createDataKey="createStock"/> + <requiredEntity createDataKey="createSource"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <waitForPageLoad stepKey="waitForDashboardLoad"/> + + <comment userInput="Assign main website to default stock" stepKey="assignChannelToStockComment"/> + <amOnPage url="{{AdminManageStockPage.url}}" stepKey="navigateToStockListPageToAssignMainWebsiteToDefaultStock"/> + <waitForPageLoad time="30" stepKey="waitForStockListPageLoad"/> + <actionGroup ref="searchAdminDataGridByKeyword" stepKey="searchDefaultStockByNameForAssignMainWebsiteChannel"> + <argument name="keyword" value="_defaultStock.name"/> + </actionGroup> + <click selector="{{AdminGridRow.editByValue(_defaultStock.name)}}" stepKey="clickEditDefaultStock"/> + <waitForPageLoad time="30" stepKey="waitForDefaultStockPageLoaded"/> + <selectOption selector="{{AdminEditStockSalesChannelsSection.websites}}" userInput="Main Website" stepKey="selectDefaultWebsiteAsSalesChannelForDefaultStock"/> + <click selector="{{AdminGridMainControls.saveAndContinue}}" stepKey="saveDefaultStock"/> + + <createData entity="SimpleSubCategory" stepKey="simpleCategory"/> + <createData entity="SimpleProduct" stepKey="simpleProduct"> + <field key="qty">100.00</field> + <requiredEntity createDataKey="simpleCategory"/> + </createData> + + <createData entity="MsiCustomer1" stepKey="createCustomer"/> + + </before> + <after> + <actionGroup ref="logout" stepKey="logoutOfAdmin1"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createStock" stepKey="deleteStock"/> + <deleteData createDataKey="simpleCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> + </after> + + <!--Login To storefront as Customer--> + <comment userInput="Login to storefront as customer." stepKey="loginToStorefrontComment"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefront"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!--Purchase product once logged in--> + <comment userInput="Purchase 5 simple product" stepKey="purchaseSimpleProductComment"/> + <amOnPage url="{{StorefrontCategoryPage.url($$simpleCategory.name$$)}}" stepKey="navigateToCategoryPage"/> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($$simpleProduct.name$$)}}" stepKey="moveMouseOverProduct" /> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$simpleProduct.name$$)}}" stepKey="clickAddToCart" /> + <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart($$simpleProduct.name$$)}}" time="30" stepKey="assertMessage"/> + <waitForText userInput="1" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> + <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> + <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForViewAndEditCartVisible"/> + <clearField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct.name$$)}}" stepKey="clearField"/> + <fillField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct.name$$)}}" userInput="5" stepKey="setProductQtyToFiftyInMiniCart"/> + <click selector="{{StorefrontMinicartSection.itemQuantityUpdate($$simpleProduct.name$$)}}" stepKey="updateQtyInMiniCart"/> + <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForPaymentSelectionPageLoad"/> + <click selector=".continue" stepKey="clickOnNextPaymentPage"/> + <waitForPageLoad stepKey="waitForPageLoadCheckoutSelectPayment"/> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyOrderPayment"/> + <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButtonVisible"/> + <see selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{US_Address_TX.street[0]}}" stepKey="chooseBillingAddress"/> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="placeOrder"/> + <waitForPageLoad stepKey="waitUntilOrderPlaced"/> + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> + <see selector="{{CheckoutSuccessMainSection.success}}" userInput="Your order number is:" stepKey="checkOrderPlaceSuccessMessage"/> + + <!--Admin Area Check ordered quantity--> + <comment userInput="Admin - Check ordered quantity" stepKey="AdminCheckOrderedQuantity"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderListPage"/> + <waitForLoadingMaskToDisappear stepKey="waitOrderListPageLoad"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrder"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchCheckOrderAfterCustomerSubmits"/> + <waitForLoadingMaskToDisappear stepKey="waitFilteredOrderListPageLoad"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="navigateToOrderViewPage"/> + <waitForElementVisible selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" stepKey="waitForViewOrderedQuantity"/> + <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Ordered 15" stepKey="orderedQuantity"/> + + <!--Admin Area Check source quantity and salable quantity--> + <comment userInput="Admin - Check Source quantity and salable quantity after order placed" stepKey="AdminCheckQuantityAfterOrderPlaced"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterPlaceOrder"/> + <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterPlaceOrder"> + <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> + <argument name="value" value="$$simpleProduct.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="100" stepKey="checkProductSourceQtyAfterPlaceOrder"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="85" stepKey="checkSalableQtyAfterPlaceOrder"/> + + <!--Admin Area Process Partial Invoice--> + <comment userInput="Admin - Process partial invoice" stepKey="InvoicePartialOrder"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToProcessInvoice"/> + <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMaskInvoice"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNumInvoice"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchInvoice"/> + <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMaskGridForInvoice"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRowInvoice"/> + <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoice"/> + <scrollTo selector="{{AdminInvoiceItemsSection.updateQty}}" stepKey="scrollToQty"/> + <fillField selector="{{AdminInvoiceItemsSection.itemQtyToInvoice(1)}}" userInput="5" stepKey="InvoiceQuantityPartial" /> + <click selector="{{AdminInvoiceItemsSection.updateQty}}" stepKey="clickUpdateQty" /> + <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> + <waitForPageLoad time="60" stepKey="waitForPageLoadSubmitInvoice"/> + <scrollToTopOfPage stepKey="scrollToTopMessage"/> + <waitForPageLoad stepKey="waitForPageLoadSuccessMessage"/> + <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="checkSuccessMessage"/> + + <!--Admin Area Process Partial Shipping--> + <comment userInput="Admin - Ship partial order" stepKey="AdminShipPartialOrder"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToCreateShipment"/> + <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMask"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNum"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchShipping"/> + <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMask"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> + <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShip"/> + <waitForLoadingMaskToDisappear stepKey="waitForShipLoadingMask"/> + <fillField selector="{{AdminShipmentItemsSection.itemQtyToShip('1')}}" userInput="3" stepKey="shipPartialQuantity3"/> + <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" stepKey="submitShipment"/> + <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The shipment has been created." stepKey="seeShipmentCreateSuccess"/> + + <!--Admin Area Check source quantity and salable quantity after partial shipment--> + <comment userInput="Admin - Check Source quantity and salable quantity after partial shipment" stepKey="AdminCheckQuantityAfterPartialShipment"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterPartialShipment"/> + <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterPartialShipment"> + <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> + <argument name="value" value="$$simpleProduct.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="97" stepKey="checkProductSourceQtyAfterPartialShipment"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="85" stepKey="checkSalableQtyAfterPartialShipment"/> + + + <!--Admin Area Create Partial Credit Memo--> + <comment userInput="Admin - Create credit memo for one item of invoiced order" stepKey="AdminCreateCreditMemoPartialOrder"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPageToCreateCreditMemo"/> + <waitForLoadingMaskToDisappear stepKey="waitForOrdersPageLoadingMaskCreditMemo"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNumCreditMemo"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchCreditMemo"/> + <waitForLoadingMaskToDisappear stepKey="waitForSubmitSearchLoadingMaskCreditMemo"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRowCreditMemo"/> + <click selector="{{AdminOrderDetailsMainActionsSection.creditMemo}}" stepKey="clickCreditMemo"/> + <click selector="{{AdminCreditMemoItemsSection.itemReturnToStock('1')}}" stepKey="returnToStockCheckbox"/> + <fillField selector="{{AdminCreditMemoItemsSection.itemQtyToRefund('1')}}" userInput="5" stepKey="partialRefund"/> + <click selector="{{AdminCreditMemoItemsSection.updateQty}}" stepKey="updateQuantityToRefund"/> + <waitForLoadingMaskToDisappear stepKey="updateQuantityLoadingMask"/> + <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickSubmit"/> + + + <!--Admin Area Check quantities after Credit Memo--> + <comment userInput="Admin - Check Source quantity and salable quantity after credit memo" stepKey="AdminCheckQuantityAfterCreditMemo"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndexPageForCheckProductQtyAfterCreditMemo"/> + <actionGroup ref="AdminGridFilterSearchResultsByInput" stepKey="findVirtualProductBySkuToCheckQtyAfterCreditMemo"> + <argument name="selector" value="AdminProductGridFilterSection.skuFilter"/> + <argument name="value" value="$$simpleProduct.sku$$"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="98" stepKey="checkProductSourceQtyAfterCreditMemo"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="90" stepKey="checkSalableQtyAfterCreditMemo"/> + + </test> +</tests> \ No newline at end of file From 3b0eeb68646d5f72ba6babc2b551d810d68b57fd Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Thu, 9 May 2019 16:15:03 -0500 Subject: [PATCH 225/231] WIP --- ...tedOrderWithSimpleProductOnTestStockFromHomepageTest.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/InventoryAdminUi/Test/Mftf/Test/LoggedInCustomerCreatedOrderWithSimpleProductOnTestStockFromHomepageTest.xml b/InventoryAdminUi/Test/Mftf/Test/LoggedInCustomerCreatedOrderWithSimpleProductOnTestStockFromHomepageTest.xml index 265314722d7d..58e6d5f4c37f 100644 --- a/InventoryAdminUi/Test/Mftf/Test/LoggedInCustomerCreatedOrderWithSimpleProductOnTestStockFromHomepageTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/LoggedInCustomerCreatedOrderWithSimpleProductOnTestStockFromHomepageTest.xml @@ -94,7 +94,7 @@ <comment userInput="Admin - Check ordered quantity" stepKey="AdminCheckOrderedQuantity"/> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderListPage"/> <waitForLoadingMaskToDisappear stepKey="waitOrderListPageLoad"/> - <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrder"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grbOrderNumber}" stepKey="searchOrder"/> <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchCheckOrderAfterCustomerSubmits"/> <waitForLoadingMaskToDisappear stepKey="waitFilteredOrderListPageLoad"/> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="navigateToOrderViewPage"/> @@ -111,5 +111,9 @@ <see selector="{{AdminProductGridSection.productQtyPerSource('1',$$createSource.source[name]$$)}}" userInput="100" stepKey="checkProductSourceQtyAfterPlaceOrder"/> <see selector="{{AdminProductGridSection.productSalableQty('1',$$createStock.stock[name]$$)}}" userInput="95" stepKey="checkSalableQtyAfterPlaceOrder"/> + <!--Admin area Reorder--> + <comment userInput="Admin - Reorder" stepKey="ReorderFromAdmin"/> + + </test> </tests> From 6802312e3ae5e4d7ba122316a9f372dc92f893b4 Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Thu, 9 May 2019 16:17:57 -0500 Subject: [PATCH 226/231] Changing assertion quantities to match Hiptest defintion --- ...erPartialInvoiceAndPartialShipmentTest.xml | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterPartialInvoiceAndPartialShipmentTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterPartialInvoiceAndPartialShipmentTest.xml index 9fa5ddcef9af..bd61e2ec4025 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterPartialInvoiceAndPartialShipmentTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterPartialInvoiceAndPartialShipmentTest.xml @@ -15,9 +15,8 @@ <description value="Credit Memo created with partial refund with Simple product on Default stock after partial invoice and partial shipment"/> <testCaseId value="MSI-1977"/> <severity value="BLOCKER"/> - <group value = "1977"/> - <!--<group value="msi"/>--> - <!--<group value="multi_mode"/>--> + <group value="msi"/> + <group value="multi_mode"/> </annotations> <before> @@ -42,8 +41,8 @@ <click selector="{{AdminGridMainControls.saveAndContinue}}" stepKey="saveDefaultStock"/> <createData entity="SimpleSubCategory" stepKey="simpleCategory"/> - <createData entity="SimpleProduct" stepKey="simpleProduct"> - <field key="qty">100.00</field> + <createData entity="SimpleProductQty100" stepKey="simpleProduct"> + <!--<field key="quantity">100.00</field>--> <requiredEntity createDataKey="simpleCategory"/> </createData> @@ -74,7 +73,7 @@ <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForViewAndEditCartVisible"/> <clearField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct.name$$)}}" stepKey="clearField"/> - <fillField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct.name$$)}}" userInput="5" stepKey="setProductQtyToFiftyInMiniCart"/> + <fillField selector="{{StorefrontMinicartSection.itemQuantity($$simpleProduct.name$$)}}" userInput="10" stepKey="setProductQtyToFiftyInMiniCart"/> <click selector="{{StorefrontMinicartSection.itemQuantityUpdate($$simpleProduct.name$$)}}" stepKey="updateQtyInMiniCart"/> <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> <waitForPageLoad stepKey="waitForPaymentSelectionPageLoad"/> @@ -97,7 +96,7 @@ <waitForLoadingMaskToDisappear stepKey="waitFilteredOrderListPageLoad"/> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="navigateToOrderViewPage"/> <waitForElementVisible selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" stepKey="waitForViewOrderedQuantity"/> - <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Ordered 15" stepKey="orderedQuantity"/> + <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Ordered 10" stepKey="orderedQuantity"/> <!--Admin Area Check source quantity and salable quantity--> <comment userInput="Admin - Check Source quantity and salable quantity after order placed" stepKey="AdminCheckQuantityAfterOrderPlaced"/> @@ -107,7 +106,7 @@ <argument name="value" value="$$simpleProduct.sku$$"/> </actionGroup> <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="100" stepKey="checkProductSourceQtyAfterPlaceOrder"/> - <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="85" stepKey="checkSalableQtyAfterPlaceOrder"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="90" stepKey="checkSalableQtyAfterPlaceOrder"/> <!--Admin Area Process Partial Invoice--> <comment userInput="Admin - Process partial invoice" stepKey="InvoicePartialOrder"/> @@ -119,8 +118,9 @@ <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRowInvoice"/> <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoice"/> <scrollTo selector="{{AdminInvoiceItemsSection.updateQty}}" stepKey="scrollToQty"/> - <fillField selector="{{AdminInvoiceItemsSection.itemQtyToInvoice(1)}}" userInput="5" stepKey="InvoiceQuantityPartial" /> + <fillField selector="{{AdminInvoiceItemsSection.itemQtyToInvoice('1')}}" userInput="7" stepKey="InvoiceQuantityPartial" /> <click selector="{{AdminInvoiceItemsSection.updateQty}}" stepKey="clickUpdateQty" /> + <waitForPageLoad stepKey="WaitForInvoiceQtyUpdate"/> <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> <waitForPageLoad time="60" stepKey="waitForPageLoadSubmitInvoice"/> <scrollToTopOfPage stepKey="scrollToTopMessage"/> @@ -149,7 +149,7 @@ <argument name="value" value="$$simpleProduct.sku$$"/> </actionGroup> <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="97" stepKey="checkProductSourceQtyAfterPartialShipment"/> - <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="85" stepKey="checkSalableQtyAfterPartialShipment"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="90" stepKey="checkSalableQtyAfterPartialShipment"/> <!--Admin Area Create Partial Credit Memo--> @@ -176,7 +176,7 @@ <argument name="value" value="$$simpleProduct.sku$$"/> </actionGroup> <see selector="{{AdminProductGridSection.productQtyPerSource('1',_defaultSource.name)}}" userInput="98" stepKey="checkProductSourceQtyAfterCreditMemo"/> - <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="90" stepKey="checkSalableQtyAfterCreditMemo"/> + <see selector="{{AdminProductGridSection.productSalableQty('1',_defaultStock.name)}}" userInput="95" stepKey="checkSalableQtyAfterCreditMemo"/> </test> </tests> \ No newline at end of file From 95a488f3117274dc0ca9ce2c8f8e5d91dbd0d52e Mon Sep 17 00:00:00 2001 From: Tom Erskine <terskine@adobe.com> Date: Thu, 9 May 2019 18:40:49 -0500 Subject: [PATCH 227/231] Fixing typos in stepKey --- ...DefaultStockAfterPartialInvoiceAndPartialShipmentTest.xml | 5 +---- ...atedOrderWithSimpleProductOnTestStockFromHomepageTest.xml | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterPartialInvoiceAndPartialShipmentTest.xml b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterPartialInvoiceAndPartialShipmentTest.xml index bd61e2ec4025..3d0ff5145dcb 100644 --- a/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterPartialInvoiceAndPartialShipmentTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/AdminCreditMemoCreatedWithPartialRefundWithSimpleProductOnDefaultStockAfterPartialInvoiceAndPartialShipmentTest.xml @@ -41,13 +41,10 @@ <click selector="{{AdminGridMainControls.saveAndContinue}}" stepKey="saveDefaultStock"/> <createData entity="SimpleSubCategory" stepKey="simpleCategory"/> - <createData entity="SimpleProductQty100" stepKey="simpleProduct"> - <!--<field key="quantity">100.00</field>--> + <createData entity="SimpleProduct" stepKey="simpleProduct"> <requiredEntity createDataKey="simpleCategory"/> </createData> - <createData entity="MsiCustomer1" stepKey="createCustomer"/> - </before> <after> <actionGroup ref="logout" stepKey="logoutOfAdmin1"/> diff --git a/InventoryAdminUi/Test/Mftf/Test/LoggedInCustomerCreatedOrderWithSimpleProductOnTestStockFromHomepageTest.xml b/InventoryAdminUi/Test/Mftf/Test/LoggedInCustomerCreatedOrderWithSimpleProductOnTestStockFromHomepageTest.xml index 58e6d5f4c37f..b3d702040d3c 100644 --- a/InventoryAdminUi/Test/Mftf/Test/LoggedInCustomerCreatedOrderWithSimpleProductOnTestStockFromHomepageTest.xml +++ b/InventoryAdminUi/Test/Mftf/Test/LoggedInCustomerCreatedOrderWithSimpleProductOnTestStockFromHomepageTest.xml @@ -94,7 +94,7 @@ <comment userInput="Admin - Check ordered quantity" stepKey="AdminCheckOrderedQuantity"/> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderListPage"/> <waitForLoadingMaskToDisappear stepKey="waitOrderListPageLoad"/> - <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grbOrderNumber}" stepKey="searchOrder"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrder"/> <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchCheckOrderAfterCustomerSubmits"/> <waitForLoadingMaskToDisappear stepKey="waitFilteredOrderListPageLoad"/> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="navigateToOrderViewPage"/> From 8fa43d5c4809c8434a299a91799e61007ddfafa1 Mon Sep 17 00:00:00 2001 From: Tetiana Blindaruk <t.blindaruk@gmail.com> Date: Fri, 10 May 2019 23:06:13 +0300 Subject: [PATCH 228/231] [store pickup] add return type to the setExtensionAttributes method [store pickup] delete interface usage, since it is already are used in the parent class [store pickup] delete unneded line in app/code/Magento/InventoryInStorePickup/Plugin/InventoryApi/SourceRepository/LoadInStorePickupOnGetPlugin.php php doc [store pickup] fixed app/code/Magento/InventoryInStorePickup/Plugin/Sales/Order/GetPickupLocationForOrderPlugin.php - since getExtensionAttributes can return interface and null. [store pickup] fixed app/code/Magento/InventoryInStorePickup/Plugin/Sales/Order/SavePickupLocationForOrderPlugin.php - since getExtensionAttributes can return interface and null. --- .../Model/Order/Email/Container/ReadyForPickupIdentity.php | 3 +-- InventoryInStorePickup/Model/PickupLocation.php | 2 +- .../SourceRepository/LoadInStorePickupOnGetPlugin.php | 2 -- .../Plugin/Sales/Order/GetPickupLocationForOrderPlugin.php | 2 +- .../Plugin/Sales/Order/SavePickupLocationForOrderPlugin.php | 2 +- InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php | 2 +- 6 files changed, 5 insertions(+), 8 deletions(-) diff --git a/InventoryInStorePickup/Model/Order/Email/Container/ReadyForPickupIdentity.php b/InventoryInStorePickup/Model/Order/Email/Container/ReadyForPickupIdentity.php index c546ae82b0f0..71af685545b5 100644 --- a/InventoryInStorePickup/Model/Order/Email/Container/ReadyForPickupIdentity.php +++ b/InventoryInStorePickup/Model/Order/Email/Container/ReadyForPickupIdentity.php @@ -8,13 +8,12 @@ namespace Magento\InventoryInStorePickup\Model\Order\Email\Container; use Magento\Sales\Model\Order\Email\Container\Container; -use Magento\Sales\Model\Order\Email\Container\IdentityInterface; use Magento\Store\Model\ScopeInterface; /** * @inheritdoc */ -class ReadyForPickupIdentity extends Container implements IdentityInterface +class ReadyForPickupIdentity extends Container { /** * Configuration paths diff --git a/InventoryInStorePickup/Model/PickupLocation.php b/InventoryInStorePickup/Model/PickupLocation.php index 02c4d475ba66..6969e74a553c 100644 --- a/InventoryInStorePickup/Model/PickupLocation.php +++ b/InventoryInStorePickup/Model/PickupLocation.php @@ -289,7 +289,7 @@ public function getOpenHours(): ?array /** * @inheritdoc */ - public function setExtensionAttributes(?PickupLocationExtensionInterface $extensionAttributes) + public function setExtensionAttributes(?PickupLocationExtensionInterface $extensionAttributes): void { $this->extensionAttributes = $extensionAttributes; } diff --git a/InventoryInStorePickup/Plugin/InventoryApi/SourceRepository/LoadInStorePickupOnGetPlugin.php b/InventoryInStorePickup/Plugin/InventoryApi/SourceRepository/LoadInStorePickupOnGetPlugin.php index 1e324b0231ab..e4e947154402 100644 --- a/InventoryInStorePickup/Plugin/InventoryApi/SourceRepository/LoadInStorePickupOnGetPlugin.php +++ b/InventoryInStorePickup/Plugin/InventoryApi/SourceRepository/LoadInStorePickupOnGetPlugin.php @@ -20,8 +20,6 @@ class LoadInStorePickupOnGetPlugin private $extensionAttributesFactory; /** - - * * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionAttributesFactory */ public function __construct(ExtensionAttributesFactory $extensionAttributesFactory) diff --git a/InventoryInStorePickup/Plugin/Sales/Order/GetPickupLocationForOrderPlugin.php b/InventoryInStorePickup/Plugin/Sales/Order/GetPickupLocationForOrderPlugin.php index 84c026a819c2..9db347a5848e 100644 --- a/InventoryInStorePickup/Plugin/Sales/Order/GetPickupLocationForOrderPlugin.php +++ b/InventoryInStorePickup/Plugin/Sales/Order/GetPickupLocationForOrderPlugin.php @@ -52,7 +52,7 @@ public function afterGet(OrderRepositoryInterface $orderRepository, OrderInterfa { $extension = $order->getExtensionAttributes(); - if (empty($extension)) { + if (null === $extension) { $extension = $this->orderExtensionFactory->create(); } diff --git a/InventoryInStorePickup/Plugin/Sales/Order/SavePickupLocationForOrderPlugin.php b/InventoryInStorePickup/Plugin/Sales/Order/SavePickupLocationForOrderPlugin.php index 982df1c28e5b..cd536a737d17 100644 --- a/InventoryInStorePickup/Plugin/Sales/Order/SavePickupLocationForOrderPlugin.php +++ b/InventoryInStorePickup/Plugin/Sales/Order/SavePickupLocationForOrderPlugin.php @@ -46,7 +46,7 @@ public function afterSave( ) { $extension = $result->getExtensionAttributes(); - if (!empty($extension) && $extension->getPickupLocationCode()) { + if (null!== $extension && $extension->getPickupLocationCode()) { $this->saveOrderPickupLocation->execute((int)$result->getEntityId(), $extension->getPickupLocationCode()); } diff --git a/InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php b/InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php index 14fa3c743834..11d801ef2147 100644 --- a/InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php +++ b/InventoryInStorePickupApi/Api/Data/PickupLocationInterface.php @@ -139,7 +139,7 @@ public function getOpenHours(): ?array; * * @return void */ - public function setExtensionAttributes(?PickupLocationExtensionInterface $extensionAttributes); + public function setExtensionAttributes(?PickupLocationExtensionInterface $extensionAttributes): void; /** * Get Extension Attributes of Pickup Location. From 3bde2848b3aaed0c8a6f0e25def13c46712a3e9c Mon Sep 17 00:00:00 2001 From: Maksym Novik <novik.kor@gmail.com> Date: Sat, 11 May 2019 20:05:27 +0300 Subject: [PATCH 229/231] SSA. Created an extension point for getting source item qty available. --- .../Result/GetDefaultSortedSourcesResult.php | 23 +++++++++++++---- .../GetSourceItemQtyAvailableInterface.php | 24 ++++++++++++++++++ .../GetSourceItemQtyAvailableService.php | 25 +++++++++++++++++++ InventorySourceSelectionApi/etc/di.xml | 3 ++- 4 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 InventorySourceSelectionApi/Model/GetSourceItemQtyAvailableInterface.php create mode 100644 InventorySourceSelectionApi/Model/GetSourceItemQtyAvailableService.php diff --git a/InventorySourceSelectionApi/Model/Algorithms/Result/GetDefaultSortedSourcesResult.php b/InventorySourceSelectionApi/Model/Algorithms/Result/GetDefaultSortedSourcesResult.php index 9c8d04c6b5e2..3db55011d7d7 100644 --- a/InventorySourceSelectionApi/Model/Algorithms/Result/GetDefaultSortedSourcesResult.php +++ b/InventorySourceSelectionApi/Model/Algorithms/Result/GetDefaultSortedSourcesResult.php @@ -3,17 +3,19 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +/** @noinspection PhpUnusedParameterInspection */ declare(strict_types=1); namespace Magento\InventorySourceSelectionApi\Model\Algorithms\Result; use Magento\Framework\App\ObjectManager; use Magento\InventoryApi\Api\Data\SourceInterface; -use Magento\InventorySourceSelectionApi\Model\GetInStockSourceItemsBySkusAndSortedSource; use Magento\InventorySourceSelectionApi\Api\Data\InventoryRequestInterface; -use Magento\InventorySourceSelectionApi\Api\Data\SourceSelectionResultInterface; use Magento\InventorySourceSelectionApi\Api\Data\SourceSelectionItemInterfaceFactory; +use Magento\InventorySourceSelectionApi\Api\Data\SourceSelectionResultInterface; use Magento\InventorySourceSelectionApi\Api\Data\SourceSelectionResultInterfaceFactory; +use Magento\InventorySourceSelectionApi\Model\GetInStockSourceItemsBySkusAndSortedSource; +use Magento\InventorySourceSelectionApi\Model\GetSourceItemQtyAvailableInterface; /** * Return a default response for sorted source algorithms @@ -35,25 +37,35 @@ class GetDefaultSortedSourcesResult */ private $getInStockSourceItemsBySkusAndSortedSource; + /** + * @var GetSourceItemQtyAvailableInterface + */ + private $getSourceItemQtyAvailable; + /** * @param SourceSelectionItemInterfaceFactory $sourceSelectionItemFactory * @param SourceSelectionResultInterfaceFactory $sourceSelectionResultFactory * @param null $searchCriteriaBuilder @deprecated * @param null $sourceItemRepository @deprecated * @param GetInStockSourceItemsBySkusAndSortedSource $getInStockSourceItemsBySkusAndSortedSource = null + * @param GetSourceItemQtyAvailableInterface|null $getSourceItemQtyAvailable * @SuppressWarnings(PHPMD.LongVariable) + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( SourceSelectionItemInterfaceFactory $sourceSelectionItemFactory, SourceSelectionResultInterfaceFactory $sourceSelectionResultFactory, $searchCriteriaBuilder, $sourceItemRepository, - GetInStockSourceItemsBySkusAndSortedSource $getInStockSourceItemsBySkusAndSortedSource = null + GetInStockSourceItemsBySkusAndSortedSource $getInStockSourceItemsBySkusAndSortedSource = null, + GetSourceItemQtyAvailableInterface $getSourceItemQtyAvailable = null ) { $this->sourceSelectionItemFactory = $sourceSelectionItemFactory; $this->sourceSelectionResultFactory = $sourceSelectionResultFactory; $this->getInStockSourceItemsBySkusAndSortedSource = $getInStockSourceItemsBySkusAndSortedSource ?: ObjectManager::getInstance()->get(GetInStockSourceItemsBySkusAndSortedSource::class); + $this->getSourceItemQtyAvailable = $getSourceItemQtyAvailable ?? + ObjectManager::getInstance()->get(GetSourceItemQtyAvailableInterface::class); } /** @@ -98,13 +110,14 @@ public function execute( ); foreach ($sourceItems as $sourceItem) { - $qtyToDeduct = min($sourceItem->getQuantity(), $itemsTdDeliver[$sourceItem->getSku()] ?? 0.0); + $sourceItemQtyAvailable = $this->getSourceItemQtyAvailable->execute($sourceItem); + $qtyToDeduct = min($sourceItemQtyAvailable, $itemsTdDeliver[$sourceItem->getSku()] ?? 0.0); $sourceItemSelections[] = $this->sourceSelectionItemFactory->create([ 'sourceCode' => $sourceItem->getSourceCode(), 'sku' => $sourceItem->getSku(), 'qtyToDeduct' => $qtyToDeduct, - 'qtyAvailable' => $sourceItem->getQuantity() + 'qtyAvailable' => $sourceItemQtyAvailable ]); $itemsTdDeliver[$sourceItem->getSku()] -= $qtyToDeduct; diff --git a/InventorySourceSelectionApi/Model/GetSourceItemQtyAvailableInterface.php b/InventorySourceSelectionApi/Model/GetSourceItemQtyAvailableInterface.php new file mode 100644 index 000000000000..aade3d630db0 --- /dev/null +++ b/InventorySourceSelectionApi/Model/GetSourceItemQtyAvailableInterface.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\InventorySourceSelectionApi\Model; + +use Magento\InventoryApi\Api\Data\SourceItemInterface; + +/** + * Get source item qty available for usage in SSA + * + * @api + */ +interface GetSourceItemQtyAvailableInterface +{ + /** + * @param SourceItemInterface $sourceItem + * + * @return float + */ + public function execute(SourceItemInterface $sourceItem): float; +} diff --git a/InventorySourceSelectionApi/Model/GetSourceItemQtyAvailableService.php b/InventorySourceSelectionApi/Model/GetSourceItemQtyAvailableService.php new file mode 100644 index 000000000000..afb02d40cd56 --- /dev/null +++ b/InventorySourceSelectionApi/Model/GetSourceItemQtyAvailableService.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventorySourceSelectionApi\Model; + +use Magento\InventoryApi\Api\Data\SourceItemInterface; + +/** + * Get source item qty available for usage in SSA + * Default implementation that returns source item qty without any modifications + */ +class GetSourceItemQtyAvailableService implements GetSourceItemQtyAvailableInterface +{ + /** + * @inheritDoc + */ + public function execute(SourceItemInterface $sourceItem): float + { + return $sourceItem->getQuantity(); + } +} diff --git a/InventorySourceSelectionApi/etc/di.xml b/InventorySourceSelectionApi/etc/di.xml index ddf9ef02e038..aa914deaaf27 100644 --- a/InventorySourceSelectionApi/etc/di.xml +++ b/InventorySourceSelectionApi/etc/di.xml @@ -11,7 +11,8 @@ type="Magento\InventorySourceSelectionApi\Model\GetSourceSelectionAlgorithmList"/> <preference for="Magento\InventorySourceSelectionApi\Api\SourceSelectionServiceInterface" type="Magento\InventorySourceSelectionApi\Model\SourceSelectionService"/> - + <preference for="Magento\InventorySourceSelectionApi\Model\GetSourceItemQtyAvailableInterface" + type="Magento\InventorySourceSelectionApi\Model\GetSourceItemQtyAvailableService"/> <type name="Magento\InventorySourceSelectionApi\Model\GetSourceSelectionAlgorithmList"> <arguments> <argument name="availableAlgorithms" xsi:type="array"> From c1e74985403d7fe689d447d2113d2f1884904439 Mon Sep 17 00:00:00 2001 From: Eugene Shakhsuvarov <shakhsuv@adobe.com> Date: Mon, 13 May 2019 23:27:48 -0500 Subject: [PATCH 230/231] Make yoda condition coding standards compliant --- .../Plugin/Sales/Order/SavePickupLocationForOrderPlugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InventoryInStorePickup/Plugin/Sales/Order/SavePickupLocationForOrderPlugin.php b/InventoryInStorePickup/Plugin/Sales/Order/SavePickupLocationForOrderPlugin.php index cd536a737d17..cb44d9bdc09e 100644 --- a/InventoryInStorePickup/Plugin/Sales/Order/SavePickupLocationForOrderPlugin.php +++ b/InventoryInStorePickup/Plugin/Sales/Order/SavePickupLocationForOrderPlugin.php @@ -46,7 +46,7 @@ public function afterSave( ) { $extension = $result->getExtensionAttributes(); - if (null!== $extension && $extension->getPickupLocationCode()) { + if (null !== $extension && $extension->getPickupLocationCode()) { $this->saveOrderPickupLocation->execute((int)$result->getEntityId(), $extension->getPickupLocationCode()); } From a420ecc4f71557420377dc1780925b9e28b85db6 Mon Sep 17 00:00:00 2001 From: "al.kravchuk" <al.kravchuk@ism-ukraine.com> Date: Tue, 14 May 2019 13:55:05 +0300 Subject: [PATCH 231/231] MSI-2185 Improve GetNearbySourcesByPostcode API. Apply refactoring according to the code review request. --- InventoryInStorePickup/Model/Address.php | 4 +-- .../AddressToSourceSelectionAddress.php | 14 ++++---- .../Model/GetNearbyPickupLocations.php | 32 +++++++++---------- ...tionsAssignedToStockOrderedByPriority.php} | 11 +++---- .../Mapper/CreateFromSource.php | 15 +++------ .../Source/GetDistanceOrderedSourceCodes.php | 10 +++--- ...sAssignedToStockOrderedByPriorityTest.php} | 10 +++--- .../Integration/PickupLocation/MapperTest.php | 16 +++++----- InventoryInStorePickup/etc/di.xml | 3 +- InventoryInStorePickup/etc/frontend/di.xml | 29 ----------------- InventoryInStorePickup/etc/webapi_rest/di.xml | 29 ----------------- .../Api/GetNearbyPickupLocationsInterface.php | 2 +- ...gnedToStockOrderedByPriorityInterface.php} | 2 +- .../Model}/Mapper.php | 16 ++++++---- .../Mapper/CreateFromSourceInterface.php | 32 +++++++++++++++++++ InventoryInStorePickupApi/composer.json | 3 +- .../etc}/di.xml | 2 +- InventoryInStorePickupApi/etc/module.xml | 6 +++- InventoryInStorePickupApi/etc/webapi.xml | 6 ++-- 19 files changed, 107 insertions(+), 135 deletions(-) rename InventoryInStorePickup/Model/{GetPickupLocations.php => GetPickupLocationsAssignedToStockOrderedByPriority.php} (79%) rename InventoryInStorePickup/Test/Integration/{GetPickupLocations.php => GetPickupLocationsAssignedToStockOrderedByPriorityTest.php} (86%) delete mode 100644 InventoryInStorePickup/etc/frontend/di.xml delete mode 100644 InventoryInStorePickup/etc/webapi_rest/di.xml rename InventoryInStorePickupApi/Api/{GetPickupLocationsInterface.php => GetPickupLocationsAssignedToStockOrderedByPriorityInterface.php} (87%) rename {InventoryInStorePickup/Model/PickupLocation => InventoryInStorePickupApi/Model}/Mapper.php (63%) create mode 100644 InventoryInStorePickupApi/Model/Mapper/CreateFromSourceInterface.php rename {InventoryInStorePickup/etc/webapi_soap => InventoryInStorePickupApi/etc}/di.xml (94%) diff --git a/InventoryInStorePickup/Model/Address.php b/InventoryInStorePickup/Model/Address.php index a82655ebd556..1c60be197149 100644 --- a/InventoryInStorePickup/Model/Address.php +++ b/InventoryInStorePickup/Model/Address.php @@ -10,7 +10,7 @@ use Magento\InventoryInStorePickupApi\Api\Data\AddressInterface; /** - * @inheritdoc + * {@inheritdoc} * @codeCoverageIgnore */ class Address implements AddressInterface @@ -35,7 +35,7 @@ class Address implements AddressInterface */ private $city; - /*** + /** * @param string $country * @param string|null $postcode * @param string|null $region diff --git a/InventoryInStorePickup/Model/Convert/AddressToSourceSelectionAddress.php b/InventoryInStorePickup/Model/Convert/AddressToSourceSelectionAddress.php index dc684795cb26..d5998cf8f8cc 100644 --- a/InventoryInStorePickup/Model/Convert/AddressToSourceSelectionAddress.php +++ b/InventoryInStorePickup/Model/Convert/AddressToSourceSelectionAddress.php @@ -7,7 +7,7 @@ namespace Magento\InventoryInStorePickup\Model\Convert; -use Magento\InventoryInStorePickupApi\Api\Data\AddressInterface; +use Magento\InventoryInStorePickupApi\Api\Data\AddressInterface as PickupLocationsRequestAddressInterface; use Magento\InventorySourceSelectionApi\Api\Data\AddressInterface as SourceSelectionAddressInterface; use Magento\InventorySourceSelectionApi\Api\Data\AddressInterfaceFactory; @@ -17,14 +17,12 @@ class AddressToSourceSelectionAddress { /** - * @var \Magento\InventorySourceSelectionApi\Api\Data\AddressInterfaceFactory + * @var AddressInterfaceFactory */ private $addressInterfaceFactory; /** - * AddressToSourceSelectionAddress constructor. - * - * @param \Magento\InventorySourceSelectionApi\Api\Data\AddressInterfaceFactory $addressInterfaceFactory + * @param AddressInterfaceFactory $addressInterfaceFactory */ public function __construct(AddressInterfaceFactory $addressInterfaceFactory) { @@ -32,11 +30,11 @@ public function __construct(AddressInterfaceFactory $addressInterfaceFactory) } /** - * @param \Magento\InventoryInStorePickupApi\Api\Data\AddressInterface $address + * @param PickupLocationsRequestAddressInterface $address * - * @return \Magento\InventorySourceSelectionApi\Api\Data\AddressInterface + * @return SourceSelectionAddressInterface */ - public function execute(AddressInterface $address): SourceSelectionAddressInterface + public function execute(PickupLocationsRequestAddressInterface $address): SourceSelectionAddressInterface { $data = [ 'country' => $address->getCountry(), diff --git a/InventoryInStorePickup/Model/GetNearbyPickupLocations.php b/InventoryInStorePickup/Model/GetNearbyPickupLocations.php index b63c15c70b61..b2c93e565e33 100644 --- a/InventoryInStorePickup/Model/GetNearbyPickupLocations.php +++ b/InventoryInStorePickup/Model/GetNearbyPickupLocations.php @@ -14,60 +14,60 @@ use Magento\InventoryApi\Api\SourceRepositoryInterface; use Magento\InventoryDistanceBasedSourceSelectionApi\Api\GetLatLngFromAddressInterface; use Magento\InventoryInStorePickup\Model\Convert\AddressToSourceSelectionAddress; -use Magento\InventoryInStorePickup\Model\PickupLocation\Mapper; use Magento\InventoryInStorePickup\Model\ResourceModel\Source\GetDistanceOrderedSourceCodes; use Magento\InventoryInStorePickupApi\Api\Data\AddressInterface; use Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface; use Magento\InventoryInStorePickupApi\Api\GetNearbyPickupLocationsInterface; +use Magento\InventoryInStorePickupApi\Model\Mapper; /** - * Find nearest Pickup Locations by postal code using Haversine formula (Great Circle Distance) database query. + * @inheritdoc */ class GetNearbyPickupLocations implements GetNearbyPickupLocationsInterface { /** - * @var \Magento\InventoryInStorePickup\Model\PickupLocation\Mapper + * @var Mapper */ private $mapper; /** - * @var \Magento\InventoryInStorePickup\Model\Convert\AddressToSourceSelectionAddress + * @var AddressToSourceSelectionAddress */ private $addressToSourceSelectionAddress; /** - * @var \Magento\InventoryDistanceBasedSourceSelectionApi\Api\GetLatLngFromAddressInterface + * @var GetLatLngFromAddressInterface */ private $getLatLngFromAddress; /** - * @var \Magento\InventoryInStorePickup\Model\ResourceModel\Source\GetDistanceOrderedSourceCodes + * @var GetDistanceOrderedSourceCodes */ private $getDistanceOrderedSourceCodes; /** - * @var \Magento\InventoryApi\Api\GetStockSourceLinksInterface + * @var GetStockSourceLinksInterface */ private $getStockSourceLinks; /** - * @var \Magento\Framework\Api\SearchCriteriaBuilder + * @var SearchCriteriaBuilder */ private $searchCriteriaBuilder; /** - * @var \Magento\InventoryApi\Api\SourceRepositoryInterface + * @var SourceRepositoryInterface */ private $sourceRepository; /** - * @param \Magento\InventoryInStorePickup\Model\PickupLocation\Mapper $mapper - * @param \Magento\InventoryInStorePickup\Model\Convert\AddressToSourceSelectionAddress $addressToSourceSelectionAddress - * @param \Magento\InventoryDistanceBasedSourceSelectionApi\Api\GetLatLngFromAddressInterface $getLatLngFromAddress - * @param \Magento\InventoryInStorePickup\Model\ResourceModel\Source\GetDistanceOrderedSourceCodes $getDistanceOrderedSourceCodes - * @param \Magento\InventoryApi\Api\GetStockSourceLinksInterface $getStockSourceLinks - * @param \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder - * @param \Magento\InventoryApi\Api\SourceRepositoryInterface $sourceRepository + * @param Mapper $mapper + * @param AddressToSourceSelectionAddress $addressToSourceSelectionAddress + * @param GetLatLngFromAddressInterface $getLatLngFromAddress + * @param GetDistanceOrderedSourceCodes $getDistanceOrderedSourceCodes + * @param GetStockSourceLinksInterface $getStockSourceLinks + * @param SearchCriteriaBuilder $searchCriteriaBuilder + * @param SourceRepositoryInterface $sourceRepository */ public function __construct( Mapper $mapper, diff --git a/InventoryInStorePickup/Model/GetPickupLocations.php b/InventoryInStorePickup/Model/GetPickupLocationsAssignedToStockOrderedByPriority.php similarity index 79% rename from InventoryInStorePickup/Model/GetPickupLocations.php rename to InventoryInStorePickup/Model/GetPickupLocationsAssignedToStockOrderedByPriority.php index a7f34f5b1d3b..6fc79bd2301e 100644 --- a/InventoryInStorePickup/Model/GetPickupLocations.php +++ b/InventoryInStorePickup/Model/GetPickupLocationsAssignedToStockOrderedByPriority.php @@ -7,14 +7,15 @@ namespace Magento\InventoryInStorePickup\Model; +use Magento\Framework\Exception\LocalizedException; use Magento\InventoryApi\Api\GetSourcesAssignedToStockOrderedByPriorityInterface; -use Magento\InventoryInStorePickup\Model\PickupLocation\Mapper; -use Magento\InventoryInStorePickupApi\Api\GetPickupLocationsInterface; +use Magento\InventoryInStorePickupApi\Model\Mapper; +use Magento\InventoryInStorePickupApi\Api\GetPickupLocationsAssignedToStockOrderedByPriorityInterface; /** * @inheritdoc */ -class GetPickupLocations implements GetPickupLocationsInterface +class GetPickupLocationsAssignedToStockOrderedByPriority implements GetPickupLocationsAssignedToStockOrderedByPriorityInterface { /** * @var GetSourcesAssignedToStockOrderedByPriorityInterface @@ -27,8 +28,6 @@ class GetPickupLocations implements GetPickupLocationsInterface private $mapper; /** - * GetPickupLocationsAssignedToStockOrderedByPriority constructor. - * * @param GetSourcesAssignedToStockOrderedByPriorityInterface $getSourcesAssignedToStockOrderedByPriority * @param Mapper $mapper */ @@ -42,7 +41,7 @@ public function __construct( /** * @inheritdoc - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException */ public function execute(int $stockId): array { diff --git a/InventoryInStorePickup/Model/PickupLocation/Mapper/CreateFromSource.php b/InventoryInStorePickup/Model/PickupLocation/Mapper/CreateFromSource.php index cf949f4c63d4..58a6ef55716c 100644 --- a/InventoryInStorePickup/Model/PickupLocation/Mapper/CreateFromSource.php +++ b/InventoryInStorePickup/Model/PickupLocation/Mapper/CreateFromSource.php @@ -12,12 +12,12 @@ use Magento\InventoryApi\Api\Data\SourceInterface; use Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface; use Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterfaceFactory; +use Magento\InventoryInStorePickupApi\Model\Mapper\CreateFromSourceInterface; /** - * Create Pickup Location based on Source. - * Transport data from Source to Pickup Location according to provided mapping. + * @inheritdoc */ -class CreateFromSource +class CreateFromSource implements CreateFromSourceInterface { /** * @var PickupLocationInterfaceFactory @@ -44,15 +44,8 @@ public function __construct( } /** - * @param SourceInterface $source - * @param array $map May contains references to fields in extension attributes. - * Please use format 'extension_attributes.field_name' to do so. E.g. - * [ - * "extension_attributes.source_field" => "pickup_location_field" - * "extension_attributes.source_field" => "extension_attributes.pickup_location_extension_field", - * ] + * @inheritdoc * @throws \InvalidArgumentException - * @return PickupLocationInterface */ public function execute(SourceInterface $source, array $map): PickupLocationInterface { diff --git a/InventoryInStorePickup/Model/ResourceModel/Source/GetDistanceOrderedSourceCodes.php b/InventoryInStorePickup/Model/ResourceModel/Source/GetDistanceOrderedSourceCodes.php index 058372bb6b98..ff74e0f4473f 100644 --- a/InventoryInStorePickup/Model/ResourceModel/Source/GetDistanceOrderedSourceCodes.php +++ b/InventoryInStorePickup/Model/ResourceModel/Source/GetDistanceOrderedSourceCodes.php @@ -12,21 +12,19 @@ use Magento\InventoryDistanceBasedSourceSelectionApi\Api\Data\LatLngInterface; /** - * Get Source Codes, ordered by distance to request coordinates. + * Get Source Codes, ordered by distance to request coordinates using Haversine formula (Great Circle Distance) database query. */ class GetDistanceOrderedSourceCodes { private const EARTH_RADIUS_KM = 6372.797; /** - * @var \Magento\Framework\App\ResourceConnection + * @var ResourceConnection */ private $resourceConnection; /** - * GetDistanceOrderedSourceCodes constructor. - * - * @param \Magento\Framework\App\ResourceConnection $resourceConnection + * @param ResourceConnection $resourceConnection */ public function __construct(ResourceConnection $resourceConnection) { @@ -34,7 +32,7 @@ public function __construct(ResourceConnection $resourceConnection) } /** - * @param \Magento\InventoryDistanceBasedSourceSelectionApi\Api\Data\LatLngInterface $latLng + * @param LatLngInterface $latLng * @param int $radius * * @return string[] diff --git a/InventoryInStorePickup/Test/Integration/GetPickupLocations.php b/InventoryInStorePickup/Test/Integration/GetPickupLocationsAssignedToStockOrderedByPriorityTest.php similarity index 86% rename from InventoryInStorePickup/Test/Integration/GetPickupLocations.php rename to InventoryInStorePickup/Test/Integration/GetPickupLocationsAssignedToStockOrderedByPriorityTest.php index f66e062d443a..0e31a2a76006 100644 --- a/InventoryInStorePickup/Test/Integration/GetPickupLocations.php +++ b/InventoryInStorePickup/Test/Integration/GetPickupLocationsAssignedToStockOrderedByPriorityTest.php @@ -7,21 +7,23 @@ namespace Magento\InventoryInStorePickup\Test\Integration; -use Magento\InventoryInStorePickup\Model\GetPickupLocations as GetPickupLocationsService; use Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface; +use Magento\InventoryInStorePickup\Model\GetPickupLocationsAssignedToStockOrderedByPriority; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; -class GetPickupLocations extends TestCase +class GetPickupLocationsAssignedToStockOrderedByPriorityTest extends TestCase { /** - * @var GetPickupLocationsService + * @var GetPickupLocationsAssignedToStockOrderedByPriority */ private $getPickupLocations; protected function setUp() { - $this->getPickupLocations = Bootstrap::getObjectManager()->get(GetPickupLocationsService::class); + $this->getPickupLocations = Bootstrap::getObjectManager()->get( + GetPickupLocationsAssignedToStockOrderedByPriority::class + ); } /** diff --git a/InventoryInStorePickup/Test/Integration/PickupLocation/MapperTest.php b/InventoryInStorePickup/Test/Integration/PickupLocation/MapperTest.php index 9671b542b9ce..a0f3b9239832 100644 --- a/InventoryInStorePickup/Test/Integration/PickupLocation/MapperTest.php +++ b/InventoryInStorePickup/Test/Integration/PickupLocation/MapperTest.php @@ -10,8 +10,8 @@ use Magento\Framework\Api\ExtensionAttributesFactory; use Magento\InventoryApi\Api\Data\SourceExtensionInterface; use Magento\InventoryApi\Api\SourceRepositoryInterface; -use Magento\InventoryInStorePickup\Model\PickupLocation\Mapper; -use Magento\InventoryInStorePickupApi\Api\Data\PickupLocationExtension; +use Magento\InventoryInStorePickupApi\Model\Mapper\CreateFromSourceInterface; +use Magento\InventoryInStorePickupApi\Model\Mapper; use Magento\InventoryInStorePickupApi\Api\Data\PickupLocationExtensionInterface; use Magento\TestFramework\Helper\Bootstrap; @@ -49,7 +49,7 @@ public function testWrongMappingForSource() $source = $this->sourceRepository->get($this->sourceCode); $map = $this->getMap(); $map['source_fail_field'] = 'fail_field'; - /** @var \Magento\InventoryInStorePickup\Model\PickupLocation\Mapper $mapper */ + /** @var Mapper $mapper */ $mapper = $this->objectManager->create(Mapper::class, ['map' => $map]); $mapper->map($source); } @@ -64,7 +64,7 @@ public function testWrongMappingForPickupLocationExtensionAttributes() $source = $this->sourceRepository->get($this->sourceCode); $map = $this->getMap(); $map['name'] = 'extension_attributes.fail_field'; - /** @var \Magento\InventoryInStorePickup\Model\PickupLocation\Mapper $mapper */ + /** @var Mapper $mapper */ $mapper = $this->objectManager->create(Mapper::class, ['map' => $map]); $mapper->map($source); } @@ -79,7 +79,7 @@ public function testWrongMappingForPickupLocation() $source = $this->sourceRepository->get($this->sourceCode); $map = $this->getMap(); $map['name'] = 'fail_field'; - /** @var \Magento\InventoryInStorePickup\Model\PickupLocation\Mapper $mapper */ + /** @var Mapper $mapper */ $mapper = $this->objectManager->create(Mapper::class, ['map' => $map]); $mapper->map($source); } @@ -90,7 +90,7 @@ public function testWrongMappingForPickupLocation() public function testMapPickupLocation() { $source = $this->sourceRepository->get($this->sourceCode); - /** @var \Magento\InventoryInStorePickup\Model\PickupLocation\Mapper $mapper */ + /** @var Mapper $mapper */ $mapper = $this->objectManager->create(Mapper::class, ['map' => $this->getMap()]); $pickupLocation = $mapper->map($source); @@ -145,7 +145,7 @@ public function testMapPickupLocationWithExtensionAttributes() ->willReturn($pickupLocationExtension); $createFromSource = $this->objectManager->create( - Mapper\CreateFromSource::class, + CreateFromSourceInterface::class, ['extensionAttributesFactory' => $extensionAttributesFactory] ); @@ -153,7 +153,7 @@ public function testMapPickupLocationWithExtensionAttributes() $map['extension_attributes.open_hours'] = 'open_hours'; $map['extension_attributes.some_attribute'] = 'extension_attributes.pickup_location_attribute'; - /** @var \Magento\InventoryInStorePickup\Model\PickupLocation\Mapper $mapper */ + /** @var Mapper $mapper */ $mapper = $this->objectManager->create( Mapper::class, ['map' => $map, 'createFromSource' => $createFromSource] diff --git a/InventoryInStorePickup/etc/di.xml b/InventoryInStorePickup/etc/di.xml index ea02f9c7b988..2c49b2118556 100644 --- a/InventoryInStorePickup/etc/di.xml +++ b/InventoryInStorePickup/etc/di.xml @@ -20,7 +20,7 @@ <preference for="Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface" type="Magento\InventoryInStorePickup\Model\PickupLocation" /> <preference for="Magento\InventoryInStorePickupApi\Api\GetNearbyPickupLocationsInterface" type="Magento\InventoryInStorePickup\Model\GetNearbyPickupLocations"/> <preference for="Magento\InventoryInStorePickupApi\Api\Data\AddressInterface" type="Magento\InventoryInStorePickup\Model\Address" /> - <preference for="Magento\InventoryInStorePickupApi\Api\GetPickupLocationsInterface" type="Magento\InventoryInStorePickup\Model\GetPickupLocations" /> + <preference for="Magento\InventoryInStorePickupApi\Api\GetPickupLocationsAssignedToStockOrderedByPriorityInterface" type="Magento\InventoryInStorePickup\Model\GetPickupLocationsAssignedToStockOrderedByPriority" /> <preference for="Magento\InventoryInStorePickupApi\Api\NotifyOrderIsReadyForPickupInterface" type="Magento\InventoryInStorePickup\Model\NotifyOrderIsReadyForPickup"/> @@ -31,4 +31,5 @@ <argument name="identityContainer" xsi:type="object">\Magento\InventoryInStorePickup\Model\Order\Email\Container\ReadyForPickupIdentity</argument> </arguments> </type> + <preference for="Magento\InventoryInStorePickupApi\Model\Mapper\CreateFromSourceInterface" type="Magento\InventoryInStorePickup\Model\PickupLocation\Mapper\CreateFromSource" /> </config> diff --git a/InventoryInStorePickup/etc/frontend/di.xml b/InventoryInStorePickup/etc/frontend/di.xml deleted file mode 100644 index e0e66c4a259a..000000000000 --- a/InventoryInStorePickup/etc/frontend/di.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?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\InventoryInStorePickup\Model\PickupLocation\Mapper"> - <arguments> - <argument name="map" xsi:type="array"> - <item name="source_code" xsi:type="string">source_code</item> - <item name="email" xsi:type="string">email</item> - <item name="fax" xsi:type="string">fax</item> - <item name="contact_name" xsi:type="string">contact_name</item> - <item name="description" xsi:type="string">description</item> - <item name="latitude" xsi:type="string">latitude</item> - <item name="longitude" xsi:type="string">longitude</item> - <item name="country_id" xsi:type="string">country_id</item> - <item name="region_id" xsi:type="string">region_id</item> - <item name="region" xsi:type="string">region</item> - <item name="city" xsi:type="string">city</item> - <item name="street" xsi:type="string">street</item> - <item name="postcode" xsi:type="string">postcode</item> - <item name="phone" xsi:type="string">phone</item> - </argument> - </arguments> - </type> -</config> diff --git a/InventoryInStorePickup/etc/webapi_rest/di.xml b/InventoryInStorePickup/etc/webapi_rest/di.xml deleted file mode 100644 index e0e66c4a259a..000000000000 --- a/InventoryInStorePickup/etc/webapi_rest/di.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?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\InventoryInStorePickup\Model\PickupLocation\Mapper"> - <arguments> - <argument name="map" xsi:type="array"> - <item name="source_code" xsi:type="string">source_code</item> - <item name="email" xsi:type="string">email</item> - <item name="fax" xsi:type="string">fax</item> - <item name="contact_name" xsi:type="string">contact_name</item> - <item name="description" xsi:type="string">description</item> - <item name="latitude" xsi:type="string">latitude</item> - <item name="longitude" xsi:type="string">longitude</item> - <item name="country_id" xsi:type="string">country_id</item> - <item name="region_id" xsi:type="string">region_id</item> - <item name="region" xsi:type="string">region</item> - <item name="city" xsi:type="string">city</item> - <item name="street" xsi:type="string">street</item> - <item name="postcode" xsi:type="string">postcode</item> - <item name="phone" xsi:type="string">phone</item> - </argument> - </arguments> - </type> -</config> diff --git a/InventoryInStorePickupApi/Api/GetNearbyPickupLocationsInterface.php b/InventoryInStorePickupApi/Api/GetNearbyPickupLocationsInterface.php index 8b173575486d..aef5a4807f99 100644 --- a/InventoryInStorePickupApi/Api/GetNearbyPickupLocationsInterface.php +++ b/InventoryInStorePickupApi/Api/GetNearbyPickupLocationsInterface.php @@ -10,7 +10,7 @@ use Magento\InventoryInStorePickupApi\Api\Data\AddressInterface; /** - * Get nearby sources of a given zip code, based on the given radius in KM. + * Find nearest Pickup Locations by requested address, radius, and affiliation to stock. * * @api */ diff --git a/InventoryInStorePickupApi/Api/GetPickupLocationsInterface.php b/InventoryInStorePickupApi/Api/GetPickupLocationsAssignedToStockOrderedByPriorityInterface.php similarity index 87% rename from InventoryInStorePickupApi/Api/GetPickupLocationsInterface.php rename to InventoryInStorePickupApi/Api/GetPickupLocationsAssignedToStockOrderedByPriorityInterface.php index 6dfb94937f4f..b348d6382fb5 100644 --- a/InventoryInStorePickupApi/Api/GetPickupLocationsInterface.php +++ b/InventoryInStorePickupApi/Api/GetPickupLocationsAssignedToStockOrderedByPriorityInterface.php @@ -12,7 +12,7 @@ * * @api */ -interface GetPickupLocationsInterface +interface GetPickupLocationsAssignedToStockOrderedByPriorityInterface { /** * @param int $stockId diff --git a/InventoryInStorePickup/Model/PickupLocation/Mapper.php b/InventoryInStorePickupApi/Model/Mapper.php similarity index 63% rename from InventoryInStorePickup/Model/PickupLocation/Mapper.php rename to InventoryInStorePickupApi/Model/Mapper.php index f91c75c78d32..3afcd6b02691 100644 --- a/InventoryInStorePickup/Model/PickupLocation/Mapper.php +++ b/InventoryInStorePickupApi/Model/Mapper.php @@ -5,15 +5,17 @@ */ declare(strict_types=1); -namespace Magento\InventoryInStorePickup\Model\PickupLocation; +namespace Magento\InventoryInStorePickupApi\Model; use Magento\InventoryApi\Api\Data\SourceInterface; -use Magento\InventoryInStorePickup\Model\PickupLocation\Mapper\CreateFromSource; use Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface; +use Magento\InventoryInStorePickupApi\Model\Mapper\CreateFromSourceInterface; /** * Create projection of sources on In-Store Pickup context. * Data transfer from source to projection will be done according to provided fields mapping. + * + * @api */ class Mapper { @@ -25,16 +27,16 @@ class Mapper private $map; /** - * @var \Magento\InventoryInStorePickup\Model\PickupLocation\Mapper\CreateFromSource + * @var CreateFromSourceInterface */ private $createFromSource; /** - * @param \Magento\InventoryInStorePickup\Model\PickupLocation\Mapper\CreateFromSource $createFromSource + * @param CreateFromSourceInterface $createFromSource * @param array $map */ public function __construct( - CreateFromSource $createFromSource, + CreateFromSourceInterface $createFromSource, array $map = [] ) { $this->map = $map; @@ -42,9 +44,9 @@ public function __construct( } /** - * @param \Magento\InventoryApi\Api\Data\SourceInterface $source + * @param SourceInterface $source * - * @return \Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface + * @return PickupLocationInterface */ public function map(SourceInterface $source): PickupLocationInterface { diff --git a/InventoryInStorePickupApi/Model/Mapper/CreateFromSourceInterface.php b/InventoryInStorePickupApi/Model/Mapper/CreateFromSourceInterface.php new file mode 100644 index 000000000000..2cb798c86bcb --- /dev/null +++ b/InventoryInStorePickupApi/Model/Mapper/CreateFromSourceInterface.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryInStorePickupApi\Model\Mapper; + +use Magento\InventoryApi\Api\Data\SourceInterface; +use Magento\InventoryInStorePickupApi\Api\Data\PickupLocationInterface; + +/** + * Create Pickup Location based on Source (Service Provider Interface - SPI). + * Transport data from Source to Pickup Location according to provided mapping. + * + * @api + */ +interface CreateFromSourceInterface +{ + /** + * @param SourceInterface $source + * @param array $map May contains references to fields in extension attributes. + * Please use format 'extension_attributes.field_name' to do so. E.g. + * [ + * "extension_attributes.source_field" => "pickup_location_field" + * "extension_attributes.source_field" => "extension_attributes.pickup_location_extension_field", + * ] + * @return PickupLocationInterface + */ + public function execute(SourceInterface $source, array $map): PickupLocationInterface; +} diff --git a/InventoryInStorePickupApi/composer.json b/InventoryInStorePickupApi/composer.json index 604313b67885..5caac657d0d3 100644 --- a/InventoryInStorePickupApi/composer.json +++ b/InventoryInStorePickupApi/composer.json @@ -3,7 +3,8 @@ "description": "N/A", "require": { "php": "~7.1.3||~7.2.0", - "magento/framework": "*" + "magento/framework": "*", + "magento/module-inventory-api": "*" }, "type": "magento2-module", "license": [ diff --git a/InventoryInStorePickup/etc/webapi_soap/di.xml b/InventoryInStorePickupApi/etc/di.xml similarity index 94% rename from InventoryInStorePickup/etc/webapi_soap/di.xml rename to InventoryInStorePickupApi/etc/di.xml index e0e66c4a259a..8585922de9ba 100644 --- a/InventoryInStorePickup/etc/webapi_soap/di.xml +++ b/InventoryInStorePickupApi/etc/di.xml @@ -6,7 +6,7 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> - <type name="Magento\InventoryInStorePickup\Model\PickupLocation\Mapper"> + <type name="Magento\InventoryInStorePickupApi\Model\Mapper"> <arguments> <argument name="map" xsi:type="array"> <item name="source_code" xsi:type="string">source_code</item> diff --git a/InventoryInStorePickupApi/etc/module.xml b/InventoryInStorePickupApi/etc/module.xml index fcd32a6f93d8..832b7ee3923a 100644 --- a/InventoryInStorePickupApi/etc/module.xml +++ b/InventoryInStorePickupApi/etc/module.xml @@ -6,5 +6,9 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_InventoryInStorePickupApi" setup_version="1.0.0" /> + <module name="Magento_InventoryInStorePickupApi" setup_version="1.0.0"> + <sequence> + <module name="Magento_InventoryApi"/> + </sequence> + </module> </config> diff --git a/InventoryInStorePickupApi/etc/webapi.xml b/InventoryInStorePickupApi/etc/webapi.xml index 57c75f773a40..46bd410d9ba6 100644 --- a/InventoryInStorePickupApi/etc/webapi.xml +++ b/InventoryInStorePickupApi/etc/webapi.xml @@ -7,14 +7,14 @@ --> <routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Webapi:etc/webapi.xsd"> - <route url="/V1/inventory/in-store-pickup/get-nearby-pickup-locations" method="GET"> + <route url="/V1/inventory/in-store-pickup/get-nearby-pickup-locations" method="POST"> <service class="Magento\InventoryInStorePickupApi\Api\GetNearbyPickupLocationsInterface" method="execute"/> <resources> <resource ref="Magento_InventoryApi::inStorePickup"/> </resources> </route> - <route url="/V1/inventory/in-store-pickup/get-pickup-locations" method="GET"> - <service class="Magento\InventoryInStorePickupApi\Api\GetPickupLocationsInterface" method="execute"/> + <route url="/V1/inventory/in-store-pickup/pickup-locations-assigned-to-stock-ordered-by-priority/:stockId" method="GET"> + <service class="Magento\InventoryInStorePickupApi\Api\GetPickupLocationsAssignedToStockOrderedByPriorityInterface" method="execute"/> <resources> <resource ref="Magento_InventoryApi::inStorePickup"/> </resources>