Delivery-RFBS API - Управление доставкой возвратной логистики FBS
Delivery-RFBS API - Управление доставкой возвратной логистики FBS
Delivery-RFBS API предоставляет инструменты для управления возвратной логистикой FBS (Return Fulfillment by Seller), включая изменение статусов доставки, добавление трек-номеров и управление расписанием доставки.
Обзор API
Количество методов: 8
Основные функции: Управление статусами доставки, трекинг, перенос сроков доставки
Назначение: Возвратная логистика FBS заказов
Статусы доставки RFBS
Отправлено продавцом → Последняя миля → Доставляется → Доставлено
(sent_by_seller) → (last_mile) → (delivering) → (delivered)
Методы API
1. Установка даты отгрузки
Метод: setCutoff()
Эндпоинт: POST /v1/posting/cutoff/set
Уточняет дату отгрузки отправления для планирования логистики.
Параметры запроса
interface DeliveryRfbsSetCutoffRequest {
posting_number: string; // Номер отправления
cutoff_at: string; // Дата отгрузки в ISO 8601
}
Пример использования
import { OzonSellerApiClient } from '@spacechemical/ozon-seller-api';
const client = new OzonSellerApiClient({
apiKey: 'your-api-key',
clientId: 'your-client-id'
});
// Установить дату отгрузки
const result = await client.deliveryRfbs.setCutoff({
posting_number: 'FBS-123456789',
cutoff_at: '2024-01-15T10:00:00Z'
});
if (result.result) {
console.log('✅ Дата отгрузки установлена');
}
2. Получение ограничений для переноса доставки
Метод: getTimeslotChangeRestrictions()
Эндпоинт: POST /v1/posting/fbs/timeslot/change-restrictions
Возвращает доступные даты для переноса доставки и количество оставшихся переносов.
Пример использования
// Получить ограничения для переноса
const restrictions = await client.deliveryRfbs.getTimeslotChangeRestrictions({
posting_number: 'FBS-123456789'
});
if (restrictions.restrictions) {
console.log('Доступные даты для переноса:');
restrictions.restrictions.available_dates?.forEach(date => {
console.log(`- ${date}`);
});
console.log(`Осталось переносов: ${restrictions.restrictions.available_reschedules}`);
console.log(`Можно ли перенести: ${restrictions.restrictions.can_change ? 'Да' : 'Нет'}`);
}
3. Перенос даты доставки
Метод: setTimeslot()
Эндпоинт: POST /v1/posting/fbs/timeslot/set
Переносит дату доставки на новую доступную дату.
Пример использования
// Перенести дату доставки
const rescheduleResult = await client.deliveryRfbs.setTimeslot({
posting_number: 'FBS-123456789',
timeslot_date: '2024-01-25'
});
if (rescheduleResult.new_timeslot_date) {
console.log(`✅ Доставка перенесена на: ${rescheduleResult.new_timeslot_date}`);
}
4. Добавление трек-номеров
Метод: setTrackingNumbers()
Эндпоинт: POST /v2/fbs/posting/tracking-number/set
Добавляет трек-номера к отправлениям для отслеживания.
Параметры запроса
interface DeliveryRfbsTrackingNumberSetRequest {
tracking_numbers: Array<{
posting_number: string; // Номер отправления
tracking_number: string; // Трек-номер
delivery_service: string; // Служба доставки
}>;
}
Пример использования
// Добавить трек-номера к нескольким отправлениям
const trackingResult = await client.deliveryRfbs.setTrackingNumbers({
tracking_numbers: [
{
posting_number: 'FBS-123456789',
tracking_number: 'TRACK123456',
delivery_service: 'CDEK'
},
{
posting_number: 'FBS-987654321',
tracking_number: 'TRACK789012',
delivery_service: 'Russian Post'
},
{
posting_number: 'FBS-555666777',
tracking_number: 'TRACK345678',
delivery_service: 'DPD'
}
]
});
// Проверить результаты
trackingResult.results?.forEach(res => {
if (res.result === 'success') {
console.log(`✅ Трек-номер добавлен для ${res.posting_number}`);
} else {
console.error(`❌ Ошибка для ${res.posting_number}: ${res.error}`);
}
});
Методы изменения статусов доставки
5. Статус “Отправлено продавцом”
Метод: setSentBySeller()
Эндпоинт: POST /v2/fbs/posting/sent-by-seller
Устанавливает статус “Отправлено продавцом” - первый статус в цепочке доставки.
Пример использования
// Изменить статус на "Отправлено продавцом"
const sentResult = await client.deliveryRfbs.setSentBySeller({
posting_number: 'FBS-123456789',
sent_by_seller_at: '2024-01-15T12:00:00Z'
});
if (sentResult.posting_number) {
console.log(`✅ Статус изменен на "Отправлено продавцом" для ${sentResult.posting_number}`);
}
6. Статус “Последняя миля”
Метод: setLastMile()
Эндпоинт: POST /v2/fbs/posting/last-mile
Устанавливает статус “Последняя миля” - товар находится в пункте выдачи или у курьера.
Пример использования
// Изменить статус на "Последняя миля"
const lastMileResult = await client.deliveryRfbs.setLastMile({
posting_number: 'FBS-123456789',
last_mile_at: '2024-01-19T14:00:00Z'
});
console.log(`✅ Начата последняя миля для ${lastMileResult.posting_number}`);
7. Статус “Доставляется”
Метод: setDelivering()
Эндпоинт: POST /v2/fbs/posting/delivering
Устанавливает статус “Доставляется” - товар находится в процессе доставки покупателю.
Пример использования
// Изменить статус на "Доставляется"
const deliveringResult = await client.deliveryRfbs.setDelivering({
posting_number: 'FBS-123456789',
delivering_at: '2024-01-20T09:00:00Z'
});
console.log(`✅ Начата доставка для ${deliveringResult.posting_number}`);
8. Статус “Доставлено”
Метод: setDelivered()
Эндпоинт: POST /v2/fbs/posting/delivered
Устанавливает финальный статус “Доставлено” - товар успешно доставлен покупателю.
Пример использования
// Изменить статус на "Доставлено"
const deliveredResult = await client.deliveryRfbs.setDelivered({
posting_number: 'FBS-123456789',
delivered_at: '2024-01-20T15:30:00Z'
});
console.log(`✅ Доставка завершена для ${deliveredResult.posting_number}`);
Практические сценарии использования
1. Полный цикл управления доставкой
class RfbsDeliveryManager {
constructor(private client: OzonSellerApiClient) {}
async manageFullDeliveryFlow(
postingNumber: string,
trackingNumber: string,
deliveryService: string
) {
console.log(`🚀 Начинаем управление доставкой ${postingNumber}`);
try {
// 1. Установить дату отгрузки
await this.client.deliveryRfbs.setCutoff({
posting_number: postingNumber,
cutoff_at: new Date().toISOString()
});
console.log('✅ Дата отгрузки установлена');
// 2. Добавить трек-номер
const trackingResult = await this.client.deliveryRfbs.setTrackingNumbers({
tracking_numbers: [{
posting_number: postingNumber,
tracking_number: trackingNumber,
delivery_service: deliveryService
}]
});
const trackingSuccess = trackingResult.results?.some(r => r.result === 'success');
if (trackingSuccess) {
console.log('✅ Трек-номер добавлен');
}
// 3. Установить статус "Отправлено продавцом"
await this.client.deliveryRfbs.setSentBySeller({
posting_number: postingNumber,
sent_by_seller_at: new Date().toISOString()
});
console.log('✅ Статус: Отправлено продавцом');
// Имитация времени в пути
console.log('⏳ Ожидаем поступления в пункт выдачи...');
// 4. Статус "Последняя миля" (обычно через 1-3 дня)
const lastMileDate = new Date();
lastMileDate.setDate(lastMileDate.getDate() + 2);
setTimeout(async () => {
await this.client.deliveryRfbs.setLastMile({
posting_number: postingNumber,
last_mile_at: lastMileDate.toISOString()
});
console.log('✅ Статус: Последняя миля');
}, 2000);
// 5. Статус "Доставляется" (день доставки)
const deliveringDate = new Date();
deliveringDate.setDate(deliveringDate.getDate() + 3);
setTimeout(async () => {
await this.client.deliveryRfbs.setDelivering({
posting_number: postingNumber,
delivering_at: deliveringDate.toISOString()
});
console.log('✅ Статус: Доставляется');
}, 4000);
// 6. Статус "Доставлено" (после получения покупателем)
const deliveredDate = new Date();
deliveredDate.setDate(deliveredDate.getDate() + 3);
deliveredDate.setHours(deliveredDate.getHours() + 4);
setTimeout(async () => {
await this.client.deliveryRfbs.setDelivered({
posting_number: postingNumber,
delivered_at: deliveredDate.toISOString()
});
console.log('🎉 Доставка завершена успешно!');
}, 6000);
return {
postingNumber,
trackingNumber,
status: 'processing'
};
} catch (error) {
console.error(`❌ Ошибка в управлении доставкой ${postingNumber}:`, error);
throw error;
}
}
// Управление переносом доставки по запросу покупателя
async handleDeliveryReschedule(postingNumber: string, preferredDate: string) {
console.log(`📅 Обработка запроса на перенос доставки ${postingNumber}`);
try {
// Получить доступные даты для переноса
const restrictions = await this.client.deliveryRfbs.getTimeslotChangeRestrictions({
posting_number: postingNumber
});
if (!restrictions.restrictions?.can_change) {
return {
success: false,
reason: 'Перенос недоступен',
remainingReschedules: restrictions.restrictions?.available_reschedules || 0
};
}
// Проверить, доступна ли предпочтительная дата
const availableDates = restrictions.restrictions.available_dates || [];
const isDateAvailable = availableDates.includes(preferredDate);
if (!isDateAvailable) {
return {
success: false,
reason: 'Выбранная дата недоступна',
availableDates,
remainingReschedules: restrictions.restrictions.available_reschedules || 0
};
}
// Выполнить перенос
const rescheduleResult = await this.client.deliveryRfbs.setTimeslot({
posting_number: postingNumber,
timeslot_date: preferredDate
});
return {
success: true,
newDate: rescheduleResult.new_timeslot_date,
remainingReschedules: (restrictions.restrictions.available_reschedules || 1) - 1
};
} catch (error) {
console.error(`❌ Ошибка переноса доставки ${postingNumber}:`, error);
return {
success: false,
reason: 'Техническая ошибка',
error: error.message
};
}
}
}
// Использование
const deliveryManager = new RfbsDeliveryManager(client);
// Полный цикл доставки
const delivery = await deliveryManager.manageFullDeliveryFlow(
'FBS-123456789',
'TRACK123456',
'CDEK'
);
// Перенос доставки
const reschedule = await deliveryManager.handleDeliveryReschedule(
'FBS-123456789',
'2024-01-25'
);
if (reschedule.success) {
console.log(`✅ Доставка перенесена на ${reschedule.newDate}`);
console.log(`Осталось переносов: ${reschedule.remainingReschedules}`);
} else {
console.log(`❌ Не удалось перенести: ${reschedule.reason}`);
}
2. Массовое управление доставками
class BulkDeliveryProcessor {
constructor(private client: OzonSellerApiClient) {}
// Массовое добавление трек-номеров
async addMultipleTrackingNumbers(
trackingData: Array<{
postingNumber: string;
trackingNumber: string;
deliveryService: string;
}>
) {
const BATCH_SIZE = 100; // Максимум за один запрос
const results = [];
const errors = [];
for (let i = 0; i < trackingData.length; i += BATCH_SIZE) {
const batch = trackingData.slice(i, i + BATCH_SIZE);
try {
const batchRequest = {
tracking_numbers: batch.map(item => ({
posting_number: item.postingNumber,
tracking_number: item.trackingNumber,
delivery_service: item.deliveryService
}))
};
const result = await this.client.deliveryRfbs.setTrackingNumbers(batchRequest);
result.results?.forEach(res => {
if (res.result === 'success') {
results.push(res.posting_number);
} else {
errors.push({
postingNumber: res.posting_number,
error: res.error
});
}
});
console.log(`📦 Обработан батч ${Math.floor(i / BATCH_SIZE) + 1}/${Math.ceil(trackingData.length / BATCH_SIZE)}`);
// Пауза между батчами
if (i + BATCH_SIZE < trackingData.length) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
} catch (error) {
console.error(`❌ Ошибка в батче ${Math.floor(i / BATCH_SIZE) + 1}:`, error);
batch.forEach(item => {
errors.push({
postingNumber: item.postingNumber,
error: error.message
});
});
}
}
return {
totalProcessed: trackingData.length,
successCount: results.length,
errorCount: errors.length,
successfulPostings: results,
errors
};
}
// Массовое изменение статусов
async bulkStatusUpdate(
postingNumbers: string[],
status: 'sent_by_seller' | 'last_mile' | 'delivering' | 'delivered',
timestamp?: string
) {
const results = [];
const errors = [];
const updateTime = timestamp || new Date().toISOString();
console.log(`🔄 Массовое изменение статуса на "${status}" для ${postingNumbers.length} отправлений`);
for (const postingNumber of postingNumbers) {
try {
let result;
switch (status) {
case 'sent_by_seller':
result = await this.client.deliveryRfbs.setSentBySeller({
posting_number: postingNumber,
sent_by_seller_at: updateTime
});
break;
case 'last_mile':
result = await this.client.deliveryRfbs.setLastMile({
posting_number: postingNumber,
last_mile_at: updateTime
});
break;
case 'delivering':
result = await this.client.deliveryRfbs.setDelivering({
posting_number: postingNumber,
delivering_at: updateTime
});
break;
case 'delivered':
result = await this.client.deliveryRfbs.setDelivered({
posting_number: postingNumber,
delivered_at: updateTime
});
break;
}
if (result?.posting_number) {
results.push(postingNumber);
console.log(`✅ ${postingNumber}: статус обновлен`);
}
} catch (error) {
errors.push({
postingNumber,
error: error.message
});
console.error(`❌ ${postingNumber}: ${error.message}`);
}
// Пауза между запросами для соблюдения лимитов
await new Promise(resolve => setTimeout(resolve, 200));
}
return {
status,
totalPostings: postingNumbers.length,
successCount: results.length,
errorCount: errors.length,
successfulPostings: results,
errors
};
}
// Автоматическое продвижение статусов по расписанию
async scheduleStatusProgression(
postingNumber: string,
trackingNumber: string,
deliveryService: string,
schedule: {
sentBySellerDelay?: number; // минуты до "Отправлено продавцом"
lastMileDelay?: number; // минуты до "Последняя миля"
deliveringDelay?: number; // минуты до "Доставляется"
deliveredDelay?: number; // минуты до "Доставлено"
} = {}
) {
const defaultSchedule = {
sentBySellerDelay: 0, // сразу
lastMileDelay: 2880, // через 2 дня
deliveringDelay: 4320, // через 3 дня
deliveredDelay: 4440, // через 3 дня 2 часа
...schedule
};
console.log(`⏰ Запланировано автоматическое продвижение для ${postingNumber}`);
// Добавить трек-номер
await this.client.deliveryRfbs.setTrackingNumbers({
tracking_numbers: [{
posting_number: postingNumber,
tracking_number: trackingNumber,
delivery_service: deliveryService
}]
});
// Запланировать статусы
const baseTime = Date.now();
if (defaultSchedule.sentBySellerDelay === 0) {
// Сразу установить "Отправлено продавцом"
await this.client.deliveryRfbs.setSentBySeller({
posting_number: postingNumber,
sent_by_seller_at: new Date().toISOString()
});
console.log(`✅ ${postingNumber}: Отправлено продавцом`);
} else {
setTimeout(async () => {
await this.client.deliveryRfbs.setSentBySeller({
posting_number: postingNumber,
sent_by_seller_at: new Date().toISOString()
});
console.log(`✅ ${postingNumber}: Отправлено продавцом`);
}, defaultSchedule.sentBySellerDelay * 60 * 1000);
}
// Последняя миля
setTimeout(async () => {
await this.client.deliveryRfbs.setLastMile({
posting_number: postingNumber,
last_mile_at: new Date().toISOString()
});
console.log(`✅ ${postingNumber}: Последняя миля`);
}, defaultSchedule.lastMileDelay * 60 * 1000);
// Доставляется
setTimeout(async () => {
await this.client.deliveryRfbs.setDelivering({
posting_number: postingNumber,
delivering_at: new Date().toISOString()
});
console.log(`✅ ${postingNumber}: Доставляется`);
}, defaultSchedule.deliveringDelay * 60 * 1000);
// Доставлено
setTimeout(async () => {
await this.client.deliveryRfbs.setDelivered({
posting_number: postingNumber,
delivered_at: new Date().toISOString()
});
console.log(`🎉 ${postingNumber}: Доставлено!`);
}, defaultSchedule.deliveredDelay * 60 * 1000);
return {
postingNumber,
trackingNumber,
scheduled: true,
timeline: {
sentBySeller: new Date(baseTime + defaultSchedule.sentBySellerDelay * 60 * 1000),
lastMile: new Date(baseTime + defaultSchedule.lastMileDelay * 60 * 1000),
delivering: new Date(baseTime + defaultSchedule.deliveringDelay * 60 * 1000),
delivered: new Date(baseTime + defaultSchedule.deliveredDelay * 60 * 1000)
}
};
}
}
// Использование пакетного процессора
const bulkProcessor = new BulkDeliveryProcessor(client);
// Массовое добавление трек-номеров
const trackingData = [
{
postingNumber: 'FBS-123456789',
trackingNumber: 'TRACK123456',
deliveryService: 'CDEK'
},
{
postingNumber: 'FBS-987654321',
trackingNumber: 'TRACK789012',
deliveryService: 'Russian Post'
}
// ... еще данные
];
const trackingResults = await bulkProcessor.addMultipleTrackingNumbers(trackingData);
console.log(`📊 Трек-номера: успешно ${trackingResults.successCount}, ошибок ${trackingResults.errorCount}`);
// Массовое изменение статусов на "Отправлено продавцом"
const postingNumbers = ['FBS-123456789', 'FBS-987654321', 'FBS-555666777'];
const statusResults = await bulkProcessor.bulkStatusUpdate(
postingNumbers,
'sent_by_seller'
);
console.log(`📊 Статусы обновлены: ${statusResults.successCount}/${statusResults.totalPostings}`);
3. Система мониторинга доставок
class DeliveryMonitor {
constructor(private client: OzonSellerApiClient) {}
// Мониторинг переносов доставки
async monitorRescheduleRequests(postingNumbers: string[]) {
const rescheduleInfo = [];
for (const postingNumber of postingNumbers) {
try {
const restrictions = await this.client.deliveryRfbs.getTimeslotChangeRestrictions({
posting_number: postingNumber
});
if (restrictions.restrictions) {
rescheduleInfo.push({
postingNumber,
canChange: restrictions.restrictions.can_change,
availableReschedules: restrictions.restrictions.available_reschedules,
availableDates: restrictions.restrictions.available_dates,
status: 'active'
});
}
} catch (error) {
rescheduleInfo.push({
postingNumber,
canChange: false,
availableReschedules: 0,
availableDates: [],
status: 'error',
error: error.message
});
}
// Пауза между запросами
await new Promise(resolve => setTimeout(resolve, 300));
}
return rescheduleInfo;
}
// Автоматический перенос доставок при критических ситуациях
async autoRescheduleOnEmergency(
emergencyPostings: Array<{
postingNumber: string;
reason: string;
urgency: 'low' | 'medium' | 'high';
}>
) {
const rescheduledPostings = [];
const failedReschedules = [];
for (const emergency of emergencyPostings) {
try {
// Получить доступные даты
const restrictions = await this.client.deliveryRfbs.getTimeslotChangeRestrictions({
posting_number: emergency.postingNumber
});
if (!restrictions.restrictions?.can_change ||
!restrictions.restrictions.available_dates ||
restrictions.restrictions.available_dates.length === 0) {
failedReschedules.push({
...emergency,
reason: 'Нет доступных дат для переноса'
});
continue;
}
// Выбрать дату в зависимости от срочности
let targetDate: string;
const availableDates = restrictions.restrictions.available_dates;
switch (emergency.urgency) {
case 'high':
// Ближайшая дата
targetDate = availableDates[0];
break;
case 'medium':
// Вторая доступная дата или первая, если только одна
targetDate = availableDates[1] || availableDates[0];
break;
case 'low':
// Последняя доступная дата
targetDate = availableDates[availableDates.length - 1];
break;
}
// Выполнить перенос
const rescheduleResult = await this.client.deliveryRfbs.setTimeslot({
posting_number: emergency.postingNumber,
timeslot_date: targetDate
});
rescheduledPostings.push({
...emergency,
oldDate: 'unknown', // API не возвращает старую дату
newDate: rescheduleResult.new_timeslot_date,
rescheduledAt: new Date().toISOString()
});
console.log(`✅ Автоперенос ${emergency.postingNumber}: ${emergency.urgency} → ${rescheduleResult.new_timeslot_date}`);
} catch (error) {
failedReschedules.push({
...emergency,
reason: error.message
});
console.error(`❌ Не удалось перенести ${emergency.postingNumber}: ${error.message}`);
}
// Пауза между операциями
await new Promise(resolve => setTimeout(resolve, 1000));
}
return {
totalEmergencies: emergencyPostings.length,
rescheduledCount: rescheduledPostings.length,
failedCount: failedReschedules.length,
rescheduled: rescheduledPostings,
failed: failedReschedules
};
}
// Отчет по доставкам за период
async generateDeliveryReport(deliveryData: Array<{
postingNumber: string;
trackingNumber?: string;
deliveryService?: string;
currentStatus: string;
statusHistory: Array<{
status: string;
timestamp: string;
}>;
}>) {
const report = {
totalDeliveries: deliveryData.length,
statusBreakdown: new Map<string, number>(),
averageDeliveryTime: 0,
serviceBreakdown: new Map<string, number>(),
completedDeliveries: 0,
pendingDeliveries: 0
};
deliveryData.forEach(delivery => {
// Подсчет по текущим статусам
const count = report.statusBreakdown.get(delivery.currentStatus) || 0;
report.statusBreakdown.set(delivery.currentStatus, count + 1);
// Подсчет по службам доставки
if (delivery.deliveryService) {
const serviceCount = report.serviceBreakdown.get(delivery.deliveryService) || 0;
report.serviceBreakdown.set(delivery.deliveryService, serviceCount + 1);
}
// Подсчет завершенных и ожидающих
if (delivery.currentStatus === 'delivered') {
report.completedDeliveries++;
// Подсчет времени доставки
const sentStatus = delivery.statusHistory.find(s => s.status === 'sent_by_seller');
const deliveredStatus = delivery.statusHistory.find(s => s.status === 'delivered');
if (sentStatus && deliveredStatus) {
const sentTime = new Date(sentStatus.timestamp).getTime();
const deliveredTime = new Date(deliveredStatus.timestamp).getTime();
const deliveryTimeHours = (deliveredTime - sentTime) / (1000 * 60 * 60);
report.averageDeliveryTime += deliveryTimeHours;
}
} else {
report.pendingDeliveries++;
}
});
// Среднее время доставки
if (report.completedDeliveries > 0) {
report.averageDeliveryTime = Math.round(
(report.averageDeliveryTime / report.completedDeliveries) * 10
) / 10;
}
return report;
}
}
const monitor = new DeliveryMonitor(client);
// Мониторинг возможностей переноса
const postingsToCheck = ['FBS-123456789', 'FBS-987654321'];
const rescheduleInfo = await monitor.monitorRescheduleRequests(postingsToCheck);
rescheduleInfo.forEach(info => {
console.log(`${info.postingNumber}: переносов доступно ${info.availableReschedules}`);
if (info.availableDates && info.availableDates.length > 0) {
console.log(` Доступные даты: ${info.availableDates.join(', ')}`);
}
});
// Экстренный перенос доставок
const emergencies = [
{
postingNumber: 'FBS-123456789',
reason: 'Повреждение товара, требуется замена',
urgency: 'high' as const
},
{
postingNumber: 'FBS-987654321',
reason: 'Задержка на складе',
urgency: 'medium' as const
}
];
const emergencyReschedule = await monitor.autoRescheduleOnEmergency(emergencies);
console.log(`🚨 Экстренных переносов: ${emergencyReschedule.rescheduledCount}/${emergencyReschedule.totalEmergencies}`);
Обработка ошибок
Типичные ошибки и их обработка
try {
const result = await client.deliveryRfbs.setDelivered({
posting_number: 'FBS-123456789',
delivered_at: '2024-01-20T15:30:00Z'
});
} catch (error) {
if (error.response?.status === 400) {
const errorData = error.response.data;
switch (errorData.code) {
case 'POSTING_NOT_FOUND':
console.error('Отправление не найдено');
break;
case 'INVALID_STATUS_TRANSITION':
console.error('Неверный переход статуса. Проверьте текущий статус отправления');
break;
case 'INVALID_DELIVERY_DATE':
console.error('Некорректная дата доставки');
break;
case 'TRACKING_NUMBER_ALREADY_EXISTS':
console.error('Трек-номер уже привязан к другому отправлению');
break;
case 'NO_RESCHEDULE_AVAILABLE':
console.error('Перенос недоступен или исчерпаны попытки');
break;
default:
console.error('Неизвестная ошибка:', errorData.message);
}
} else if (error.response?.status === 422) {
console.error('Ошибка валидации данных:', error.response.data);
} else if (error.response?.status === 429) {
console.error('Превышен лимит запросов. Повторите попытку позже.');
} else {
console.error('Произошла ошибка:', error.message);
}
}
Лучшие практики
1. Корректная последовательность статусов
// Всегда соблюдайте правильную последовательность статусов
const statusFlow = [
'sent_by_seller',
'last_mile',
'delivering',
'delivered'
];
// Никогда не пропускайте промежуточные статусы
const updateStatusSequentially = async (postingNumber: string) => {
// ❌ Неправильно - пропускаем статусы
// await client.deliveryRfbs.setDelivered({...});
// ✅ Правильно - последовательно
await client.deliveryRfbs.setSentBySeller({
posting_number: postingNumber,
sent_by_seller_at: new Date().toISOString()
});
// Ждем реального времени в пути
await new Promise(resolve => setTimeout(resolve, 60000)); // 1 минута для примера
await client.deliveryRfbs.setLastMile({
posting_number: postingNumber,
last_mile_at: new Date().toISOString()
});
// И так далее...
};
2. Управление трек-номерами
// Централизованное управление трек-номерами
class TrackingManager {
private trackingCache = new Map<string, string>();
async addTrackingNumber(postingNumber: string, trackingNumber: string, deliveryService: string) {
// Проверить, не добавлен ли уже трек-номер
if (this.trackingCache.has(postingNumber)) {
console.log(`Трек-номер для ${postingNumber} уже добавлен`);
return this.trackingCache.get(postingNumber);
}
const result = await client.deliveryRfbs.setTrackingNumbers({
tracking_numbers: [{
posting_number: postingNumber,
tracking_number: trackingNumber,
delivery_service: deliveryService
}]
});
if (result.results?.[0]?.result === 'success') {
this.trackingCache.set(postingNumber, trackingNumber);
return trackingNumber;
}
throw new Error(`Не удалось добавить трек-номер для ${postingNumber}`);
}
}
3. Соблюдение лимитов API
// Используйте очереди запросов для соблюдения лимитов
class RequestQueue {
private queue: Array<() => Promise<any>> = [];
private processing = false;
private readonly DELAY_MS = 500; // Задержка между запросами
async enqueue<T>(requestFn: () => Promise<T>): Promise<T> {
return new Promise((resolve, reject) => {
this.queue.push(async () => {
try {
const result = await requestFn();
resolve(result);
} catch (error) {
reject(error);
}
});
if (!this.processing) {
this.processQueue();
}
});
}
private async processQueue() {
if (this.processing || this.queue.length === 0) return;
this.processing = true;
while (this.queue.length > 0) {
const request = this.queue.shift()!;
await request();
if (this.queue.length > 0) {
await new Promise(resolve => setTimeout(resolve, this.DELAY_MS));
}
}
this.processing = false;
}
}
Связанные API: Delivery-FBS API (основная доставка), FBS API (управление заказами), Return API (управление возвратами)