Omnichannel Sales Implementation on Website
Omnichannel is not just sales on multiple platforms. It is a unified customer experience: customers see consistent prices, order history is available across all channels, and returns made on marketplaces can be processed through the website. Technically, this is a complex integration of multiple systems with a single source of truth.
Omnichannel System Components
┌─────────────────────────────┐
│ Unified Commerce Core │
│ (inventory, customers, orders)│
└──────────────┬──────────────┘
│
┌──────────────────────┼──────────────────────┐
│ │ │
┌──────▼──────┐ ┌────────▼───────┐ ┌────────▼────────┐
│ Website/App│ │ Marketplaces │ │ Offline / POS │
│ │ │ Ozon WB YM │ │ (if any) │
└─────────────┘ └────────────────┘ └─────────────────┘
Unified Customer Profile
class CustomerIdentityResolver
{
// Merge customers from different channels
public function resolve(array $customerData, string $source): Customer
{
// Search by email → phone → name + address
$customer = null;
if (!empty($customerData['email'])) {
$customer = Customer::where('email', $customerData['email'])->first();
}
if (!$customer && !empty($customerData['phone'])) {
$normalized = $this->normalizePhone($customerData['phone']);
$customer = Customer::where('phone_normalized', $normalized)->first();
}
if (!$customer) {
$customer = Customer::create([
'name' => $customerData['name'],
'email' => $customerData['email'] ?? null,
'phone_normalized' => $normalized ?? null,
'source_first' => $source,
]);
}
// Update channel identifiers
$customer->channelIds()->updateOrCreate(
['channel' => $source],
['external_id' => $customerData['id'] ?? null]
);
return $customer;
}
}
Unified Order History
// Customers see all their orders — from website and marketplaces — in their account
public function getOrderHistory(Customer $customer): Collection
{
return Order::where('customer_id', $customer->id)
->with(['items.product', 'source_details'])
->orderByDesc('created_at')
->get()
->map(fn($order) => [
'id' => $order->id,
'source' => $order->source,
'source_label' => $this->sourceLabel($order->source),
'status' => $order->status,
'total' => $order->total,
'items' => $order->items->count(),
'date' => $order->created_at->format('d.m.Y'),
]);
}
Unified Inventory with Channel Reservation
class InventoryManager
{
// Channel reserves allow inventory distribution management
private array $channelReservePercent = [
'site' => 40, // 40% reserved for website only
'ozon' => 30,
'wb' => 20,
'buffer' => 10, // safety buffer
];
public function getChannelAllocation(int $productId): array
{
$total = WarehouseItem::where('product_id', $productId)->sum('quantity');
return array_map(
fn($pct) => (int)floor($total * $pct / 100),
$this->channelReservePercent
);
}
}
Unified Promotion Management
class OmnichannelPromotion
{
public function apply(string $promoCode, Order $order): void
{
$promo = Promotion::where('code', $promoCode)->first();
// Check applicability to order channel
if (!in_array($order->source, $promo->applicable_channels)) {
throw new PromoNotApplicableException("Promo code is not valid for {$order->source}");
}
// Apply discount
$discount = $promo->calculateDiscount($order->total);
$order->applyDiscount($discount, $promoCode);
// Decrease available uses
$promo->increment('used_count');
}
}
Channel Analytics
SELECT
source,
COUNT(*) AS orders_count,
SUM(total) AS revenue,
AVG(total) AS aov,
COUNT(DISTINCT customer_id) AS unique_customers
FROM orders
WHERE created_at > NOW() - INTERVAL '30 days'
GROUP BY source
ORDER BY revenue DESC;
Timeline
Basic Omnichannel system (3 channels, unified inventory, unified order history): 20–30 business days.







