From 7e027f9ac4070f4ac3d91b4fa4d1ada38b9f2911 Mon Sep 17 00:00:00 2001 From: "Vasiliev.A" Date: Tue, 1 Dec 2020 01:29:02 +0200 Subject: [PATCH 1/5] Init implementation for default for country SSA --- .../Algorithms/DefaultForCountryAlgorithm.php | 169 ++++++++++++++++++ .../Model/Configuration.php | 70 ++++++++ ...tCountriesSelectionExtensionAttributes.php | 57 ++++++ .../LoadCountriesSelectionGetListPlugin.php | 54 ++++++ .../LoadCountriesSelectionOnGetPlugin.php | 50 ++++++ .../SaveCountriesSelectionPlugin.php | 50 ++++++ .../README.md | 3 + .../Test/Api/SourceSelectionServiceTest.php | 169 ++++++++++++++++++ .../composer.json | 26 +++ .../etc/config.xml | 18 ++ .../etc/db_schema.xml | 13 ++ .../etc/db_schema_whitelist.json | 7 + .../etc/di.xml | 37 ++++ .../etc/extension_attributes.xml | 12 ++ .../etc/module.xml | 16 ++ .../i18n/en_US.csv | 1 + .../registration.php | 12 ++ .../Config/Source/AdditionalAlgorithms.php | 59 ++++++ .../README.md | 3 + .../composer.json | 22 +++ .../etc/adminhtml/system.xml | 30 ++++ .../etc/module.xml | 12 ++ .../i18n/en_US.csv | 3 + .../registration.php | 12 ++ .../ui_component/inventory_source_form.xml | 37 ++++ 25 files changed, 942 insertions(+) create mode 100644 InventoryDefaultForCountrySourceSelection/Model/Algorithms/DefaultForCountryAlgorithm.php create mode 100644 InventoryDefaultForCountrySourceSelection/Model/Configuration.php create mode 100644 InventoryDefaultForCountrySourceSelection/Model/Source/InitCountriesSelectionExtensionAttributes.php create mode 100644 InventoryDefaultForCountrySourceSelection/Plugin/InventoryApi/SourceRepository/LoadCountriesSelectionGetListPlugin.php create mode 100644 InventoryDefaultForCountrySourceSelection/Plugin/InventoryApi/SourceRepository/LoadCountriesSelectionOnGetPlugin.php create mode 100644 InventoryDefaultForCountrySourceSelection/Plugin/InventoryApi/SourceRepository/SaveCountriesSelectionPlugin.php create mode 100644 InventoryDefaultForCountrySourceSelection/README.md create mode 100644 InventoryDefaultForCountrySourceSelection/Test/Api/SourceSelectionServiceTest.php create mode 100644 InventoryDefaultForCountrySourceSelection/composer.json create mode 100644 InventoryDefaultForCountrySourceSelection/etc/config.xml create mode 100644 InventoryDefaultForCountrySourceSelection/etc/db_schema.xml create mode 100644 InventoryDefaultForCountrySourceSelection/etc/db_schema_whitelist.json create mode 100644 InventoryDefaultForCountrySourceSelection/etc/di.xml create mode 100644 InventoryDefaultForCountrySourceSelection/etc/extension_attributes.xml create mode 100644 InventoryDefaultForCountrySourceSelection/etc/module.xml create mode 100644 InventoryDefaultForCountrySourceSelection/i18n/en_US.csv create mode 100644 InventoryDefaultForCountrySourceSelection/registration.php create mode 100644 InventoryDefaultForCountrySourceSelectionAdminUi/Model/Config/Source/AdditionalAlgorithms.php create mode 100644 InventoryDefaultForCountrySourceSelectionAdminUi/README.md create mode 100644 InventoryDefaultForCountrySourceSelectionAdminUi/composer.json create mode 100644 InventoryDefaultForCountrySourceSelectionAdminUi/etc/adminhtml/system.xml create mode 100644 InventoryDefaultForCountrySourceSelectionAdminUi/etc/module.xml create mode 100644 InventoryDefaultForCountrySourceSelectionAdminUi/i18n/en_US.csv create mode 100644 InventoryDefaultForCountrySourceSelectionAdminUi/registration.php create mode 100644 InventoryDefaultForCountrySourceSelectionAdminUi/view/adminhtml/ui_component/inventory_source_form.xml diff --git a/InventoryDefaultForCountrySourceSelection/Model/Algorithms/DefaultForCountryAlgorithm.php b/InventoryDefaultForCountrySourceSelection/Model/Algorithms/DefaultForCountryAlgorithm.php new file mode 100644 index 000000000000..7eb0f653c391 --- /dev/null +++ b/InventoryDefaultForCountrySourceSelection/Model/Algorithms/DefaultForCountryAlgorithm.php @@ -0,0 +1,169 @@ +getSourcesAssignedToStockOrderedByPriority = $getSourcesAssignedToStockOrderedByPriority; + $this->getDefaultSortedSourcesResult = $getDefaultSortedSourcesResult; + $this->configuration = $configuration; + $this->sourceSelectionService = $sourceSelectionService; + } + + /** + * @inheritdoc + * @throws LocalizedException + */ + public function execute(InventoryRequestInterface $inventoryRequest): SourceSelectionResultInterface + { + $destinationAddress = $inventoryRequest->getExtensionAttributes()->getDestinationAddress(); + if ($destinationAddress === null) { + throw new LocalizedException(__('No destination address was provided in the request')); + } + + $stockId = $inventoryRequest->getStockId(); + $sortedSources = $this->getEnabledSourcesOrderedByDefaultForCountriesByStockId( + $stockId, + $destinationAddress, + $inventoryRequest + ); + + return $this->getDefaultSortedSourcesResult->execute($inventoryRequest, $sortedSources); + } + + /** + * Get enabled sources ordered by countries and fallback algorithm by $stockId + * + * @param int $stockId + * @param AddressInterface $address + * @param InventoryRequestInterface $inventoryRequest + * @return array + * @throws InputException + * @throws LocalizedException + */ + private function getEnabledSourcesOrderedByDefaultForCountriesByStockId( + int $stockId, + AddressInterface $address, + InventoryRequestInterface $inventoryRequest + ): array { + $priorityBySourceCode = $sortSources = $sourcesFromAdditional = []; + + $additionalAlgorithmCode = $this->configuration->getAdditionalAlgorithmCode(); + if (!empty($additionalAlgorithmCode)) { + $additionalAlgorithmResult = $this->sourceSelectionService->execute( + $inventoryRequest, + $additionalAlgorithmCode + ); + $sourceSelectionItemsFromAdditional = $additionalAlgorithmResult->getSourceSelectionItems(); + $i = 1; + foreach ($sourceSelectionItemsFromAdditional as $sourceSelectionItem) { + $sourcesFromAdditional[$sourceSelectionItem->getSourceCode()] = $i++; + } + } + + // Keep priority order as computational base + $sources = $this->getSourcesAssignedToStockOrderedByPriority->execute($stockId); + $sources = array_filter($sources, function (SourceInterface $source) { + return $source->isEnabled(); + }); + + $isExcludeUnmatchedEnabled = $this->configuration->isExcludeUnmatchedEnabled(); + $defaultSortOrder = count($sourcesFromAdditional) + count($sources); + foreach ($sources as $source) { + // Keep default sort order big, so source from default for countries can be pushed to start of array + $sortOrder = $defaultSortOrder; + $defaultForCountries = $source->getExtensionAttributes()->getDefaultForCountries(); + + $countryMatchToSourceFlag = isset($defaultForCountries) + && in_array($address->getCountry(), $defaultForCountries); + if ($countryMatchToSourceFlag) { + // push default for country source to start of array + $sortOrder = 0; + } + + if ($isExcludeUnmatchedEnabled && !$countryMatchToSourceFlag) { + continue; + } + + if (isset($sourcesFromAdditional[$source->getSourceCode()])) { + // increase sort order based on sort order from additional algorithm + $sortOrder += $sourcesFromAdditional[$source->getSourceCode()]; + } + + $priorityBySourceCode[$source->getSourceCode()] = $sortOrder; + $sortSources[] = $source; + } + + // Sort sources by priority + uasort( + $sortSources, + function (SourceInterface $a, SourceInterface $b) use ($priorityBySourceCode) { + $priorityA = $priorityBySourceCode[$a->getSourceCode()]; + $priorityB = $priorityBySourceCode[$b->getSourceCode()]; + + return ($priorityA < $priorityB) ? -1 : 1; + } + ); + + return $sortSources; + } +} diff --git a/InventoryDefaultForCountrySourceSelection/Model/Configuration.php b/InventoryDefaultForCountrySourceSelection/Model/Configuration.php new file mode 100644 index 000000000000..47fbc1521424 --- /dev/null +++ b/InventoryDefaultForCountrySourceSelection/Model/Configuration.php @@ -0,0 +1,70 @@ +scopeConfig = $scopeConfig; + } + + /** + * Get additional algorithm code + * + * @param null|string|bool|int|Store $store + * @return string|null + */ + public function getAdditionalAlgorithmCode($store = null): ?string + { + return $this->scopeConfig->getValue( + self::XML_PATH_ADDITIONAL_ALGORITHM, + ScopeInterface::SCOPE_STORE, + $store + ); + } + + /** + * Get exclude_unmatched config flag + * + * @param null|string|bool|int|Store $store + * @return bool + */ + public function isExcludeUnmatchedEnabled($store = null): bool + { + return $this->scopeConfig->isSetFlag( + self::XML_PATH_EXCLUDE_UNMATCHED, + ScopeInterface::SCOPE_STORE, + $store + ); + } +} diff --git a/InventoryDefaultForCountrySourceSelection/Model/Source/InitCountriesSelectionExtensionAttributes.php b/InventoryDefaultForCountrySourceSelection/Model/Source/InitCountriesSelectionExtensionAttributes.php new file mode 100644 index 000000000000..f29f4cf1895a --- /dev/null +++ b/InventoryDefaultForCountrySourceSelection/Model/Source/InitCountriesSelectionExtensionAttributes.php @@ -0,0 +1,57 @@ +extensionAttributesFactory = $extensionAttributesFactory; + } + + /** + * Set store-pickup related source extension attributes. + * + * @param SourceInterface $source + */ + public function execute(SourceInterface $source): void + { + if (!$source instanceof DataObject) { + return; + } + $defaultForCountries = $source->getData(self::DEFAULT_FOR_COUNTRIES_KEY); + $defaultForCountries = (empty($defaultForCountries)) ? [] : explode(',', $defaultForCountries); + $extensionAttributes = $source->getExtensionAttributes(); + + if ($extensionAttributes === null) { + $extensionAttributes = $this->extensionAttributesFactory->create(SourceInterface::class); + /** @noinspection PhpParamsInspection */ + $source->setExtensionAttributes($extensionAttributes); + } + if (!empty($defaultForCountries)) { + $extensionAttributes->setDefaultForCountries($defaultForCountries); + } + } +} diff --git a/InventoryDefaultForCountrySourceSelection/Plugin/InventoryApi/SourceRepository/LoadCountriesSelectionGetListPlugin.php b/InventoryDefaultForCountrySourceSelection/Plugin/InventoryApi/SourceRepository/LoadCountriesSelectionGetListPlugin.php new file mode 100644 index 000000000000..316949d91985 --- /dev/null +++ b/InventoryDefaultForCountrySourceSelection/Plugin/InventoryApi/SourceRepository/LoadCountriesSelectionGetListPlugin.php @@ -0,0 +1,54 @@ +setExtensionAttributes = $setExtensionAttributes; + } + + /** + * Enrich the given Source Objects with the countries selection attributes + * + * @param SourceRepositoryInterface $subject + * @param SourceSearchResultsInterface $sourceSearchResults + * + * @return SourceSearchResultsInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterGetList( + SourceRepositoryInterface $subject, + SourceSearchResultsInterface $sourceSearchResults + ): SourceSearchResultsInterface { + $items = $sourceSearchResults->getItems(); + array_walk( + $items, + [$this->setExtensionAttributes, 'execute'] + ); + + return $sourceSearchResults; + } +} diff --git a/InventoryDefaultForCountrySourceSelection/Plugin/InventoryApi/SourceRepository/LoadCountriesSelectionOnGetPlugin.php b/InventoryDefaultForCountrySourceSelection/Plugin/InventoryApi/SourceRepository/LoadCountriesSelectionOnGetPlugin.php new file mode 100644 index 000000000000..41a72162d14a --- /dev/null +++ b/InventoryDefaultForCountrySourceSelection/Plugin/InventoryApi/SourceRepository/LoadCountriesSelectionOnGetPlugin.php @@ -0,0 +1,50 @@ +setExtensionAttributes = $setExtensionAttributes; + } + + /** + * Enrich the given Source Objects with the In-Store pickup attribute + * + * @param SourceRepositoryInterface $subject + * @param SourceInterface $source + * + * @return SourceInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterGet( + SourceRepositoryInterface $subject, + SourceInterface $source + ): SourceInterface { + $this->setExtensionAttributes->execute($source); + + return $source; + } +} diff --git a/InventoryDefaultForCountrySourceSelection/Plugin/InventoryApi/SourceRepository/SaveCountriesSelectionPlugin.php b/InventoryDefaultForCountrySourceSelection/Plugin/InventoryApi/SourceRepository/SaveCountriesSelectionPlugin.php new file mode 100644 index 000000000000..feeb27f18dcf --- /dev/null +++ b/InventoryDefaultForCountrySourceSelection/Plugin/InventoryApi/SourceRepository/SaveCountriesSelectionPlugin.php @@ -0,0 +1,50 @@ +getExtensionAttributes(); + + if ($extensionAttributes !== null) { + $defaultForCountries = $extensionAttributes->getDefaultForCountries(); + if (!empty($defaultForCountries)) { + $source->setData( + InitCountriesSelectionExtensionAttributes::DEFAULT_FOR_COUNTRIES_KEY, + implode(',', $defaultForCountries) + ); + } + } + + return [$source]; + } +} diff --git a/InventoryDefaultForCountrySourceSelection/README.md b/InventoryDefaultForCountrySourceSelection/README.md new file mode 100644 index 000000000000..b5e224537282 --- /dev/null +++ b/InventoryDefaultForCountrySourceSelection/README.md @@ -0,0 +1,3 @@ +# InventoryDefaultForCountrySourceSelection module + +The `InventoryDefaultForCountrySourceSelection` module implements logic for default for countries source selection. Example, source based in Japan may be preferred by merchant for shipping even if another source based in China is a closer to customer. diff --git a/InventoryDefaultForCountrySourceSelection/Test/Api/SourceSelectionServiceTest.php b/InventoryDefaultForCountrySourceSelection/Test/Api/SourceSelectionServiceTest.php new file mode 100644 index 000000000000..720873e3b066 --- /dev/null +++ b/InventoryDefaultForCountrySourceSelection/Test/Api/SourceSelectionServiceTest.php @@ -0,0 +1,169 @@ + ['eu-2'], + 'US' => ['eu-1', 'us-1'], + ]; + foreach ($countriesToTest as $countryCode => $sources) { + foreach ($sources as $sourceCode) { + $source = $this->sourceRepository->get($sourceCode); + $source->setData( + InitCountriesSelectionExtensionAttributes::DEFAULT_FOR_COUNTRIES_KEY, + $countryCode + ); + $this->sourceRepository->save($source); + } + } + + $inventoryRequest = [ + 'stockId' => 10, + 'items' => [ + [ + 'sku' => 'SKU-1', + 'qty' => 1, + ], + ], + 'extension_attributes' => [ + 'destination_address' => [ + 'country' => 'DE', + 'postcode' => '45000', + 'street' => 'test street', + 'region' => 'Region', + 'city' => 'City', + ], + ], + ]; + + $expectedResultData = + [ + 'DE' => [ + 'source_selection_items' => [ + [ + 'source_code' => 'eu-2', + 'sku' => 'SKU-1', + 'qty_to_deduct' => 1, + 'qty_available' => 3, + ] + ], + 'shippable' => 1, + ], + 'US' => [ + 'source_selection_items' => [ + [ + 'source_code' => 'eu-1', + 'sku' => 'SKU-1', + 'qty_to_deduct' => 1, + 'qty_available' => 5.5, + ], + [ + 'source_code' => 'eu-2', + 'sku' => 'SKU-1', + 'qty_to_deduct' => 1, + 'qty_available' => 3, + ], + ], + 'shippable' => 1, + ], + ]; + + $algorithmCode = DefaultForCountryAlgorithm::CODE; + $requestData = [ + 'inventoryRequest' => $inventoryRequest, + 'algorithmCode' => $algorithmCode, + ]; + + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH, + 'httpMethod' => Request::HTTP_METHOD_POST, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'operation' => self::SERVICE_NAME . 'Execute', + ], + ]; + + foreach ($countriesToTest as $countryCode => $sources) { + $requestData['inventoryRequest']['extension_attributes']['destination_address']['country'] = + $countryCode; + $sourceSelectionAlgorithmResult = (TESTS_WEB_API_ADAPTER === self::ADAPTER_REST) + ? $this->_webApiCall($serviceInfo, $requestData) + : $this->_webApiCall($serviceInfo, $requestData); + + $this->assertIsArray($sourceSelectionAlgorithmResult); + $this->assertNotEmpty($sourceSelectionAlgorithmResult); + AssertArrayContains::assert($expectedResultData, $sourceSelectionAlgorithmResult); + } + } + + /** + * Setup test instance + */ + protected function setUp(): void + { + parent::setUp(); + $this->defaultAlgorithmCode = Bootstrap::getObjectManager()->get( + GetDefaultSourceSelectionAlgorithmCodeInterface::class + ); + $this->sourceRepository = Bootstrap::getObjectManager()->get( + SourceRepositoryInterface::class + ); + $this->sourceFactory = Bootstrap::getObjectManager()->get( + SourceInterfaceFactory::class + ); + } +} diff --git a/InventoryDefaultForCountrySourceSelection/composer.json b/InventoryDefaultForCountrySourceSelection/composer.json new file mode 100644 index 000000000000..15c56456762a --- /dev/null +++ b/InventoryDefaultForCountrySourceSelection/composer.json @@ -0,0 +1,26 @@ +{ + "name": "magento/module-inventory-default-for-country-source-selection", + "description": "N/A", + "require": { + "php": "~7.3.0||~7.4.0", + "magento/framework": "*", + "magento/module-inventory-distance-based-source-selection-api": "1.1.*", + "magento/module-inventory-source-selection-api": "1.3.*", + "magento/module-inventory-api": "1.1.*", + "magento/module-config": "*" + }, + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\InventoryDefaultForCountrySourceSelection\\": "" + } + }, + "version": "1.1.0" +} diff --git a/InventoryDefaultForCountrySourceSelection/etc/config.xml b/InventoryDefaultForCountrySourceSelection/etc/config.xml new file mode 100644 index 000000000000..6f9126aa504e --- /dev/null +++ b/InventoryDefaultForCountrySourceSelection/etc/config.xml @@ -0,0 +1,18 @@ + + + + + + + priority + 1 + + + + diff --git a/InventoryDefaultForCountrySourceSelection/etc/db_schema.xml b/InventoryDefaultForCountrySourceSelection/etc/db_schema.xml new file mode 100644 index 000000000000..249ab3cc690f --- /dev/null +++ b/InventoryDefaultForCountrySourceSelection/etc/db_schema.xml @@ -0,0 +1,13 @@ + + + + + +
+
diff --git a/InventoryDefaultForCountrySourceSelection/etc/db_schema_whitelist.json b/InventoryDefaultForCountrySourceSelection/etc/db_schema_whitelist.json new file mode 100644 index 000000000000..72461d8fd75a --- /dev/null +++ b/InventoryDefaultForCountrySourceSelection/etc/db_schema_whitelist.json @@ -0,0 +1,7 @@ +{ + "inventory_source": { + "column": { + "default_for_countries": true + } + } +} diff --git a/InventoryDefaultForCountrySourceSelection/etc/di.xml b/InventoryDefaultForCountrySourceSelection/etc/di.xml new file mode 100644 index 000000000000..872f1b1651f7 --- /dev/null +++ b/InventoryDefaultForCountrySourceSelection/etc/di.xml @@ -0,0 +1,37 @@ + + + + + + + + Magento\InventoryDefaultForCountrySourceSelection\Model\Algorithms\DefaultForCountryAlgorithm + + + + + + + + + Magento\InventoryDefaultForCountrySourceSelection\Model\Algorithms\DefaultForCountryAlgorithm::CODE + Default For Country Priority + Algorithm which provides Source Selections based on shipping address country from the source + + + + + + + + + + + diff --git a/InventoryDefaultForCountrySourceSelection/etc/extension_attributes.xml b/InventoryDefaultForCountrySourceSelection/etc/extension_attributes.xml new file mode 100644 index 000000000000..957f9dfc6106 --- /dev/null +++ b/InventoryDefaultForCountrySourceSelection/etc/extension_attributes.xml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/InventoryDefaultForCountrySourceSelection/etc/module.xml b/InventoryDefaultForCountrySourceSelection/etc/module.xml new file mode 100644 index 000000000000..cb575aa63f5d --- /dev/null +++ b/InventoryDefaultForCountrySourceSelection/etc/module.xml @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/InventoryDefaultForCountrySourceSelection/i18n/en_US.csv b/InventoryDefaultForCountrySourceSelection/i18n/en_US.csv new file mode 100644 index 000000000000..0f1e3acb6bbf --- /dev/null +++ b/InventoryDefaultForCountrySourceSelection/i18n/en_US.csv @@ -0,0 +1 @@ +"No destination address was provided in the request","No destination address was provided in the request" diff --git a/InventoryDefaultForCountrySourceSelection/registration.php b/InventoryDefaultForCountrySourceSelection/registration.php new file mode 100644 index 000000000000..c2a994694ed3 --- /dev/null +++ b/InventoryDefaultForCountrySourceSelection/registration.php @@ -0,0 +1,12 @@ +getSourceSelectionAlgorithmList = $getSourceSelectionAlgorithmList; + } + + /** + * @inheritDoc + */ + public function toOptionArray() + { + $options = [ + [ + 'value' => '', + 'label' => '', + ], + ]; + $algorithms = $this->getSourceSelectionAlgorithmList->execute(); + foreach ($algorithms as $algorithm) { + if ($algorithm->getCode() === DefaultForCountryAlgorithm::CODE) { + continue; + } + $options[] = [ + 'value' => $algorithm->getCode(), + 'label' => $algorithm->getTitle(), + ]; + } + return $options; + } +} diff --git a/InventoryDefaultForCountrySourceSelectionAdminUi/README.md b/InventoryDefaultForCountrySourceSelectionAdminUi/README.md new file mode 100644 index 000000000000..6131f9db8129 --- /dev/null +++ b/InventoryDefaultForCountrySourceSelectionAdminUi/README.md @@ -0,0 +1,3 @@ +# InventoryDefaultForCountrySourceSelectionAdminUi module + +The `InventoryDefaultForCountrySourceSelectionAdminUi` module extends Magento's admin UI with source selection default for countries source selection functionality. diff --git a/InventoryDefaultForCountrySourceSelectionAdminUi/composer.json b/InventoryDefaultForCountrySourceSelectionAdminUi/composer.json new file mode 100644 index 000000000000..2d1b18591030 --- /dev/null +++ b/InventoryDefaultForCountrySourceSelectionAdminUi/composer.json @@ -0,0 +1,22 @@ +{ + "name": "magento/module-inventory-default-for-country-source-selection-admin-ui", + "description": "N/A", + "require": { + "php": "~7.3.0||~7.4.0", + "magento/framework": "*" + }, + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\InventoryDefaultForCountrySourceSelectionAdminUi\\": "" + } + }, + "version": "1.1.0" +} diff --git a/InventoryDefaultForCountrySourceSelectionAdminUi/etc/adminhtml/system.xml b/InventoryDefaultForCountrySourceSelectionAdminUi/etc/adminhtml/system.xml new file mode 100644 index 000000000000..5145ecc93c2c --- /dev/null +++ b/InventoryDefaultForCountrySourceSelectionAdminUi/etc/adminhtml/system.xml @@ -0,0 +1,30 @@ + + + + +
+ + + + + Magento\InventoryDefaultForCountrySourceSelectionAdminUi\Model\Config\Source\AdditionalAlgorithms + Additional algorithm for cases when many countries available. + + + + Magento\Config\Model\Config\Source\Yesno + Sources which doesn't have assigned matched countries will be excluded from result list at all instead of pushing them to bottom of result list. + + +
+
+
diff --git a/InventoryDefaultForCountrySourceSelectionAdminUi/etc/module.xml b/InventoryDefaultForCountrySourceSelectionAdminUi/etc/module.xml new file mode 100644 index 000000000000..4c9a01e69660 --- /dev/null +++ b/InventoryDefaultForCountrySourceSelectionAdminUi/etc/module.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/InventoryDefaultForCountrySourceSelectionAdminUi/i18n/en_US.csv b/InventoryDefaultForCountrySourceSelectionAdminUi/i18n/en_US.csv new file mode 100644 index 000000000000..5b60c764f1f5 --- /dev/null +++ b/InventoryDefaultForCountrySourceSelectionAdminUi/i18n/en_US.csv @@ -0,0 +1,3 @@ +"Additional Algorithm","Additional Algorithm" +"Additional algorithm for cases when many countries available.","Additional algorithm for cases when many countries available." +"Default For Country SSA","Default For Country SSA" diff --git a/InventoryDefaultForCountrySourceSelectionAdminUi/registration.php b/InventoryDefaultForCountrySourceSelectionAdminUi/registration.php new file mode 100644 index 000000000000..067380ced9a8 --- /dev/null +++ b/InventoryDefaultForCountrySourceSelectionAdminUi/registration.php @@ -0,0 +1,12 @@ + + +
+
+ + true + + general + + true + + + + + text + extension_attributes.default_for_countries + + Used in "based by country" algorithm selection. + + false + + + + + + + + + + +
+
From adede55b4c45c24606ee1f573717b10fb640292d Mon Sep 17 00:00:00 2001 From: "Vasiliev.A" Date: Tue, 1 Dec 2020 01:32:30 +0200 Subject: [PATCH 2/5] set "disable" as default exclude unmatched sources config --- InventoryDefaultForCountrySourceSelection/etc/config.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InventoryDefaultForCountrySourceSelection/etc/config.xml b/InventoryDefaultForCountrySourceSelection/etc/config.xml index 6f9126aa504e..a98e71e019d1 100644 --- a/InventoryDefaultForCountrySourceSelection/etc/config.xml +++ b/InventoryDefaultForCountrySourceSelection/etc/config.xml @@ -11,7 +11,7 @@ priority - 1 + 0 From 3ffa90cbde9fe5ffd5bc131d6b07221d3c6aae76 Mon Sep 17 00:00:00 2001 From: "Vasiliev.A" Date: Mon, 25 Jan 2021 13:17:03 +0200 Subject: [PATCH 3/5] api-functional test, dependencies fixes --- .../LoadCountriesSelectionGetListPlugin.php | 2 +- .../LoadCountriesSelectionOnGetPlugin.php | 2 +- ...eTest.php => DefaultForCountrySSATest.php} | 157 +++++++++++------- .../composer.json | 8 +- .../composer.json | 4 +- .../etc/module.xml | 3 + 6 files changed, 111 insertions(+), 65 deletions(-) rename InventoryDefaultForCountrySourceSelection/Test/Api/{SourceSelectionServiceTest.php => DefaultForCountrySSATest.php} (52%) diff --git a/InventoryDefaultForCountrySourceSelection/Plugin/InventoryApi/SourceRepository/LoadCountriesSelectionGetListPlugin.php b/InventoryDefaultForCountrySourceSelection/Plugin/InventoryApi/SourceRepository/LoadCountriesSelectionGetListPlugin.php index 316949d91985..a4b12170f059 100644 --- a/InventoryDefaultForCountrySourceSelection/Plugin/InventoryApi/SourceRepository/LoadCountriesSelectionGetListPlugin.php +++ b/InventoryDefaultForCountrySourceSelection/Plugin/InventoryApi/SourceRepository/LoadCountriesSelectionGetListPlugin.php @@ -31,7 +31,7 @@ public function __construct( } /** - * Enrich the given Source Objects with the countries selection attributes + * Add extension attribute object to source items * * @param SourceRepositoryInterface $subject * @param SourceSearchResultsInterface $sourceSearchResults diff --git a/InventoryDefaultForCountrySourceSelection/Plugin/InventoryApi/SourceRepository/LoadCountriesSelectionOnGetPlugin.php b/InventoryDefaultForCountrySourceSelection/Plugin/InventoryApi/SourceRepository/LoadCountriesSelectionOnGetPlugin.php index 41a72162d14a..0151b8f3f225 100644 --- a/InventoryDefaultForCountrySourceSelection/Plugin/InventoryApi/SourceRepository/LoadCountriesSelectionOnGetPlugin.php +++ b/InventoryDefaultForCountrySourceSelection/Plugin/InventoryApi/SourceRepository/LoadCountriesSelectionOnGetPlugin.php @@ -31,7 +31,7 @@ public function __construct( } /** - * Enrich the given Source Objects with the In-Store pickup attribute + * Add extension attribute object to source * * @param SourceRepositoryInterface $subject * @param SourceInterface $source diff --git a/InventoryDefaultForCountrySourceSelection/Test/Api/SourceSelectionServiceTest.php b/InventoryDefaultForCountrySourceSelection/Test/Api/DefaultForCountrySSATest.php similarity index 52% rename from InventoryDefaultForCountrySourceSelection/Test/Api/SourceSelectionServiceTest.php rename to InventoryDefaultForCountrySourceSelection/Test/Api/DefaultForCountrySSATest.php index 720873e3b066..627994335056 100644 --- a/InventoryDefaultForCountrySourceSelection/Test/Api/SourceSelectionServiceTest.php +++ b/InventoryDefaultForCountrySourceSelection/Test/Api/DefaultForCountrySSATest.php @@ -13,14 +13,13 @@ use Magento\InventoryDefaultForCountrySourceSelection\Model\Algorithms\DefaultForCountryAlgorithm; use Magento\InventoryDefaultForCountrySourceSelection\Model\Source\InitCountriesSelectionExtensionAttributes; use Magento\InventorySourceSelectionApi\Api\GetDefaultSourceSelectionAlgorithmCodeInterface; -use Magento\TestFramework\Assert\AssertArrayContains; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\WebapiAbstract; /** * Test default_for_countries algorithm */ -class SourceSelectionServiceTest extends WebapiAbstract +class DefaultForCountrySSATest extends WebapiAbstract { /**#@+ * Service constants @@ -45,19 +44,55 @@ class SourceSelectionServiceTest extends WebapiAbstract private $sourceFactory; /** + * Test default for countries SSA with disabled exclude unmatched + * * @magentoConfigFixture cataloginventory/source_selection_default_for_country/additional_algorithm priority + * @magentoConfigFixture cataloginventory/source_selection_default_for_country/exclude_unmatched 0 * @magentoApiDataFixture ../../../../vendor/magento/module-inventory-api/Test/_files/products.php * @magentoApiDataFixture ../../../../vendor/magento/module-inventory-api/Test/_files/sources.php * @magentoApiDataFixture ../../../../vendor/magento/module-inventory-api/Test/_files/stocks.php * @magentoApiDataFixture ../../../../vendor/magento/module-inventory-api/Test/_files/stock_source_links.php - * @magentoApiDataFixture ../../../../vendor/magento/module-inventory-api/Test/_files/source_items_eu_stock_only.php + * @magentoApiDataFixture ../../../../vendor/magento/module-inventory-api/Test/_files/source_items.php */ - public function testSourceSelectionService() + public function testDefaultForCountrySSA() { - $countriesToTest = [ - 'DE' => ['eu-2'], - 'US' => ['eu-1', 'us-1'], + $countriesToTest = $this->getCountriesToTestData(); + foreach ($countriesToTest as $countryCode => $sources) { + foreach ($sources as $sourceCode) { + $source = $this->sourceRepository->get($sourceCode); + $source->setData( + InitCountriesSelectionExtensionAttributes::DEFAULT_FOR_COUNTRIES_KEY, + $countryCode + ); + $this->sourceRepository->save($source); + } + } + + $expectedListOrder = [ + 'DE' => ['eu-2', 'eu-1'], + 'FR' => ['eu-1', 'eu-2'], ]; + $wrongListOrder = [ + 'DE' => ['eu-1', 'eu-2'], + 'FR' => ['eu-2', 'eu-1'], + ]; + $this->assertSourceSelectionOrderLists($expectedListOrder, $wrongListOrder); + } + + /** + * Test default for countries SSA with enabled exclude_unmatched config + * + * @magentoConfigFixture cataloginventory/source_selection_default_for_country/additional_algorithm priority + * @magentoConfigFixture cataloginventory/source_selection_default_for_country/exclude_unmatched 1 + * @magentoApiDataFixture ../../../../vendor/magento/module-inventory-api/Test/_files/products.php + * @magentoApiDataFixture ../../../../vendor/magento/module-inventory-api/Test/_files/sources.php + * @magentoApiDataFixture ../../../../vendor/magento/module-inventory-api/Test/_files/stocks.php + * @magentoApiDataFixture ../../../../vendor/magento/module-inventory-api/Test/_files/stock_source_links.php + * @magentoApiDataFixture ../../../../vendor/magento/module-inventory-api/Test/_files/source_items.php + */ + public function testDefaultForCountrySSAWithExcludeUnmatched() + { + $countriesToTest = $this->getCountriesToTestData(); foreach ($countriesToTest as $countryCode => $sources) { foreach ($sources as $sourceCode) { $source = $this->sourceRepository->get($sourceCode); @@ -69,6 +104,44 @@ public function testSourceSelectionService() } } + $expectedListOrder = [ + 'DE' => ['eu-2'], + 'FR' => ['eu-1'], + ]; + $wrongListOrder = [ + 'DE' => ['eu-1', 'eu-2'], + 'FR' => ['eu-2', 'eu-1'], + ]; + $this->assertSourceSelectionOrderLists($expectedListOrder, $wrongListOrder); + } + + /** + * @inheridoc + */ + protected function setUp(): void + { + parent::setUp(); + $this->defaultAlgorithmCode = Bootstrap::getObjectManager()->get( + GetDefaultSourceSelectionAlgorithmCodeInterface::class + ); + $this->sourceRepository = Bootstrap::getObjectManager()->get( + SourceRepositoryInterface::class + ); + $this->sourceFactory = Bootstrap::getObjectManager()->get( + SourceInterfaceFactory::class + ); + } + + /** + * Call webapi service and assert order list + * + * @param array $expectedListOrder + * @param array $wrongListOrder + * @return void + */ + private function assertSourceSelectionOrderLists(array $expectedListOrder, array $wrongListOrder): void + { + $countriesToTest = $this->getCountriesToTestData(); $inventoryRequest = [ 'stockId' => 10, 'items' => [ @@ -88,38 +161,6 @@ public function testSourceSelectionService() ], ]; - $expectedResultData = - [ - 'DE' => [ - 'source_selection_items' => [ - [ - 'source_code' => 'eu-2', - 'sku' => 'SKU-1', - 'qty_to_deduct' => 1, - 'qty_available' => 3, - ] - ], - 'shippable' => 1, - ], - 'US' => [ - 'source_selection_items' => [ - [ - 'source_code' => 'eu-1', - 'sku' => 'SKU-1', - 'qty_to_deduct' => 1, - 'qty_available' => 5.5, - ], - [ - 'source_code' => 'eu-2', - 'sku' => 'SKU-1', - 'qty_to_deduct' => 1, - 'qty_available' => 3, - ], - ], - 'shippable' => 1, - ], - ]; - $algorithmCode = DefaultForCountryAlgorithm::CODE; $requestData = [ 'inventoryRequest' => $inventoryRequest, @@ -140,30 +181,32 @@ public function testSourceSelectionService() foreach ($countriesToTest as $countryCode => $sources) { $requestData['inventoryRequest']['extension_attributes']['destination_address']['country'] = $countryCode; - $sourceSelectionAlgorithmResult = (TESTS_WEB_API_ADAPTER === self::ADAPTER_REST) - ? $this->_webApiCall($serviceInfo, $requestData) - : $this->_webApiCall($serviceInfo, $requestData); + $response = $this->_webApiCall($serviceInfo, $requestData); - $this->assertIsArray($sourceSelectionAlgorithmResult); - $this->assertNotEmpty($sourceSelectionAlgorithmResult); - AssertArrayContains::assert($expectedResultData, $sourceSelectionAlgorithmResult); + $this->assertIsArray($response); + $this->assertNotEmpty($response); + $this->assertArrayHasKey('source_selection_items', $response); + $listByPriority = []; + foreach ($response['source_selection_items'] as $sourceSelectionItem) { + $this->assertIsArray($sourceSelectionItem); + $this->assertArrayHasKey('source_code', $sourceSelectionItem); + $listByPriority[] = $sourceSelectionItem['source_code']; + } + $this->assertEquals($expectedListOrder[$countryCode], $listByPriority); + $this->assertNotEquals($wrongListOrder[$countryCode], $listByPriority); } } /** - * Setup test instance + * Countries to test data array + * + * @return array */ - protected function setUp(): void + private function getCountriesToTestData(): array { - parent::setUp(); - $this->defaultAlgorithmCode = Bootstrap::getObjectManager()->get( - GetDefaultSourceSelectionAlgorithmCodeInterface::class - ); - $this->sourceRepository = Bootstrap::getObjectManager()->get( - SourceRepositoryInterface::class - ); - $this->sourceFactory = Bootstrap::getObjectManager()->get( - SourceInterfaceFactory::class - ); + return [ + 'DE' => ['eu-2'], + 'FR' => ['eu-1'], + ]; } } diff --git a/InventoryDefaultForCountrySourceSelection/composer.json b/InventoryDefaultForCountrySourceSelection/composer.json index 15c56456762a..bcaa5d873216 100644 --- a/InventoryDefaultForCountrySourceSelection/composer.json +++ b/InventoryDefaultForCountrySourceSelection/composer.json @@ -4,9 +4,9 @@ "require": { "php": "~7.3.0||~7.4.0", "magento/framework": "*", - "magento/module-inventory-distance-based-source-selection-api": "1.1.*", - "magento/module-inventory-source-selection-api": "1.3.*", - "magento/module-inventory-api": "1.1.*", + "magento/module-inventory-distance-based-source-selection-api": "*", + "magento/module-inventory-source-selection-api": "*", + "magento/module-inventory-api": "*", "magento/module-config": "*" }, "type": "magento2-module", @@ -22,5 +22,5 @@ "Magento\\InventoryDefaultForCountrySourceSelection\\": "" } }, - "version": "1.1.0" + "version": "1.2.0" } diff --git a/InventoryDefaultForCountrySourceSelectionAdminUi/composer.json b/InventoryDefaultForCountrySourceSelectionAdminUi/composer.json index 2d1b18591030..43409220ac19 100644 --- a/InventoryDefaultForCountrySourceSelectionAdminUi/composer.json +++ b/InventoryDefaultForCountrySourceSelectionAdminUi/composer.json @@ -3,7 +3,7 @@ "description": "N/A", "require": { "php": "~7.3.0||~7.4.0", - "magento/framework": "*" + "magento/module-inventory-default-for-country-source-selection": "*" }, "type": "magento2-module", "license": [ @@ -18,5 +18,5 @@ "Magento\\InventoryDefaultForCountrySourceSelectionAdminUi\\": "" } }, - "version": "1.1.0" + "version": "1.2.0" } diff --git a/InventoryDefaultForCountrySourceSelectionAdminUi/etc/module.xml b/InventoryDefaultForCountrySourceSelectionAdminUi/etc/module.xml index 4c9a01e69660..adc51282d310 100644 --- a/InventoryDefaultForCountrySourceSelectionAdminUi/etc/module.xml +++ b/InventoryDefaultForCountrySourceSelectionAdminUi/etc/module.xml @@ -8,5 +8,8 @@ + + + From 7b488d9a9056da79f5d4bd350d94e9bd3c4a3448 Mon Sep 17 00:00:00 2001 From: "Vasiliev.A" Date: Fri, 5 Feb 2021 11:22:12 +0200 Subject: [PATCH 4/5] fix tests --- InventoryDefaultForCountrySourceSelection/composer.json | 5 +---- .../composer.json | 1 + _metapackage/composer.json | 5 +++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/InventoryDefaultForCountrySourceSelection/composer.json b/InventoryDefaultForCountrySourceSelection/composer.json index bcaa5d873216..734c8647cc3e 100644 --- a/InventoryDefaultForCountrySourceSelection/composer.json +++ b/InventoryDefaultForCountrySourceSelection/composer.json @@ -4,10 +4,7 @@ "require": { "php": "~7.3.0||~7.4.0", "magento/framework": "*", - "magento/module-inventory-distance-based-source-selection-api": "*", - "magento/module-inventory-source-selection-api": "*", - "magento/module-inventory-api": "*", - "magento/module-config": "*" + "magento/module-inventory-distance-based-source-selection-api": "*" }, "type": "magento2-module", "license": [ diff --git a/InventoryDefaultForCountrySourceSelectionAdminUi/composer.json b/InventoryDefaultForCountrySourceSelectionAdminUi/composer.json index 43409220ac19..f0bc6ad99ece 100644 --- a/InventoryDefaultForCountrySourceSelectionAdminUi/composer.json +++ b/InventoryDefaultForCountrySourceSelectionAdminUi/composer.json @@ -3,6 +3,7 @@ "description": "N/A", "require": { "php": "~7.3.0||~7.4.0", + "magento/framework": "*", "magento/module-inventory-default-for-country-source-selection": "*" }, "type": "magento2-module", diff --git a/_metapackage/composer.json b/_metapackage/composer.json index 625387ab1df2..31edbce07c23 100644 --- a/_metapackage/composer.json +++ b/_metapackage/composer.json @@ -4,7 +4,6 @@ "description": "Metapackage with Magento Inventory modules for simple installation", "type": "metapackage", "require": { - "magento/inventory-composer-installer": "1.1.0", "magento/module-inventory": "*", "magento/module-inventory-admin-ui": "*", "magento/module-inventory-advanced-checkout": "*", @@ -75,6 +74,8 @@ "magento/module-inventory-configurable-product-frontend-ui": "*", "magento/module-inventory-wishlist": "*", "magento/module-inventory-catalog-search-bundle-product": "*", - "magento/module-inventory-catalog-search-configurable-product": "*" + "magento/module-inventory-catalog-search-configurable-product": "*", + "magento/module-inventory-default-for-country-source-selection": "*", + "magento/module-inventory-default-for-country-source-selection-admin-ui": "*" } } From 68297f8513e15253a28d149e8c1166a2d6bd4fb5 Mon Sep 17 00:00:00 2001 From: "Vasiliev.A" Date: Sat, 20 Feb 2021 00:30:13 +0200 Subject: [PATCH 5/5] fix dependencies --- InventoryDefaultForCountrySourceSelection/composer.json | 5 ++++- InventoryDefaultForCountrySourceSelection/etc/module.xml | 3 +++ .../composer.json | 3 ++- .../etc/module.xml | 2 ++ 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/InventoryDefaultForCountrySourceSelection/composer.json b/InventoryDefaultForCountrySourceSelection/composer.json index 734c8647cc3e..d19efed422af 100644 --- a/InventoryDefaultForCountrySourceSelection/composer.json +++ b/InventoryDefaultForCountrySourceSelection/composer.json @@ -4,7 +4,10 @@ "require": { "php": "~7.3.0||~7.4.0", "magento/framework": "*", - "magento/module-inventory-distance-based-source-selection-api": "*" + "magento/module-store": "*", + "magento/module-inventory": "*", + "magento/module-inventory-api": "*", + "magento/module-inventory-source-selection-api": "*" }, "type": "magento2-module", "license": [ diff --git a/InventoryDefaultForCountrySourceSelection/etc/module.xml b/InventoryDefaultForCountrySourceSelection/etc/module.xml index cb575aa63f5d..0de56300f798 100644 --- a/InventoryDefaultForCountrySourceSelection/etc/module.xml +++ b/InventoryDefaultForCountrySourceSelection/etc/module.xml @@ -9,7 +9,10 @@ xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + + + diff --git a/InventoryDefaultForCountrySourceSelectionAdminUi/composer.json b/InventoryDefaultForCountrySourceSelectionAdminUi/composer.json index f0bc6ad99ece..2f83373d9840 100644 --- a/InventoryDefaultForCountrySourceSelectionAdminUi/composer.json +++ b/InventoryDefaultForCountrySourceSelectionAdminUi/composer.json @@ -4,7 +4,8 @@ "require": { "php": "~7.3.0||~7.4.0", "magento/framework": "*", - "magento/module-inventory-default-for-country-source-selection": "*" + "magento/module-inventory-default-for-country-source-selection": "*", + "magento/module-inventory-source-selection-api": "*" }, "type": "magento2-module", "license": [ diff --git a/InventoryDefaultForCountrySourceSelectionAdminUi/etc/module.xml b/InventoryDefaultForCountrySourceSelectionAdminUi/etc/module.xml index adc51282d310..dc68398df964 100644 --- a/InventoryDefaultForCountrySourceSelectionAdminUi/etc/module.xml +++ b/InventoryDefaultForCountrySourceSelectionAdminUi/etc/module.xml @@ -9,6 +9,8 @@ xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + +