Разработка единой панели управления заказами со всех маркетплейсов
Единая панель собирает заказы с сайта и всех подключённых маркетплейсов в один интерфейс. Менеджер работает в одном окне: видит все новые заказы, меняет статусы, печатает этикетки, не переключаясь между кабинетами площадок.
Функциональность панели
Список заказов:
- Фильтрация по источнику (сайт, Ozon, WB, Яндекс.Маркет)
- Фильтрация по статусу, дате, сумме
- Поиск по номеру заказа, имени клиента, SKU
- Индикатор срочности (FBS-заказы с коротким сроком сборки)
- Массовые действия: подтвердить несколько заказов
Карточка заказа:
- Полные данные клиента и доставки
- Список товаров с фото
- Кнопки действий в зависимости от статуса
- Печать этикетки / акта передачи
- История изменений статуса
Архитектура данных
// Периодически подтягиваем заказы со всех маркетплейсов
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()]);
}
}
}
}
Компонент списка заказов
function OrdersDashboard() {
const [filters, setFilters] = useState({ source: 'all', status: 'all', search: '' });
const { data, isLoading } = useQuery({
queryKey: ['orders', filters],
queryFn: () => fetchOrders(filters),
refetchInterval: 60_000, // обновление каждую минуту
});
return (
<div>
<OrderFilters filters={filters} onChange={setFilters} />
{/* Счётчики по источникам */}
<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: 'Сайт', ozon: 'Ozon', wb: 'WB', ym: 'Яндекс.Маркет' };
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>
);
}
Печать этикеток
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']);
}
// Для сайта генерируем сами
$pdf = PDF::loadView('labels.order', compact('order'));
return $pdf->stream("order-{$order->number}.pdf");
}
Уведомления о новых заказах
Real-time уведомления через WebSocket (Laravel Echo / Pusher) — при появлении нового заказа от любого маркетплейса панель обновляется автоматически и показывает toast-уведомление.
Сроки
Панель управления заказами для 3 маркетплейсов с синхронизацией и печатью этикеток: 20–28 рабочих дней.







