FBO API
FBO API
API для работы с логистикой “Выполнение OZON” (Fulfillment by OZON) в OZON Seller API.
Количество методов: 13 методов
Обзор
FBO API предоставляет возможности для работы с логистической схемой FBO:
- 📋 Управление заявками на поставку и отправлениями
- 🏭 Мониторинг загруженности складов OZON
- 📅 Управление временными слотами поставок
- 🚚 Регистрация водителей и транспорта
- 📊 Аналитика по статусам заказов
- ❌ Причины отмены отправлений
Основные возможности
🎯 Схема работы FBO
- Поставка товаров → на склады OZON
- Хранение → OZON управляет остатками
- Обработка заказов → OZON упаковывает и отправляет
- Доставка → через сеть OZON
- Возвраты → OZON обрабатывает возвраты
📦 Преимущества FBO
- Быстрая доставка через сеть OZON
- Автоматическая обработка заказов
- Управление возвратами
- Масштабируемость операций
🏭 Управление складами
- Мониторинг загруженности складов
- Планирование поставок по доступности
- Оптимизация размещения товаров
- Контроль таймслотов поставки
Методы API
Справочная информация
getCancelReasons()
Назначение: Получить список причин отмены отправлений FBO
interface FboCancelReasonListRequest {
// Пустой интерфейс, дополнительные параметры не требуются
}
getWarehouseAvailability()
Назначение: Получить информацию о загруженности складов OZON
Управление заявками на поставку
getSupplyOrdersList()
Назначение: Получить список заявок на поставку
interface FboSupplyOrderListRequest {
since: string;
to: string;
filter?: {
status?: string[];
warehouse_id?: number[];
};
limit?: number;
offset?: number;
}
getSupplyOrder()
Назначение: Получить детальную информацию о заявке на поставку
getSupplyOrderBundle()
Назначение: Получить состав заявки на поставку
getSupplyOrderStatusCounter()
Назначение: Получить количество заявок по статусам
Управление временными слотами
getSupplyOrderTimeslots()
Назначение: Получить доступные интервалы поставки
interface FboSupplyOrderTimeslotGetRequest {
warehouse_id: number;
date_from: string;
date_to: string;
}
getSupplyOrderTimeslotStatus()
Назначение: Получить статус интервала поставки
updateSupplyOrderTimeslot()
Назначение: Обновить интервал поставки
Управление транспортом
createSupplyOrderPass()
Назначение: Указать данные о водителе и автомобиле
interface FboSupplyOrderPassCreateRequest {
supply_order_id: number;
driver: {
name: string;
phone: string;
passport: string;
};
vehicle: {
model: string;
license_plate: string;
color: string;
};
}
getSupplyOrderPassStatus()
Назначение: Получить статус ввода данных о водителе и автомобиле
Управление отправлениями
getPostingsList()
Назначение: Получить список отправлений FBO
interface FboPostingListRequest {
since: string;
to: string;
filter?: {
status?: string[];
warehouse_id?: number[];
};
with?: {
analytics_data?: boolean;
financial_data?: boolean;
};
limit?: number;
}
getPosting()
Назначение: Получить детальную информацию об отправлении FBO
Практические примеры
Базовое использование
import { OzonSellerAPI } from 'bmad-ozon-seller-api';
const api = new OzonSellerAPI({
clientId: 'your-client-id',
apiKey: 'your-api-key'
});
// Проверить доступность складов
const warehouses = await api.fbo.getWarehouseAvailability();
console.log('Доступные склады:', warehouses.warehouses?.length);
// Получить список заявок на поставку
const supplyOrders = await api.fbo.getSupplyOrdersList({
since: '2024-01-01T00:00:00Z',
to: '2024-01-31T23:59:59Z',
filter: {
status: ['created', 'confirmed']
},
limit: 100
});
// Получить доступные временные слоты
const timeslots = await api.fbo.getSupplyOrderTimeslots({
warehouse_id: 123,
date_from: '2024-01-15T00:00:00Z',
date_to: '2024-01-20T23:59:59Z'
});
// Зарегистрировать водителя и транспорт
const passResult = await api.fbo.createSupplyOrderPass({
supply_order_id: 123456,
driver: {
name: 'Иванов Иван Иванович',
phone: '+7 (999) 123-45-67',
passport: '1234 567890'
},
vehicle: {
model: 'ГАЗель NEXT',
license_plate: 'А123БВ777',
color: 'белый'
}
});
// Получить список отправлений
const postings = await api.fbo.getPostingsList({
since: '2024-01-01T00:00:00Z',
to: '2024-01-31T23:59:59Z',
filter: {
status: ['shipped', 'delivered']
},
with: {
analytics_data: true,
financial_data: true
},
limit: 50
});
Продвинутые сценарии
Менеджер поставок FBO
class FboSupplyManager {
constructor(private api: OzonSellerAPI) {}
async planOptimalSupplies(): Promise<void> {
console.log('📊 Планирование оптимальных поставок FBO');
// 1. Анализ загруженности складов
const warehouseData = await this.analyzeWarehouseCapacity();
// 2. Получение статистики текущих заявок
const orderStats = await this.analyzeCurrentOrders();
// 3. Планирование новых поставок
const recommendations = await this.generateSupplyRecommendations(warehouseData, orderStats);
console.log('\n💡 Рекомендации по планированию поставок:');
recommendations.forEach((rec, index) => {
console.log(`${index + 1}. ${rec.warehouse}: ${rec.recommendation}`);
if (rec.optimal_slots?.length > 0) {
console.log(` Оптимальные слоты: ${rec.optimal_slots.join(', ')}`);
}
});
}
private async analyzeWarehouseCapacity(): Promise<any[]> {
const warehouses = await this.api.fbo.getWarehouseAvailability();
const warehouseAnalysis = [];
for (const warehouse of warehouses.warehouses || []) {
const utilizationLevel = this.getUtilizationLevel(warehouse.capacity_utilization || 0);
// Получаем доступные слоты для планирования
const timeslots = await this.api.fbo.getSupplyOrderTimeslots({
warehouse_id: warehouse.warehouse_id,
date_from: new Date().toISOString(),
date_to: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000).toISOString()
});
const availableSlots = timeslots.timeslots?.filter(slot => slot.is_available) || [];
warehouseAnalysis.push({
warehouse_id: warehouse.warehouse_id,
name: warehouse.name,
utilization: warehouse.capacity_utilization,
utilization_level: utilizationLevel,
available_slots: availableSlots.length,
total_capacity: availableSlots.reduce((sum, slot) => sum + (slot.max_pallets || 0), 0),
next_available: availableSlots[0]?.start_time,
region: warehouse.region
});
}
console.log('\n🏭 Анализ складов:');
warehouseAnalysis.forEach(analysis => {
console.log(`${analysis.name} (${analysis.region}):`);
console.log(` Загруженность: ${analysis.utilization}% (${analysis.utilization_level})`);
console.log(` Доступно слотов: ${analysis.available_slots}`);
console.log(` Общая вместимость: ${analysis.total_capacity} палет`);
console.log(` Ближайший слот: ${analysis.next_available || 'Нет доступных'}`);
});
return warehouseAnalysis;
}
private async analyzeCurrentOrders(): Promise<any> {
// Получаем статистику по статусам заявок
const statusCounters = await this.api.fbo.getSupplyOrderStatusCounter();
// Получаем детали активных заявок
const activeOrders = await this.api.fbo.getSupplyOrdersList({
since: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString(),
to: new Date().toISOString(),
filter: {
status: ['created', 'confirmed', 'in_progress']
},
limit: 1000
});
console.log('\n📈 Статистика текущих заявок:');
statusCounters.counters?.forEach(counter => {
console.log(`${counter.status}: ${counter.count} заявок`);
});
console.log(`Всего активных: ${activeOrders.total || 0}`);
// Анализ по складам
const warehouseStats = new Map();
activeOrders.supply_orders?.forEach(order => {
const warehouseId = order.warehouse_id;
if (!warehouseStats.has(warehouseId)) {
warehouseStats.set(warehouseId, {
orders_count: 0,
total_volume: 0,
avg_lead_time: 0
});
}
const stats = warehouseStats.get(warehouseId);
stats.orders_count++;
stats.total_volume += order.total_products || 0;
});
return {
status_counters: statusCounters.counters,
active_orders_total: activeOrders.total,
warehouse_stats: warehouseStats
};
}
private async generateSupplyRecommendations(warehouseData: any[], orderStats: any): Promise<any[]> {
const recommendations = [];
for (const warehouse of warehouseData) {
let recommendation = '';
let priority = 'normal';
let optimal_slots = [];
// Анализ загруженности
if (warehouse.utilization < 60) {
recommendation = 'Увеличить объем поставок - низкая загруженность';
priority = 'high';
} else if (warehouse.utilization > 85) {
recommendation = 'Сократить поставки или найти альтернативный склад';
priority = 'urgent';
} else {
recommendation = 'Поддерживать текущий уровень поставок';
}
// Поиск оптимальных слотов
if (warehouse.available_slots > 0) {
try {
const detailedSlots = await this.api.fbo.getSupplyOrderTimeslots({
warehouse_id: warehouse.warehouse_id,
date_from: new Date().toISOString(),
date_to: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString()
});
optimal_slots = detailedSlots.timeslots
?.filter(slot => slot.is_available && (slot.max_pallets || 0) > 10)
?.slice(0, 3)
?.map(slot => new Date(slot.start_time).toLocaleDateString()) || [];
} catch (error) {
console.log(`Ошибка получения слотов для склада ${warehouse.name}`);
}
}
recommendations.push({
warehouse: warehouse.name,
warehouse_id: warehouse.warehouse_id,
recommendation,
priority,
utilization: warehouse.utilization,
optimal_slots
});
}
return recommendations.sort((a, b) => {
const priorityOrder = { urgent: 3, high: 2, normal: 1 };
return (priorityOrder[b.priority] || 0) - (priorityOrder[a.priority] || 0);
});
}
private getUtilizationLevel(utilization: number): string {
if (utilization < 60) return 'Низкая';
if (utilization < 75) return 'Средняя';
if (utilization < 85) return 'Высокая';
return 'Критическая';
}
}
Система мониторинга отправлений FBO
class FboMonitoringSystem {
constructor(private api: OzonSellerAPI) {}
async monitorFboOperations(): Promise<void> {
console.log('🔍 Мониторинг операций FBO');
console.log('='.repeat(50));
// Мониторинг отправлений
await this.monitorPostings();
// Мониторинг заявок на поставку
await this.monitorSupplyOrders();
// Анализ производительности
await this.analyzePerformance();
// Проверка транспортных данных
await this.checkTransportData();
}
private async monitorPostings(): Promise<void> {
console.log('\n📦 Мониторинг отправлений FBO:');
const oneWeekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
const now = new Date().toISOString();
const postings = await this.api.fbo.getPostingsList({
since: oneWeekAgo,
to: now,
with: {
analytics_data: true,
financial_data: true
},
limit: 1000
});
// Группировка по статусам
const statusGroups = new Map();
let totalRevenue = 0;
let totalOrders = postings.total || 0;
postings.postings?.forEach(posting => {
const status = posting.status || 'unknown';
if (!statusGroups.has(status)) {
statusGroups.set(status, 0);
}
statusGroups.set(status, statusGroups.get(status) + 1);
if (posting.financial_data?.payout_amount) {
totalRevenue += posting.financial_data.payout_amount;
}
});
console.log(`Всего отправлений за неделю: ${totalOrders}`);
console.log(`Общая выручка: ${totalRevenue.toFixed(2)} руб.`);
console.log('\nРаспределение по статусам:');
Array.from(statusGroups.entries()).forEach(([status, count]) => {
const percentage = ((count / totalOrders) * 100).toFixed(1);
console.log(` ${status}: ${count} (${percentage}%)`);
});
// Анализ проблемных отправлений
const problemStatuses = ['cancelled', 'cancelled_by_ozon', 'returned'];
const problemPostings = postings.postings?.filter(p =>
problemStatuses.includes(p.status || '')
) || [];
if (problemPostings.length > 0) {
console.log(`\n⚠️ Проблемные отправления: ${problemPostings.length}`);
// Получаем причины отмен
const cancelReasons = await this.api.fbo.getCancelReasons();
console.log('Доступные причины отмен:', cancelReasons.cancel_reasons?.length);
}
}
private async monitorSupplyOrders(): Promise<void> {
console.log('\n🚛 Мониторинг заявок на поставку:');
const twoWeeksAgo = new Date(Date.now() - 14 * 24 * 60 * 60 * 1000).toISOString();
const now = new Date().toISOString();
// Получаем статистику по статусам
const statusCounter = await this.api.fbo.getSupplyOrderStatusCounter();
console.log('Статистика по статусам:');
statusCounter.counters?.forEach(counter => {
console.log(` ${counter.status}: ${counter.count} заявок`);
});
// Получаем детали активных заявок
const supplyOrders = await this.api.fbo.getSupplyOrdersList({
since: twoWeeksAgo,
to: now,
limit: 500
});
// Анализ по типам заявок
const warehouseStats = new Map();
let overdueOrders = 0;
const currentTime = new Date();
supplyOrders.supply_orders?.forEach(order => {
const warehouseId = order.warehouse_id || 'unknown';
if (!warehouseStats.has(warehouseId)) {
warehouseStats.set(warehouseId, {
orders: 0,
products: 0,
statuses: new Set()
});
}
const stats = warehouseStats.get(warehouseId);
stats.orders++;
stats.products += order.total_products || 0;
stats.statuses.add(order.status);
// Проверка просроченных заявок
if (order.planned_delivery_date && new Date(order.planned_delivery_date) < currentTime) {
if (['created', 'confirmed'].includes(order.status || '')) {
overdueOrders++;
}
}
});
console.log(`\nВсего заявок: ${supplyOrders.total}`);
console.log(`Просроченных заявок: ${overdueOrders}`);
console.log('\nСтатистика по складам:');
Array.from(warehouseStats.entries()).forEach(([warehouseId, stats]) => {
console.log(` Склад ${warehouseId}: ${stats.orders} заявок, ${stats.products} товаров`);
console.log(` Статусы: ${Array.from(stats.statuses).join(', ')}`);
});
}
private async analyzePerformance(): Promise<void> {
console.log('\n📊 Анализ производительности:');
try {
// Анализ доступности складов
const warehouses = await this.api.fbo.getWarehouseAvailability();
const totalWarehouses = warehouses.warehouses?.length || 0;
const highUtilization = warehouses.warehouses?.filter(w =>
(w.capacity_utilization || 0) > 80
).length || 0;
console.log(`Всего складов: ${totalWarehouses}`);
console.log(`С высокой загрузкой (>80%): ${highUtilization}`);
if (highUtilization > 0) {
console.log('⚠️ Рекомендуется перераспределить нагрузку между складами');
}
// Средняя загрузка складов
const avgUtilization = warehouses.warehouses?.reduce((sum, w) =>
sum + (w.capacity_utilization || 0), 0) / totalWarehouses;
console.log(`Средняя загрузка складов: ${avgUtilization?.toFixed(1)}%`);
} catch (error) {
console.log('Ошибка анализа производительности:', error.message);
}
}
private async checkTransportData(): Promise<void> {
console.log('\n🚚 Проверка транспортных данных:');
// Получаем недавние заявки для проверки
const recentOrders = await this.api.fbo.getSupplyOrdersList({
since: new Date(Date.now() - 3 * 24 * 60 * 60 * 1000).toISOString(),
to: new Date().toISOString(),
filter: {
status: ['confirmed', 'in_progress']
},
limit: 50
});
let ordersWithoutTransport = 0;
let ordersWithPendingTransport = 0;
for (const order of recentOrders.supply_orders || []) {
try {
const passStatus = await this.api.fbo.getSupplyOrderPassStatus({
supply_order_id: order.supply_order_id!
});
if (passStatus.status === 'not_provided') {
ordersWithoutTransport++;
} else if (passStatus.status === 'pending') {
ordersWithPendingTransport++;
}
} catch (error) {
// Игнорируем ошибки для заявок, где данные о транспорте не требуются
}
}
console.log(`Заявок без данных о транспорте: ${ordersWithoutTransport}`);
console.log(`Заявок с ожидающими данными: ${ordersWithPendingTransport}`);
if (ordersWithoutTransport > 0) {
console.log('⚠️ Требуется заполнить данные о водителях и транспорте');
}
}
async generateDailyReport(): Promise<void> {
console.log('📈 Ежедневный отчет FBO');
console.log('='.repeat(40));
console.log(`Дата: ${new Date().toLocaleDateString()}`);
const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
const now = new Date().toISOString();
// Отправления за сутки
const dailyPostings = await this.api.fbo.getPostingsList({
since: yesterday,
to: now,
with: { financial_data: true },
limit: 1000
});
const dailyRevenue = dailyPostings.postings?.reduce((sum, posting) => {
return sum + (posting.financial_data?.payout_amount || 0);
}, 0) || 0;
console.log(`\n📦 Отправления за сутки: ${dailyPostings.total}`);
console.log(`💰 Выручка за сутки: ${dailyRevenue.toFixed(2)} руб.`);
if (dailyPostings.total && dailyPostings.total > 0) {
const avgOrderValue = dailyRevenue / dailyPostings.total;
console.log(`📊 Средний чек: ${avgOrderValue.toFixed(2)} руб.`);
}
// Рекомендации
console.log('\n💡 Рекомендации:');
if (dailyRevenue > 50000) {
console.log('✅ Отличные результаты! Рассмотрите расширение ассортимента');
} else if (dailyRevenue > 20000) {
console.log('📈 Хорошие результаты. Продолжайте в том же направлении');
} else {
console.log('📉 Результаты ниже среднего. Проанализируйте причины');
}
}
}
Автоматизация регистрации транспорта
class TransportRegistrationService {
constructor(private api: OzonSellerAPI) {}
async autoRegisterTransport(
supplyOrderId: number,
transportData: {
driver: { name: string; phone: string; passport: string };
vehicle: { model: string; license_plate: string; color: string };
}
): Promise<boolean> {
console.log(`🚛 Регистрация транспорта для заявки ${supplyOrderId}`);
try {
// Проверяем текущий статус
const currentStatus = await this.api.fbo.getSupplyOrderPassStatus({
supply_order_id: supplyOrderId
});
if (currentStatus.status === 'approved') {
console.log('✅ Транспорт уже зарегистрирован и одобрен');
return true;
}
// Регистрируем транспорт
const registrationResult = await this.api.fbo.createSupplyOrderPass({
supply_order_id: supplyOrderId,
driver: transportData.driver,
vehicle: transportData.vehicle
});
console.log(`📋 Заявка на регистрацию создана: ${registrationResult.task_id}`);
// Ожидаем обработки
const approved = await this.waitForApproval(supplyOrderId, 60000); // 1 минута
if (approved) {
console.log('✅ Транспорт успешно зарегистрирован');
return true;
} else {
console.log('⏳ Регистрация все еще обрабатывается');
return false;
}
} catch (error) {
console.error(`❌ Ошибка регистрации транспорта: ${error.message}`);
return false;
}
}
private async waitForApproval(supplyOrderId: number, maxWaitTime: number): Promise<boolean> {
const startTime = Date.now();
const checkInterval = 5000; // 5 секунд
while (Date.now() - startTime < maxWaitTime) {
try {
const status = await this.api.fbo.getSupplyOrderPassStatus({
supply_order_id: supplyOrderId
});
if (status.status === 'approved') {
return true;
} else if (status.status === 'rejected') {
console.log(`❌ Регистрация отклонена: ${status.rejection_reason}`);
return false;
}
console.log(`⏳ Статус: ${status.status}, ожидаем...`);
await new Promise(resolve => setTimeout(resolve, checkInterval));
} catch (error) {
console.log('Ошибка проверки статуса, повторяем...');
await new Promise(resolve => setTimeout(resolve, checkInterval));
}
}
return false;
}
async batchRegisterTransport(
registrations: Array<{
supply_order_id: number;
driver: { name: string; phone: string; passport: string };
vehicle: { model: string; license_plate: string; color: string };
}>
): Promise<void> {
console.log(`🚛 Массовая регистрация транспорта для ${registrations.length} заявок`);
const results = [];
for (const registration of registrations) {
const success = await this.autoRegisterTransport(
registration.supply_order_id,
{
driver: registration.driver,
vehicle: registration.vehicle
}
);
results.push({
supply_order_id: registration.supply_order_id,
success
});
// Пауза между запросами
await new Promise(resolve => setTimeout(resolve, 1000));
}
// Отчет о результатах
const successful = results.filter(r => r.success).length;
const failed = results.length - successful;
console.log(`\n📊 Результаты массовой регистрации:`);
console.log(`✅ Успешно: ${successful}`);
console.log(`❌ Неудачно: ${failed}`);
if (failed > 0) {
console.log('\n❌ Неудачные регистрации:');
results.filter(r => !r.success).forEach(r => {
console.log(` Заявка ${r.supply_order_id}: требует ручной проверки`);
});
}
}
}
Обработка ошибок
try {
await api.fbo.getPostingsList({
since: '2024-01-01T00:00:00Z',
to: '2024-01-31T23:59:59Z'
});
} catch (error) {
if (error.response?.status === 400) {
console.error('Ошибка валидации параметров:', error.response.data);
} else if (error.response?.status === 404) {
console.error('Заявка или отправление не найдено');
} else if (error.response?.status === 403) {
console.error('Нет доступа к FBO операциям');
} else {
console.error('Неожиданная ошибка:', error.message);
}
}
Рекомендации по использованию
🎯 Планирование поставок
- Регулярно мониторьте загруженность складов
- Планируйте поставки с учетом доступных временных слотов
- Используйте аналитику для оптимизации размещения товаров
📦 Управление отправлениями
- Отслеживайте статусы отправлений в режиме реального времени
- Анализируйте причины отмен для улучшения качества
- Используйте финансовые данные для расчета рентабельности
🚚 Работа с транспортом
- Заранее регистрируйте данные водителей и транспорта
- Подготавливайте резервные варианты транспорта
- Мониторьте статусы одобрения транспортных данных
🔍 Мониторинг и аналитика
- Настройте автоматические отчеты по ключевым метрикам
- Используйте исторические данные для прогнозирования
- Анализируйте производительность по складам и регионам
🚀 Автоматизация
- Автоматизируйте регистрацию транспорта для регулярных поставок
- Настройте уведомления о критических событиях
- Интегрируйте с системами планирования ресурсов
FBO API обеспечивает полный контроль над логистическими операциями схемы “Выполнение OZON”, от планирования поставок до мониторинга доставки заказов.