Regional Warehouse & Delivery Implementation for E-commerce
Imagine an online store with 5 warehouses across Russia. Without proper order distribution, a delivery to Novosibirsk might ship from Moscow even though the item is available in Krasnoyarsk. The customer pays more and waits longer. The result is increased returns and lower conversion rates. Our system automatically finds the nearest warehouse, reserves stock there, and calculates delivery from the actual dispatch city. On average, this reduces delivery time by 40% for regional customers and cuts operational costs by 25% through route optimization. The solution is built on Laravel 11 and React 18, using Repository and BFF patterns.
Problems We Solve
- Reservation errors. The system always selects the warehouse closest to the customer and reserves stock there. The customer receives the order faster and cheaper.
- Incorrect delivery calculation. Delivery is calculated from the actual warehouse, not the central one. This reduces costs for regional buyers by an average of 15–20%.
- Scattered stock data. The product card shows total stock across all warehouses as well as city-level details—the customer sees exactly where the item is available.
Unlike a simple "in stock/out of stock" indicator, we display exact quantities per warehouse and offer pickup from the nearest location. This reduces return rates by 15–20%.
How to Select a Warehouse for the Customer?
The core algorithm is implemented in the WarehouseSelector class on Laravel. It receives the product ID, required quantity, and destination city, then performs two steps:
- Finds warehouses where
available >= quantity, sorted by priority (configurable in admin panel). - If geocoding of the city succeeds, sorts by distance to the customer using the Haversine formula. If not, selects the first by priority.
class WarehouseSelector
{
public function selectForDelivery(
int $productId,
int $quantity,
string $destinationCity,
): ?WarehouseSelectionResult {
// Warehouses with sufficient stock
$available = WarehouseStock::where('product_id', $productId)
->where('available', '>=', $quantity)
->with('warehouse')
->orderByDesc('warehouse.priority')
->get();
if ($available->isEmpty()) {
// Try split—take from multiple warehouses
return $this->splitWarehouseSelection($productId, $quantity);
}
// Sort by proximity to customer
$coords = $this->geocoder->getCoords($destinationCity);
if ($coords) {
$sorted = $available->sortBy(function ($stock) use ($coords) {
return $this->haversineDistance(
$coords['lat'], $coords['lng'],
$stock->warehouse->lat, $stock->warehouse->lng,
);
});
return new WarehouseSelectionResult(
warehouse: $sorted->first()->warehouse,
isSplit: false,
);
}
// Geocoding failed—take by priority
return new WarehouseSelectionResult(
warehouse: $available->first()->warehouse,
isSplit: false,
);
}
private function haversineDistance(float $lat1, float $lng1, float $lat2, float $lng2): float
{
$R = 6371; // km
$dLat = deg2rad($lat2 - $lat1);
$dLng = deg2rad($lng2 - $lng1);
$a = sin($dLat / 2) ** 2
+ cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin($dLng / 2) ** 2;
return $R * 2 * atan2(sqrt($a), sqrt(1 - $a));
}
}
Why Is Stock Reservation Important at Order Time?
Without reservation, a race condition can occur: two customers may simultaneously place an order for the last unit. Our StockReservationService uses lockForUpdate locks within a transaction to avoid double reservation. The reservation lasts 30 minutes, after which it is automatically canceled.
class StockReservationService
{
public function reserve(Order $order): ReservationResult
{
$reservations = [];
DB::transaction(function () use ($order, &$reservations) {
foreach ($order->items as $item) {
// Find warehouse with required quantity
$stock = WarehouseStock::where('product_id', $item->product_id)
->where('available', '>=', $item->quantity)
->where('warehouse_id', $item->preferred_warehouse_id
?? $this->getNearestWarehouse($order->delivery_city, $item->product_id)->id
)
->lockForUpdate()
->first();
if (!$stock) {
throw new InsufficientStockException(
"Insufficient stock at warehouse: SKU {$item->product->sku}"
);
}
// Increment reserved
$stock->increment('reserved', $item->quantity);
$reservations[] = StockReservation::create([
'order_id' => $order->id,
'order_item_id' => $item->id,
'warehouse_id' => $stock->warehouse_id,
'product_id' => $item->product_id,
'quantity' => $item->quantity,
'expires_at' => now()->addMinutes(30),
]);
}
});
return new ReservationResult(reservations: $reservations);
}
public function cancelExpiredReservations(): int
{
$expired = StockReservation::where('status', 'reserved')
->where('expires_at', '<', now())
->get();
foreach ($expired as $reservation) {
DB::transaction(function () use ($reservation) {
WarehouseStock::where([
'product_id' => $reservation->product_id,
'warehouse_id' => $reservation->warehouse_id,
])->decrement('reserved', $reservation->quantity);
$reservation->update(['status' => 'cancelled']);
});
}
return $expired->count();
}
}
How Is Delivery Calculated with Warehouse Awareness?
RegionalDeliveryCalculator groups cart items by selected warehouse and requests delivery cost from the carrier calculator for each group. If items ship from multiple warehouses, costs are summed, and the customer is shown a single option—the minimum price for each method.
class RegionalDeliveryCalculator
{
public function calculate(
array $cartItems,
string $destination,
): DeliveryResult {
// Group items by warehouse
$warehouseGroups = $this->groupByWarehouse($cartItems, $destination);
$allOptions = collect();
foreach ($warehouseGroups as $warehouseId => $items) {
$warehouse = Warehouse::find($warehouseId);
// Calculate delivery from specific warehouse
$request = new DeliveryRequest(
fromCity: $warehouse->city,
fromLat: $warehouse->lat,
fromLng: $warehouse->lng,
destination: $destination,
items: $items,
);
$options = $this->carrierCalculator->calculate($request);
// If multiple warehouses, sum the costs
if (count($warehouseGroups) > 1) {
$allOptions = $allOptions->merge(
$options->map(fn($o) => $o->withWarehouseNote($warehouse->city))
);
} else {
$allOptions = $allOptions->merge($options);
}
}
// Deduplicate by delivery method—select the best option
return new DeliveryResult(
options: $allOptions->groupBy('method')
->map(fn($g) => $g->sortBy('price')->first())
->values(),
);
}
private function groupByWarehouse(array $cartItems, string $destination): array
{
$groups = [];
foreach ($cartItems as $item) {
$warehouse = $this->selector->selectForDelivery(
$item->product_id,
$item->quantity,
$destination,
);
$warehouseId = $warehouse?->warehouse->id ?? $this->defaultWarehouse->id;
$groups[$warehouseId][] = $item;
}
return $groups;
}
}
Displaying Warehouse Stock on the Product Card
The React frontend component queries stock by city via API and shows a collapsible list. If no stock is available at any warehouse, it displays "Out of stock".
// Stock by city component
const StockByCity: React.FC<{ productId: number }> = ({ productId }) => {
const { data } = useQuery(['stock', productId], () => fetchStockByCity(productId));
if (!data?.length) return <span className="text-red-500">Out of stock</span>;
return (
<details className="text-sm">
<summary className="cursor-pointer text-green-600 font-medium">
In stock — {data.length} city/cities
</summary>
<ul className="mt-1 space-y-1 pl-3">
{data.map(s => (
<li key={s.warehouse} className="text-gray-600">
{s.city} — {s.qty} pcs.
</li>
))}
</ul>
</details>
);
};
Implementation Process
Implementation follows 5 stages:
| Stage | Duration |
|---|---|
| Analysis of current architecture and data collection | 1-2 days |
| Data schema and API design | 1 day |
| Backend class implementation (WarehouseSelector, RegionalDeliveryCalculator, StockReservationService) | 2-3 days |
| UI component integration (product card, cart) | 1-2 days |
| Testing, deployment, and manager training | 1-2 days |
Total estimated time: 6 to 7 business days, excluding inter-warehouse transfers. Exact timeline is determined after an audit of your current architecture.
What Is Included in the Result
- Documentation of data schema and API.
- Working backend classes (WarehouseSelector, RegionalDeliveryCalculator, StockReservationService).
- UI components for product card and cart.
- Administrator guide for managing warehouses and priorities.
- Manager training on reservation and transfers.
- 30 days of post-deployment support.
Comparison with Centralized Warehouse
| Feature | Single Warehouse | Regional Warehouses (Our Solution) |
|---|---|---|
| Delivery speed | High only in warehouse city | Minimal for each region |
| Delivery cost | Fixed from central location | Adaptive, often 15-20% lower |
| Stock display on site | Only total | Per warehouse with details |
| Reservation error risk | Low | Practically eliminated |
Order implementation—get a preliminary estimate in 1 day. Our team has 8+ years of e-commerce development experience and has delivered over 50 projects with multi-warehouse logistics. Certified Laravel and React engineers use proven patterns like Repository and BFF to ensure fault tolerance and ease of maintenance. Get a free engineer consultation.







