ddtcorex

22 Sept 2026 · 4 min read

In-store pickup and source selection edge cases

Handle in-store pickup source selection, distance priority and out-of-stock stores without breaking checkout

Part 3 of the MSI / Inventory series — pickup looks simple until the nearest store has zero units.

The nearest store had zero units

A fashion merchant enabled in-store pickup on twelve locations and immediately got support tickets: customers chose the closest store, drove there, and were told the item was out of stock. Admin showed each store had the SKU. The bug was ordering: the pickup location list sorted by distance first and filtered stock second, so a store 500 meters away with zero salable units outranked a store 3 km away with ten. Pickup mixes geography with inventory, and geography must never win over salability.

Why this matters

Pickup is the only checkout path where the customer commits to a physical source before payment. Shipment can re-route between warehouses after the order; pickup cannot. Checkout must therefore filter candidate sources to the order's stock, keep only sources flagged for pickup, check per-source salable quantity, and only then rank by distance or priority. Skip any step and the storefront offers a promise a specific shelf cannot keep, with a customer already in transit.

How it works

The pickup pipeline per checkout:

  1. Resolve the stock from the website via inventory_stock_sales_channel. Only sources linked to this stock in inventory_source_stock_link are candidates.
  2. Filter to pickup-enabled sources: inventory_source.use_as_pickup_location = 1 plus the source's frontend name, address, and pickup_location_code configured. Sources without staff workflow or opening hours must stay shipment-only.
  3. Check per-source salability. The stock view gives pooled salability, but pickup needs per-source truth: inventory_source_item.quantity minus reservations attributable to that source. The inventory_in_store_pickup module exposes this through the pickup location search API with the search-request extension attributes.
  4. Rank with a Source Selection Algorithm: priority (admin-ordered), distance (lat/lon via inventory_geoname after bin/magento inventory-geonames:import), or custom logic implementing SourceSelectionInterface. Distance sorting runs last, never first.

Key commands and config:

bin/magento inventory-geonames:import
bin/magento indexer:reindex inventory
SELECT source_code, name, use_as_pickup_location, latitude, longitude
FROM inventory_source
WHERE use_as_pickup_location = 1;

SELECT source_code, sku, quantity, status
FROM inventory_source_item
WHERE sku = 'SKU-123' AND status = 1;

Config paths: Stores > Configuration > Sales > Delivery Methods > In-Store Delivery (enable, search radius, sort order), and each source's record under Manage Sources (pickup flag, address, coordinates). The GraphQL pickupLocations query and REST GET /V1/inventory/in-store-pickup/pickup-locations both accept a search area; verify the area filter cannot return a source outside the order's stock by testing with a SKU stocked only in a non-pickup warehouse.

Unavailable pickup must fall back to shipment, never to a dead end. If no pickup source has salable qty, keep shipping methods visible and say so explicitly ("Pickup unavailable at nearby stores, shipment still available"). Hiding shipment when pickup fails converts a partial constraint into a lost order, the worst possible trade imaginable.

Pitfalls

  • Enabling pickup on sources with no staff workflow behind them. A flag without a packing bench, notification email, and hold shelf creates orders nobody fulfills. Enable pickup only where staff confirm pickup-ready in the admin workflow.
  • Distance sorting without stock filtering first. Sorting twelve stores by kilometers before checking inventory_source_item promotes the closest empty store. Filter by stock, flag, and quantity, then sort.
  • Hiding shipment when pickup fails. Some themes collapse shipping methods once pickup is selected. Keep both visible so a stockout at one store does not block the whole checkout.
  • Missing geonames data. Distance algorithms without imported inventory_geoname rows fall back to arbitrary order. Import geonames per country served and re-check after adding new regions.

Field check on a live store

Test pickup as a customer, not as an admin. Choose a SKU stocked in exactly one pickup-enabled source and one shipment-only warehouse, then search pickup locations from a postcode near the shipment warehouse. The shipment-only source must never appear in the pickup list regardless of distance. Repeat with a SKU at zero salable units in the nearest store but positive in a farther one: the near store must be absent or flagged unavailable, never selectable. The fashion merchant's bug survived staging because test data stocked every store evenly, so distance order and stock order coincided. One asymmetric SKU would have exposed the ordering bug before launch. Add that asymmetric case to regression data permanently, and re-run it after every inventory module upgrade since source selection behavior shifts between releases.

Closing

Pickup is a constrained promise to a specific shelf. Filter to the stock, keep only real pickup locations, verify per-source salability, then rank. Log every fallback from pickup to shipment so merchandising sees which stores turn customers away; a store with a rising fallback rate is either understocked or wrongly flagged. With pickup honest, the last inventory promise to price correctly is selling what has not arrived yet.

Next in this series:

magento2-msi-backorders

magento2msiinventory