diff --git a/app/code/Magento/InventoryInStorePickup/Model/ResourceModel/OrderPickupLocation/GetPickupLocationByOrderId.php b/app/code/Magento/InventoryInStorePickup/Model/ResourceModel/OrderPickupLocation/GetPickupLocationByOrderId.php
new file mode 100644
index 000000000000..307b0bc7fe7b
--- /dev/null
+++ b/app/code/Magento/InventoryInStorePickup/Model/ResourceModel/OrderPickupLocation/GetPickupLocationByOrderId.php
@@ -0,0 +1,56 @@
+connection = $connection;
+ }
+
+ /**
+ * Fetch pickup location identifier by order identifier.
+ *
+ * @param int $orderId
+ *
+ * @return string|null
+ */
+ public function execute(int $orderId): ?string
+ {
+ $connection = $this->connection->getConnection();
+ $table = $this->connection->getTableName('inventory_pickup_location_order');
+
+ $select = $connection->select()
+ ->from($table, [self::PICKUP_LOCATION_CODE => self::PICKUP_LOCATION_CODE])
+ ->where(self::ORDER_ID . '= ?', $orderId)
+ ->limit(1);
+
+ $id = $connection->fetchOne($select);
+
+ return $id ?: null;
+ }
+}
diff --git a/app/code/Magento/InventoryInStorePickup/Model/ResourceModel/OrderPickupLocation/SaveOrderPickupLocation.php b/app/code/Magento/InventoryInStorePickup/Model/ResourceModel/OrderPickupLocation/SaveOrderPickupLocation.php
new file mode 100644
index 000000000000..15b07555f4e1
--- /dev/null
+++ b/app/code/Magento/InventoryInStorePickup/Model/ResourceModel/OrderPickupLocation/SaveOrderPickupLocation.php
@@ -0,0 +1,56 @@
+connection = $connection;
+ }
+
+ /**
+ * Fetch pickup location identifier by order identifier.
+ *
+ * @param int $orderId
+ * @param string $pickupLocationCode
+ *
+ * @return void
+ */
+ public function execute(int $orderId, string $pickupLocationCode): void
+ {
+ $connection = $this->connection->getConnection();
+ $table = $this->connection->getTableName('inventory_pickup_location_order');
+
+ $data = [
+ self::ORDER_ID => $orderId,
+ self::PICKUP_LOCATION_CODE => $pickupLocationCode
+ ];
+
+ $connection->insertOnDuplicate($table, $data);
+ }
+}
diff --git a/app/code/Magento/InventoryInStorePickup/Plugin/Sales/Order/GetPickupLocationForOrderPlugin.php b/app/code/Magento/InventoryInStorePickup/Plugin/Sales/Order/GetPickupLocationForOrderPlugin.php
new file mode 100644
index 000000000000..fa34698bcebc
--- /dev/null
+++ b/app/code/Magento/InventoryInStorePickup/Plugin/Sales/Order/GetPickupLocationForOrderPlugin.php
@@ -0,0 +1,73 @@
+orderExtensionFactory = $orderExtensionFactory;
+ $this->getPickupLocationByOrderId = $getPickupLocationByOrderId;
+ }
+
+ /**
+ * Add Pickup Location Code extension attribute when loading Order with OrderRepository.
+ *
+ * @param OrderRepositoryInterface $orderRepository
+ * @param OrderInterface $order
+ *
+ * @return OrderInterface
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function afterGet(OrderRepositoryInterface $orderRepository, OrderInterface $order): OrderInterface
+ {
+ $extension = $order->getExtensionAttributes();
+
+ if (empty($extension)) {
+ $extension = $this->orderExtensionFactory->create();
+ }
+
+ if ($extension->getPickupLocationCode()) {
+ return $order;
+ }
+
+ $pickupLocationCode = $this->getPickupLocationByOrderId->execute((int)$order->getEntityId());
+
+ if ($pickupLocationCode) {
+ $extension->setPickupLocationCode($pickupLocationCode);
+ }
+
+ $order->setExtensionAttributes($extension);
+
+ return $order;
+ }
+}
diff --git a/app/code/Magento/InventoryInStorePickup/Plugin/Sales/Order/SavePickupLocationForOrderPlugin.php b/app/code/Magento/InventoryInStorePickup/Plugin/Sales/Order/SavePickupLocationForOrderPlugin.php
new file mode 100644
index 000000000000..a0c163d4631c
--- /dev/null
+++ b/app/code/Magento/InventoryInStorePickup/Plugin/Sales/Order/SavePickupLocationForOrderPlugin.php
@@ -0,0 +1,55 @@
+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
+ *
+ * @return \Magento\Sales\Api\Data\OrderInterface
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function afterSave(
+ OrderRepositoryInterface $orderRepository,
+ OrderInterface $result,
+ OrderInterface $entity
+ ) {
+ $extension = $result->getExtensionAttributes();
+
+ if (!empty($extension) && $extension->getPickupLocationCode()) {
+ $this->saveOrderPickupLocation->execute((int)$result->getEntityId(), $extension->getPickupLocationCode());
+ }
+
+ return $result;
+ }
+}
diff --git a/app/code/Magento/InventoryInStorePickup/Test/Integration/PickupLocationOrderTest.php b/app/code/Magento/InventoryInStorePickup/Test/Integration/PickupLocationOrderTest.php
new file mode 100644
index 000000000000..ef2321e737d9
--- /dev/null
+++ b/app/code/Magento/InventoryInStorePickup/Test/Integration/PickupLocationOrderTest.php
@@ -0,0 +1,85 @@
+objectManager = Bootstrap::getObjectManager();
+
+ $this->orderRepository = $this->objectManager->get(OrderRepositoryInterface::class);
+ $this->searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class);
+ $this->orderExtensionFactory = $this->objectManager->get(OrderExtensionFactory::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/InventorySalesApi/Test/_files/websites_with_stores.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/stock_website_sales_channels.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryApi/Test/_files/source_items.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryIndexer/Test/_files/reindex_inventory.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryInStorePickup/Test/_files/create_in_store_pickup_quote_on_eu_website.php
+ * @magentoDataFixture ../../../../app/code/Magento/InventoryInStorePickup/Test/_files/place_order.php
+ *
+ * @magentoDbIsolation disabled
+ */
+ public function testPickupLocationSaveWithOrder()
+ {
+ $sourceId = 'eu-1';
+
+ $searchCriteria = $this->searchCriteriaBuilder
+ ->addFilter('increment_id', 'in_store_pickup_test_order')
+ ->create();
+ /** @var OrderInterface $createdOrder */
+ $createdOrder = current($this->orderRepository->getList($searchCriteria)->getItems());
+ $orderId = $createdOrder->getEntityId();
+
+ $extension = $createdOrder->getExtensionAttributes();
+
+ if (empty($extension)) {
+ /** @var \Magento\Sales\Api\Data\OrderExtensionInterface $extension */
+ $extension = $this->orderExtensionFactory->create();
+ }
+
+ $extension->setPickupLocationCode($sourceId);
+ $createdOrder->setExtensionAttributes($extension);
+
+ $this->orderRepository->save($createdOrder);
+
+ // Remove value to re-load from DB during 'get'.
+ $extension->setPickupLocationCode(null);
+
+ $order = $this->orderRepository->get($orderId);
+
+ $this->assertEquals($order->getExtensionAttributes()->getPickupLocationCode(), $sourceId);
+ }
+}
diff --git a/app/code/Magento/InventoryInStorePickup/Test/_files/create_in_store_pickup_quote_on_eu_website.php b/app/code/Magento/InventoryInStorePickup/Test/_files/create_in_store_pickup_quote_on_eu_website.php
new file mode 100644
index 000000000000..fa289d7dbb02
--- /dev/null
+++ b/app/code/Magento/InventoryInStorePickup/Test/_files/create_in_store_pickup_quote_on_eu_website.php
@@ -0,0 +1,59 @@
+get(CartRepositoryInterface::class);
+/** @var CartManagementInterface $cartManagement */
+$cartManagement = Bootstrap::getObjectManager()->get(CartManagementInterface::class);
+/** @var AddressInterfaceFactory $addressFactory */
+$addressFactory = Bootstrap::getObjectManager()->get(AddressInterfaceFactory::class);
+/** @var StoreRepositoryInterface $storeRepository */
+$storeRepository = Bootstrap::getObjectManager()->get(StoreRepositoryInterface::class);
+/** @var StoreManagerInterface\ $storeManager */
+$storeManager = Bootstrap::getObjectManager()->get(StoreManagerInterface::class);
+
+$cartId = $cartManagement->createEmptyCart();
+$cart = $cartRepository->get($cartId);
+$cart->setCustomerEmail('admin@example.com');
+$cart->setCustomerIsGuest(true);
+$store = $storeRepository->get('store_for_eu_website');
+$cart->setStoreId($store->getId());
+$storeManager->setCurrentStore($store->getCode());
+
+/** @var AddressInterface $address */
+$address = $addressFactory->create(
+ [
+ 'data' => [
+ AddressInterface::KEY_COUNTRY_ID => 'US',
+ AddressInterface::KEY_REGION_ID => 15,
+ AddressInterface::KEY_LASTNAME => 'Doe',
+ AddressInterface::KEY_FIRSTNAME => 'John',
+ AddressInterface::KEY_STREET => 'example street',
+ AddressInterface::KEY_EMAIL => 'customer@example.com',
+ AddressInterface::KEY_CITY => 'Los Angeles',
+ AddressInterface::KEY_TELEPHONE => '937 99 92',
+ AddressInterface::KEY_POSTCODE => 12345
+ ]
+ ]
+);
+$cart->setReservedOrderId('in_store_pickup_test_order');
+$cart->setBillingAddress($address);
+$cart->setShippingAddress($address);
+$cart->getPayment()->setMethod('checkmo');
+/** Will be replaced with 'In Store Pickup' delivery method */
+$cart->getShippingAddress()->setShippingMethod('flatrate_flatrate');
+$cart->getShippingAddress()->setCollectShippingRates(true);
+$cart->getShippingAddress()->collectShippingRates();
+$cartRepository->save($cart);
diff --git a/app/code/Magento/InventoryInStorePickup/Test/_files/create_in_store_pickup_quote_on_eu_website_rollback.php b/app/code/Magento/InventoryInStorePickup/Test/_files/create_in_store_pickup_quote_on_eu_website_rollback.php
new file mode 100644
index 000000000000..0ac614eb443d
--- /dev/null
+++ b/app/code/Magento/InventoryInStorePickup/Test/_files/create_in_store_pickup_quote_on_eu_website_rollback.php
@@ -0,0 +1,40 @@
+get(Registry::class);
+
+/** @var CartRepositoryInterface $cartRepository */
+$cartRepository = Bootstrap::getObjectManager()->get(CartRepositoryInterface::class);
+/** @var SearchCriteriaBuilder $searchCriteriaBuilder */
+$searchCriteriaBuilder = Bootstrap::getObjectManager()->get(SearchCriteriaBuilder::class);
+
+$searchCriteria = $searchCriteriaBuilder
+ ->addFilter('reserved_order_id', 'in_store_pickup_test_order')
+ ->create();
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+/** @var CartInterface[] $order */
+$carts = $cartRepository->getList($searchCriteria)->getItems();
+foreach ($carts as $cart) {
+ $cartRepository->delete($cart);
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
+
+/* Refresh stores memory cache */
+Bootstrap::getObjectManager()->get(StoreManagerInterface::class)->reinitStores();
diff --git a/app/code/Magento/InventoryInStorePickup/Test/_files/place_order.php b/app/code/Magento/InventoryInStorePickup/Test/_files/place_order.php
new file mode 100644
index 000000000000..8e5a39c7e3ea
--- /dev/null
+++ b/app/code/Magento/InventoryInStorePickup/Test/_files/place_order.php
@@ -0,0 +1,37 @@
+get(SearchCriteriaBuilder::class);
+/** @var CartRepositoryInterface $cartRepository */
+$cartRepository = Bootstrap::getObjectManager()->get(CartRepositoryInterface::class);
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class);
+/** @var CartManagementInterface $cartManagement */
+$cartManagement = Bootstrap::getObjectManager()->get(CartManagementInterface::class);
+
+$searchCriteria = $searchCriteriaBuilder
+ ->addFilter('reserved_order_id', 'in_store_pickup_test_order')
+ ->create();
+$cart = current($cartRepository->getList($searchCriteria)->getItems());
+
+$product = $productRepository->get('SKU-1');
+$requestData = [
+ 'product' => $product->getProductId(),
+ 'qty' => 3.5
+];
+$request = new \Magento\Framework\DataObject($requestData);
+$cart->addProduct($product, $request);
+
+$cartRepository->save($cart);
+$cartManagement->placeOrder($cart->getId());
diff --git a/app/code/Magento/InventoryInStorePickup/Test/_files/place_order_rollback.php b/app/code/Magento/InventoryInStorePickup/Test/_files/place_order_rollback.php
new file mode 100644
index 000000000000..194465bb9eaa
--- /dev/null
+++ b/app/code/Magento/InventoryInStorePickup/Test/_files/place_order_rollback.php
@@ -0,0 +1,41 @@
+get(Registry::class);
+
+/** @var OrderRepositoryInterface $orderRepository */
+$orderRepository = Bootstrap::getObjectManager()->get(OrderRepositoryInterface::class);
+/** @var OrderManagementInterface $orderManagement */
+$orderManagement = Bootstrap::getObjectManager()->get(OrderManagementInterface::class);
+/** @var SearchCriteriaBuilder $searchCriteriaBuilder */
+$searchCriteriaBuilder = Bootstrap::getObjectManager()->get(SearchCriteriaBuilder::class);
+
+$searchCriteria = $searchCriteriaBuilder
+ ->addFilter('increment_id', 'in_store_pickup_test_order')
+ ->create();
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+/** @var OrderInterface $order */
+$order = current($orderRepository->getList($searchCriteria)->getItems());
+if ($order) {
+ $orderManagement->cancel($order->getEntityId());
+ $orderRepository->delete($order);
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/app/code/Magento/InventoryInStorePickup/composer.json b/app/code/Magento/InventoryInStorePickup/composer.json
index 5938a3b3e082..caa00eafee06 100644
--- a/app/code/Magento/InventoryInStorePickup/composer.json
+++ b/app/code/Magento/InventoryInStorePickup/composer.json
@@ -5,7 +5,8 @@
"php": "~7.1.3||~7.2.0",
"magento/framework": "*",
"magento/module-inventory-in-store-pickup-api": "*",
- "magento/module-inventory-api": "*"
+ "magento/module-inventory-api": "*",
+ "magento/module-sales": "*"
},
"type": "magento2-module",
"license": [
diff --git a/app/code/Magento/InventoryInStorePickup/etc/db_schema.xml b/app/code/Magento/InventoryInStorePickup/etc/db_schema.xml
new file mode 100644
index 000000000000..ce0351a1aba0
--- /dev/null
+++ b/app/code/Magento/InventoryInStorePickup/etc/db_schema.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
diff --git a/app/code/Magento/InventoryInStorePickup/etc/db_schema_whitelist.json b/app/code/Magento/InventoryInStorePickup/etc/db_schema_whitelist.json
new file mode 100644
index 000000000000..6577e0903af9
--- /dev/null
+++ b/app/code/Magento/InventoryInStorePickup/etc/db_schema_whitelist.json
@@ -0,0 +1,12 @@
+{
+ "inventory_pickup_location_order": {
+ "column": {
+ "order_id": true,
+ "pickup_location_code": true
+ },
+ "constraint": {
+ "PRIMARY": true,
+ "INVENTORY_PICKUP_LOCATION_ORDER_ORDER_ID_SALES_ORDER_ENTITY_ID": true
+ }
+ }
+}
diff --git a/app/code/Magento/InventoryInStorePickup/etc/di.xml b/app/code/Magento/InventoryInStorePickup/etc/di.xml
index eed5311a30ee..a7d9095347de 100644
--- a/app/code/Magento/InventoryInStorePickup/etc/di.xml
+++ b/app/code/Magento/InventoryInStorePickup/etc/di.xml
@@ -5,10 +5,13 @@
* See COPYING.txt for license details.
*/
-->
-
+
+
+
+
+
+ type="Magento\InventoryInStorePickup\Model\GetNearbySourcesByPostcode"/>
diff --git a/app/code/Magento/InventoryInStorePickup/etc/extension_attributes.xml b/app/code/Magento/InventoryInStorePickup/etc/extension_attributes.xml
new file mode 100644
index 000000000000..9658b9c75e5e
--- /dev/null
+++ b/app/code/Magento/InventoryInStorePickup/etc/extension_attributes.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+ pickup_location_code
+
+
+
+
diff --git a/app/code/Magento/InventoryInStorePickup/etc/module.xml b/app/code/Magento/InventoryInStorePickup/etc/module.xml
index 850ca9e9c6a7..224ec751bd77 100644
--- a/app/code/Magento/InventoryInStorePickup/etc/module.xml
+++ b/app/code/Magento/InventoryInStorePickup/etc/module.xml
@@ -6,5 +6,9 @@
*/
-->
-
+
+
+
+
+