Managers spend up to 2 hours a day switching between Ozon, Wildberries, and Yandex.Market dashboards. According to our data, manual synchronization leads to 15–20% order loss and up to 80% of negative reviews due to delays. Automation reduces these risks to zero, and the average order processing time drops from 5 minutes to 30 seconds, saving operators up to 50,000 rubles per month. A unified order dashboard solves this: it collects all orders from the site and marketplaces into one interface. The manager works in a single window — sees new orders, changes statuses, prints labels without switching between platforms. Payback for such a solution is on average 2–3 months due to reduced sales department labor costs. We have 12+ years of experience in web development and over 50 successful marketplace integrations.
Why a Unified Order Management Dashboard?
Manual order export — operators copy data from five dashboards, make errors in numbers, lose orders. FBS orders with short assembly deadlines (4 hours on Ozon) go unnoticed. Duplicates and conflicts — the same order may be created twice if the marketplace API returns data again. Our solution uses the Repository pattern for deduplication and Adapter to unify data from different sources. This ensures processing of up to 1000 orders per minute without loss. Compare: manual handling requires 2–3 operators, the automated dashboard handles it alone — 10 times faster. A single order window combining Ozon, Wildberries, Yandex.Market, and the site gives full control over all processes.
How We Sync Orders Without Loss?
Synchronization is based on the Laravel Queue job system. Each marketplace is polled at a set frequency (typically every 5–15 minutes). All new and changed orders are mapped to a common format and saved locally. For reliability, we log every run and automatically retry on failures. The concept of queues is described in Laravel Queues.
Here’s what the core synchronization class looks like:
// Periodically pull orders from all marketplaces
class MarketplaceOrdersSyncJob implements ShouldQueue
{
public function handle(): void
{
$adapters = [
'ozon' => app(OzonAdapter::class),
'wb' => app(WildberriesAdapter::class),
'ym' => app(YandexMarketAdapter::class),
];
foreach ($adapters as $source => $adapter) {
try {
$lastSync = SyncLog::where('source', $source)->max('synced_at')
?? now()->subHours(24);
$orders = $adapter->getOrdersSince($lastSync);
foreach ($orders as $rawOrder) {
$unified = $adapter->toUnifiedOrder($rawOrder);
Order::updateOrCreate(
['source' => $source, 'source_order_id' => $unified->sourceOrderId],
$unified->toArray()
);
}
SyncLog::create(['source' => $source, 'synced_at' => now(), 'count' => count($orders)]);
} catch (Exception $e) {
Log::error("Sync failed for {$source}", ['error' => $e->getMessage()]);
}
}
}
}
Order Deduplication Eliminates Duplicates — Unified Dashboard
Without deduplication, a repeated API request (due to network failure) creates a duplicate order. We use a unique source_order_id for each order within a marketplace, so updateOrCreate simply updates the existing record. This prevents duplicates even on repeated calls. The Laravel React panel built on this principle reliably handles thousands of orders daily.
Dashboard Functionality
Order List
- Filtering by source (site, Ozon, WB, Yandex.Market)
- Filtering by status, date, amount
- Search by order number, customer name, SKU
- Urgency indicator (FBS orders with short assembly time — red background)
- Bulk actions: confirm multiple orders, print labels
function OrdersDashboard() {
const [filters, setFilters] = useState({ source: 'all', status: 'all', search: '' });
const { data, isLoading } = useQuery({
queryKey: ['orders', filters],
queryFn: () => fetchOrders(filters),
refetchInterval: 60_000, // refresh every minute
});
return (
<div>
<OrderFilters filters={filters} onChange={setFilters} />
{/* Counters by source */}
<div className="grid grid-cols-5 gap-3 mb-6">
{['site', 'ozon', 'wb', 'ym'].map(source => (
<SourceCounter key={source} source={source} count={data?.counts[source] ?? 0} />
))}
</div>
<OrdersTable
orders={data?.orders ?? []}
loading={isLoading}
onStatusChange={handleStatusChange}
/>
</div>
);
}
function SourceCounter({ source, count }: { source: string; count: number }) {
const labels = { site: 'Site', ozon: 'Ozon', wb: 'WB', ym: 'Yandex.Market' };
return (
<div className={cn('rounded-xl p-4 border', sourceColors[source])}>
<p className="text-2xl font-bold">{count}</p>
<p className="text-sm text-gray-600">{labels[source]}</p>
</div>
);
}
Order Card
- Full customer and delivery data
- Product list with photos
- Action buttons depending on status (confirm, cancel, send to assembly)
- Print label / transfer act
- Status change history with timestamps
Label Printing
Each marketplace requires its own label format. We automatically select a template based on the source:
public function printLabel(Order $order): Response
{
if ($order->source === 'ozon') {
$label = $this->ozon->getPostingLabel($order->source_order_id);
return response($label, 200, ['Content-Type' => 'application/pdf']);
}
if ($order->source === 'wb') {
$label = $this->wb->getLabel($order->source_order_id);
return response($label, 200, ['Content-Type' => 'application/pdf']);
}
// For site, generate ourselves
$pdf = PDF::loadView('labels.order', compact('order'));
return $pdf->stream("order-{$order->number}.pdf");
}
For bulk printing, you can select multiple orders and click "Print Labels" — the system generates a single PDF with all labels.
New Order Notifications
Real-time notifications via WebSocket (Laravel Echo / Pusher) — when a new order arrives from any marketplace, the dashboard updates automatically and shows a toast notification. The operator can immediately start working on the order without refreshing the page.
Process Overview
- Analysis — we study your marketplace APIs, document fields for synchronization.
- Design — design database structure, queue system, notification scheme.
- Implementation — write adapters, interface, sync logic.
- Testing — test with sample orders, simulate network failures, data conflicts.
- Deployment — deploy on your hosting or our server, set up monitoring.
| Stage | Duration | Result |
|---|---|---|
| Analysis | 1-2 days | Integration specification document |
| Design | 2-3 days | Database schema, API specification |
| Implementation | 10-14 days | Ready panel with test data |
| Testing | 3-4 days | Testing protocol, bug fixes |
| Deployment | 2 days | Working panel, operator manual |
Comparison of Order Management Approaches
| Criteria | Manual Management | Automated Dashboard |
|---|---|---|
| Order processing time | 5 minutes | 30 seconds |
| Number of operators | 2-3 | 1 |
| Order loss risk | 15-20% | 0% |
| Error frequency | High | Minimal |
| Cost savings | 0 rubles/month | up to 50,000 rubles/month |
Timeline and What’s Included
Dashboard for 3 marketplaces with synchronization and label printing: 20–28 working days. We work at a fixed cost, calculated individually. Includes:
- Integration of up to 4 sources (site + 3 marketplaces)
- Web interface with order list, filters, search, and counters
- Label printing for each marketplace
- Notification system (WebSocket + email)
- Administration documentation
- Operator training (1 hour)
- Synchronization warranty — 6 months
Technical integration details
During implementation we configure:
- Laravel queues with Supervisor
- Token refresh mechanism (OAuth2 refresh)
- Logging of all marketplace API requests
- Monitoring via Grafana + Prometheus
- Backup of order database
Typical Integration Mistakes
- Token expiration — most marketplaces issue tokens valid for 24 hours. We automatically refresh them via a refresh mechanism.
- Timezone differences — an order created at 23:59 Moscow time may appear as tomorrow's. We convert everything to UTC.
- Duplicates on retries — if the API fails, the request is repeated, potentially creating duplicates. We use
source_order_idfor deduplication.
Get a consultation for your project — we'll help estimate timeline and cost. Contact us to receive a detailed calculation.







