💰 Finance API - Финансовая отчётность
💰 Finance API - Финансовая отчётность
Ключевая категория для управления финансами продавца — получение отчётов, анализ транзакций и взаиморасчёты с маркетплейсом OZON.
🎯 Назначение API
Finance API предоставляет полный набор инструментов для:
- Финансовая отчётность — детальные отчёты по продажам и реализации
- Анализ транзакций — подробная информация о всех начислениях и списаниях
- Взаиморасчёты — отчёты о взаиморасчётах с OZON
- Компенсации — управление компенсациями и декомпенсациями
- B2B продажи — специализированные отчёты для юридических лиц
- Выкупленные товары — отчёты по товарам для продажи в ЕАЭС
📋 Список методов (10 endpoints)
📊 Основные отчёты
| Метод | Endpoint | Версия | Назначение |
|——-|———-|———|————|
| getTransactionList
| /v3/finance/transaction/list
| v3 | Список транзакций с детализацией |
| getTransactionTotals
| /v3/finance/transaction/totals
| v3 | Итоговые суммы транзакций |
| getRealizationReport
| /v2/finance/realization
| v2 | Отчёт о реализации товаров |
| getRealizationReportPosting
| /v1/finance/realization/posting
| v1 | Позаказный отчёт о реализации |
🏢 B2B отчёты
| Метод | Endpoint | Версия | Назначение |
|——-|———-|———|————|
| getDocumentB2BSalesReport
| /v1/finance/document-b2b-sales
| v1 | Реестр продаж юридическим лицам |
| getDocumentB2BSalesJSON
| /v1/finance/document-b2b-sales/json
| v1 | B2B отчёт в JSON формате |
| getMutualSettlementReport
| /v1/finance/mutual-settlement
| v1 | Отчёт о взаиморасчётах |
🔄 Компенсации и специальные отчёты
| Метод | Endpoint | Версия | Назначение |
|——-|———-|———|————|
| getCompensationReport
| /v1/finance/compensation
| v1 | Отчёт о компенсациях |
| getDecompensationReport
| /v1/finance/decompensation
| v1 | Отчёт о декомпенсациях |
| getProductsBuyoutReport
| /v1/finance/products/buyout
| v1 | Отчёт о выкупленных товарах |
🚀 Быстрый старт
Инициализация клиента
import { OzonSellerAPI } from 'daytona-ozon-seller-api';
const client = new OzonSellerAPI({
clientId: 'your-client-id',
apiKey: 'your-api-key'
});
Базовые операции
1. Получение списка транзакций
try {
const transactions = await client.finance.getTransactionList({
filter: {
date: {
from: '2024-01-01T00:00:00.000Z',
to: '2024-01-31T23:59:59.999Z'
},
operation_type: ['orders'], // Только заказы
posting_number: [] // Все отправления
},
page: 1,
page_size: 1000
});
console.log(`💰 Найдено транзакций: ${transactions.result?.operations?.length || 0}`);
// Анализ транзакций
let totalIncome = 0;
let totalExpenses = 0;
transactions.result?.operations?.forEach(operation => {
const amount = parseFloat(operation.amount || '0');
if (amount > 0) {
totalIncome += amount;
} else {
totalExpenses += Math.abs(amount);
}
console.log(`${operation.operation_date}: ${operation.operation_type} - ${amount}₽`);
});
console.log(`📈 Доходы: ${totalIncome}₽`);
console.log(`📉 Расходы: ${totalExpenses}₽`);
console.log(`💰 Прибыль: ${totalIncome - totalExpenses}₽`);
} catch (error) {
console.error('❌ Ошибка получения транзакций:', error);
}
2. Получение итогов по транзакциям
try {
const totals = await client.finance.getTransactionTotals({
date: {
from: '2024-01-01T00:00:00.000Z',
to: '2024-01-31T23:59:59.999Z'
},
transaction_type: 'all' // Все типы операций
});
const result = totals.result;
if (result) {
console.log('📊 Финансовая сводка за месяц:');
console.log(`💸 Акции: ${result.accruals_for_sale || 0}₽`);
console.log(`📦 Товары: ${result.sale_commission || 0}₽`);
console.log(`🚚 Доставка: ${result.postings || 0}₽`);
console.log(`🔄 Возвраты: ${result.returns || 0}₽`);
console.log(`💰 Итого к выплате: ${result.others || 0}₽`);
}
} catch (error) {
console.error('❌ Ошибка получения итогов:', error);
}
3. Отчёт о реализации товаров
try {
// Получаем отчёт за прошлый месяц
const currentDate = new Date();
const lastMonth = new Date(currentDate.getFullYear(), currentDate.getMonth() - 1);
const report = await client.finance.getRealizationReport({
month: lastMonth.getMonth() + 1, // Месяц от 1 до 12
year: lastMonth.getFullYear()
});
console.log('📋 Отчёт о реализации готов');
console.log(`📄 Заголовок: ${report.result?.header?.doc_number || 'N/A'}`);
console.log(`📅 Период: ${report.result?.header?.doc_date || 'N/A'}`);
// Обработка строк отчёта
const rows = report.result?.rows || [];
let totalSales = 0;
rows.forEach(row => {
if (row.row_number && row.row_number > 0) {
const amount = parseFloat(row.sale_price || '0');
totalSales += amount;
console.log(`📦 ${row.product_name}: ${amount}₽`);
}
});
console.log(`💰 Общие продажи: ${totalSales}₽`);
} catch (error) {
console.error('❌ Ошибка получения отчёта о реализации:', error);
}
🎯 Детальные сценарии использования
📊 Сценарий 1: Полный финансовый анализ за период
Задача: Провести комплексный анализ финансов за месяц с детализацией по типам операций
class FinanceAnalyzer {
async analyzeMonth(year: number, month: number) {
console.log(`📊 Анализ финансов за ${month}/${year}...`);
const startDate = new Date(year, month - 1, 1);
const endDate = new Date(year, month, 0, 23, 59, 59, 999);
try {
// 1. Получаем все транзакции за месяц
const allTransactions = await this.getAllTransactions(startDate, endDate);
// 2. Получаем итоговые суммы
const totals = await client.finance.getTransactionTotals({
date: {
from: startDate.toISOString(),
to: endDate.toISOString()
},
transaction_type: 'all'
});
// 3. Получаем отчёт о реализации
const realizationReport = await client.finance.getRealizationReport({
month: month,
year: year
});
// 4. Анализируем данные
const analysis = this.processFinancialData(
allTransactions,
totals.result,
realizationReport.result
);
// 5. Генерируем отчёт
this.generateReport(analysis);
return analysis;
} catch (error) {
console.error('❌ Ошибка анализа финансов:', error);
throw error;
}
}
private async getAllTransactions(startDate: Date, endDate: Date) {
const allOperations = [];
let page = 1;
const pageSize = 1000;
while (true) {
const batch = await client.finance.getTransactionList({
filter: {
date: {
from: startDate.toISOString(),
to: endDate.toISOString()
}
},
page: page,
page_size: pageSize
});
const operations = batch.result?.operations || [];
allOperations.push(...operations);
console.log(`📥 Загружено транзакций: ${allOperations.length}`);
if (operations.length < pageSize) {
break; // Последняя страница
}
page++;
// Пауза между запросами
await new Promise(resolve => setTimeout(resolve, 1000));
}
return allOperations;
}
private processFinancialData(transactions: any[], totals: any, realization: any) {
// Группировка транзакций по типам
const groupedTransactions = this.groupTransactionsByType(transactions);
// Расчёт ключевых метрик
const metrics = {
totalRevenue: 0,
totalExpenses: 0,
netProfit: 0,
orderCount: 0,
avgOrderValue: 0,
returnRate: 0,
// ... другие метрики
};
// Анализ по категориям товаров
const categoryAnalysis = this.analyzeByCatgeories(transactions);
// Анализ трендов
const trends = this.analyzeTrends(transactions);
return {
metrics,
groupedTransactions,
categoryAnalysis,
trends,
totals,
realization
};
}
private groupTransactionsByType(transactions: any[]) {
const groups: { [key: string]: any[] } = {};
transactions.forEach(transaction => {
const type = transaction.operation_type || 'unknown';
if (!groups[type]) {
groups[type] = [];
}
groups[type].push(transaction);
});
return groups;
}
private generateReport(analysis: any) {
console.log('\n📊 ФИНАНСОВЫЙ ОТЧЁТ');
console.log('===================');
console.log('\n💰 Основные показатели:');
console.log(` Общая выручка: ${analysis.metrics.totalRevenue}₽`);
console.log(` Общие расходы: ${analysis.metrics.totalExpenses}₽`);
console.log(` Чистая прибыль: ${analysis.metrics.netProfit}₽`);
console.log(` Количество заказов: ${analysis.metrics.orderCount}`);
console.log(` Средний чек: ${analysis.metrics.avgOrderValue}₽`);
console.log('\n📦 По типам операций:');
Object.entries(analysis.groupedTransactions).forEach(([type, operations]) => {
const total = operations.reduce((sum: number, op: any) =>
sum + parseFloat(op.amount || '0'), 0);
console.log(` ${type}: ${total}₽ (${operations.length} операций)`);
});
}
}
// Использование
const analyzer = new FinanceAnalyzer();
const analysis = await analyzer.analyzeMonth(2024, 1);
🏢 Сценарий 2: Автоматическая генерация B2B отчётов
Задача: Создать систему автоматического формирования отчётов для бухгалтерии
class B2BReportGenerator {
async generateMonthlyReports(year: number, month: number) {
console.log(`📋 Генерация B2B отчётов за ${month}/${year}...`);
const dateStr = `${year}-${month.toString().padStart(2, '0')}`;
try {
// 1. Реестр продаж юридическим лицам (PDF)
const salesReportPDF = await client.finance.getDocumentB2BSalesReport({
date: dateStr,
language: 'RU'
});
console.log('✅ PDF отчёт о продажах B2B создан');
// 2. Реестр продаж в JSON формате для обработки
const salesReportJSON = await client.finance.getDocumentB2BSalesJSON({
date: dateStr
});
console.log('✅ JSON отчёт о продажах B2B получен');
// 3. Отчёт о взаиморасчётах
const settlementReport = await client.finance.getMutualSettlementReport({
date: dateStr,
language: 'RU'
});
console.log('✅ Отчёт о взаиморасчётах создан');
// 4. Обработка JSON данных для анализа
const jsonData = salesReportJSON;
if (jsonData.invoices) {
console.log(`📄 Найдено счетов-фактур: ${jsonData.invoices.length}`);
let totalB2BSales = 0;
jsonData.invoices.forEach((invoice: any) => {
const amount = parseFloat(invoice.total_amount || '0');
totalB2BSales += amount;
console.log(`🏢 ${invoice.buyer_name}: ${amount}₽`);
});
console.log(`💰 Общие B2B продажи: ${totalB2BSales}₽`);
}
// 5. Отправка отчётов в бухгалтерию (пример интеграции)
await this.sendReportsToAccounting({
salesReportPDF,
settlementReport,
jsonAnalysis: this.analyzeB2BData(salesReportJSON),
period: `${month}/${year}`
});
console.log('📧 Отчёты отправлены в бухгалтерию');
} catch (error) {
console.error('❌ Ошибка генерации B2B отчётов:', error);
throw error;
}
}
private analyzeB2BData(jsonData: any) {
const analysis = {
totalInvoices: 0,
totalAmount: 0,
topClients: [] as Array<{name: string, amount: number}>,
avgInvoiceAmount: 0
};
if (jsonData.invoices && Array.isArray(jsonData.invoices)) {
analysis.totalInvoices = jsonData.invoices.length;
const clientTotals = new Map<string, number>();
jsonData.invoices.forEach((invoice: any) => {
const amount = parseFloat(invoice.total_amount || '0');
analysis.totalAmount += amount;
const clientName = invoice.buyer_name || 'Unknown';
const currentTotal = clientTotals.get(clientName) || 0;
clientTotals.set(clientName, currentTotal + amount);
});
analysis.avgInvoiceAmount = analysis.totalAmount / analysis.totalInvoices;
// Топ клиенты
analysis.topClients = Array.from(clientTotals.entries())
.map(([name, amount]) => ({ name, amount }))
.sort((a, b) => b.amount - a.amount)
.slice(0, 10);
}
return analysis;
}
private async sendReportsToAccounting(reports: any) {
// Здесь может быть интеграция с системой бухгалтерии
// Например, отправка через API, email или файловое хранилище
console.log('📊 Подготовка отчётов для отправки...');
// Пример сохранения в файловую систему
const fs = require('fs').promises;
const reportsDir = `./reports/${reports.period}`;
// await fs.mkdir(reportsDir, { recursive: true });
// await fs.writeFile(`${reportsDir}/b2b_analysis.json`, JSON.stringify(reports.jsonAnalysis, null, 2));
console.log(`💾 Отчёты сохранены в ${reportsDir}`);
}
}
💸 Сценарий 3: Мониторинг компенсаций и декомпенсаций
Задача: Отслеживать все компенсации и создавать алерты при значительных суммах
class CompensationMonitor {
private readonly ALERT_THRESHOLD = 10000; // 10,000₽
async monitorCompensations(year: number, month: number) {
console.log(`🔍 Мониторинг компенсаций за ${month}/${year}...`);
const dateStr = `${year}-${month.toString().padStart(2, '0')}`;
try {
// 1. Получаем отчёт о компенсациях
const compensationReport = await client.finance.getCompensationReport({
date: dateStr,
language: 'RU'
});
console.log('✅ Отчёт о компенсациях получен');
// 2. Получаем отчёт о декомпенсациях
const decompensationReport = await client.finance.getDecompensationReport({
date: dateStr,
language: 'RU'
});
console.log('✅ Отчёт о декомпенсациях получен');
// 3. Анализируем компенсации из транзакций
const transactions = await this.getCompensationTransactions(dateStr);
const analysis = this.analyzeCompensations(transactions);
// 4. Проверяем на превышение лимитов
const alerts = this.checkAlerts(analysis);
if (alerts.length > 0) {
console.log('🚨 ОБНАРУЖЕНЫ АЛЕРТЫ:');
alerts.forEach(alert => {
console.log(` ⚠️ ${alert.type}: ${alert.amount}₽ - ${alert.reason}`);
});
// Отправка уведомлений
await this.sendAlerts(alerts);
}
// 5. Создаём сводный отчёт
this.generateCompensationSummary(analysis);
return analysis;
} catch (error) {
console.error('❌ Ошибка мониторинга компенсаций:', error);
throw error;
}
}
private async getCompensationTransactions(dateStr: string) {
const [year, month] = dateStr.split('-').map(Number);
const startDate = new Date(year, month - 1, 1);
const endDate = new Date(year, month, 0, 23, 59, 59, 999);
const transactions = await client.finance.getTransactionList({
filter: {
date: {
from: startDate.toISOString(),
to: endDate.toISOString()
},
operation_type: ['compensation'] // Только компенсации
},
page: 1,
page_size: 1000
});
return transactions.result?.operations || [];
}
private analyzeCompensations(transactions: any[]) {
const analysis = {
totalCompensation: 0,
totalDecompensation: 0,
netCompensation: 0,
compensationCount: 0,
decompensationCount: 0,
largeCompensations: [] as any[],
byReason: new Map<string, { amount: number, count: number }>()
};
transactions.forEach(transaction => {
const amount = parseFloat(transaction.amount || '0');
const reason = transaction.operation_type_name || 'Unknown';
if (amount > 0) {
// Компенсация (положительная сумма)
analysis.totalCompensation += amount;
analysis.compensationCount++;
if (amount > this.ALERT_THRESHOLD) {
analysis.largeCompensations.push({
amount,
reason,
date: transaction.operation_date,
posting: transaction.posting_number
});
}
} else {
// Декомпенсация (отрицательная сумма)
const absAmount = Math.abs(amount);
analysis.totalDecompensation += absAmount;
analysis.decompensationCount++;
if (absAmount > this.ALERT_THRESHOLD) {
analysis.largeCompensations.push({
amount: absAmount,
reason: `Декомпенсация: ${reason}`,
date: transaction.operation_date,
posting: transaction.posting_number
});
}
}
// Группировка по причинам
const current = analysis.byReason.get(reason) || { amount: 0, count: 0 };
analysis.byReason.set(reason, {
amount: current.amount + Math.abs(amount),
count: current.count + 1
});
});
analysis.netCompensation = analysis.totalCompensation - analysis.totalDecompensation;
return analysis;
}
private checkAlerts(analysis: any) {
const alerts = [];
// Проверка крупных компенсаций
analysis.largeCompensations.forEach((comp: any) => {
alerts.push({
type: 'Крупная компенсация',
amount: comp.amount,
reason: comp.reason,
details: `Отправление: ${comp.posting}, Дата: ${comp.date}`
});
});
// Проверка общей суммы декомпенсаций
if (analysis.totalDecompensation > 50000) {
alerts.push({
type: 'Высокая сумма декомпенсаций',
amount: analysis.totalDecompensation,
reason: 'Общая сумма декомпенсаций превышает 50,000₽',
details: `Количество операций: ${analysis.decompensationCount}`
});
}
return alerts;
}
private async sendAlerts(alerts: any[]) {
// Отправка алертов через различные каналы
console.log('📧 Отправка алертов...');
// Здесь может быть интеграция с:
// - Электронной почтой
// - Slack/Telegram
// - SMS уведомлениями
// - Системой мониторинга
alerts.forEach(alert => {
console.log(`🔔 Алерт: ${JSON.stringify(alert, null, 2)}`);
});
}
private generateCompensationSummary(analysis: any) {
console.log('\n💸 ОТЧЁТ ПО КОМПЕНСАЦИЯМ');
console.log('=======================');
console.log(`💰 Общие компенсации: ${analysis.totalCompensation}₽ (${analysis.compensationCount} операций)`);
console.log(`💸 Общие декомпенсации: ${analysis.totalDecompensation}₽ (${analysis.decompensationCount} операций)`);
console.log(`🏦 Чистые компенсации: ${analysis.netCompensation}₽`);
console.log('\n📊 По причинам:');
Array.from(analysis.byReason.entries())
.sort(([,a], [,b]) => b.amount - a.amount)
.slice(0, 10)
.forEach(([reason, data]) => {
console.log(` ${reason}: ${data.amount}₽ (${data.count} операций)`);
});
}
}
📝 TypeScript типы и интерфейсы
Основные Request интерфейсы
// Список транзакций
interface FinanceTransactionListV3Request {
filter?: FinanceTransactionListV3RequestFilter;
page: number;
page_size: number;
}
interface FinanceTransactionListV3RequestFilter {
date?: {
from: string; // ISO 8601 format
to: string; // ISO 8601 format
};
operation_type?: ('orders' | 'returns' | 'services' | 'compensation' | 'transferDelivery' | 'other')[];
posting_number?: string[];
transaction_type?: 'all' | 'orders' | 'returns' | 'services' | 'compensation' | 'transferDelivery' | 'other';
}
// Итоги транзакций
interface FinanceTransactionTotalsV3Request {
date?: {
from: string; // ISO 8601 format
to: string; // ISO 8601 format
};
posting_number?: string;
transaction_type?: 'all' | 'orders' | 'returns' | 'services' | 'compensation' | 'transferDelivery' | 'other';
}
// Отчёт о реализации
interface GetRealizationReportRequestV2 {
month: number; // 1-12
year: number; // Например, 2024
}
interface GetRealizationReportPostingRequest {
month: number; // 1-12
year: number; // Например, 2024
}
// B2B отчёты
interface CreateDocumentB2BSalesReportRequest {
date: string; // Формат YYYY-MM
language?: 'RU' | 'EN';
}
interface CreateDocumentB2BSalesJSONReportRequest {
date: string; // Формат YYYY-MM, доступен до января 2019
}
interface CreateMutualSettlementReportRequest {
date: string; // Формат YYYY-MM
language?: 'RU' | 'EN';
}
// Компенсации
interface GetCompensationReportRequest {
date: string; // Формат YYYY-MM
language?: 'RU' | 'EN';
}
interface GetDecompensationReportRequest {
date: string; // Формат YYYY-MM
language?: 'RU' | 'EN';
}
// Выкупленные товары
interface GetFinanceProductsBuyoutRequest {
date_from: string; // Дата начала периода
date_to: string; // Дата окончания (макс. 31 день)
}
Response интерфейсы
// Список транзакций
interface FinanceTransactionListV3Response {
result?: {
operations?: FinanceOperation[];
page?: number;
page_size?: number;
page_count?: number;
};
}
interface FinanceOperation {
operation_id?: string;
operation_type?: string;
operation_type_name?: string;
operation_date?: string;
operation_date_full?: string;
description?: string;
accruals_for_sale?: string;
sale_commission?: string;
amount?: string;
type?: string;
posting_number?: string;
posting_number_child?: string;
cluster_from?: string;
cluster_to?: string;
product_id?: number;
product_name?: string;
warehouse_id?: number;
warehouse_name?: string;
region_from?: string;
region_to?: string;
delivery_schema?: string;
tariff_zone?: string;
payment_subject?: string;
delivery_type?: string;
}
// Итоги транзакций
interface FinanceTransactionTotalsV3Response {
result?: {
accruals_for_sale?: string;
sale_commission?: string;
postings?: string;
returns?: string;
services?: string;
compensation?: string;
others?: string;
acquiring_bank?: string;
acquiring_percent?: string;
acquiring_fix?: string;
claim?: string;
revenue?: string;
};
}
// Отчёт о реализации
interface GetRealizationReportResponseV2 {
result?: {
header?: RealizationReportHeader;
rows?: RealizationReportRow[];
};
}
interface RealizationReportHeader {
doc_number?: string;
doc_date?: string;
contract_date?: string;
contract_number?: string;
currency_code?: string;
seller_name?: string;
seller_inn?: string;
seller_kpp?: string;
seller_address?: string;
}
interface RealizationReportRow {
row_number?: number;
product_id?: number;
product_name?: string;
brand_name?: string;
model?: string;
posting_number?: string;
posting_date?: string;
warehouse_name?: string;
supplier_name?: string;
sale_price?: string;
for_pay?: string;
partial_num?: number;
}
// B2B JSON отчёт
interface CreateDocumentB2BSalesJSONReportResponse {
date_from?: string; // YYYY-MM-DD
date_to?: string; // YYYY-MM-DD
invoices?: B2BInvoice[];
seller_info?: B2BSellerInfo;
}
interface B2BInvoice {
invoice_number?: string;
invoice_date?: string;
buyer_name?: string;
buyer_inn?: string;
buyer_kpp?: string;
total_amount?: string;
currency_code?: string;
items?: B2BInvoiceItem[];
}
interface B2BInvoiceItem {
product_name?: string;
quantity?: number;
unit_price?: string;
total_price?: string;
vat_rate?: string;
vat_amount?: string;
}
interface B2BSellerInfo {
name?: string;
inn?: string;
kpp?: string;
address?: string;
}
// Выкупленные товары
interface GetFinanceProductsBuyoutResponse {
products?: BuyoutProduct[];
}
interface BuyoutProduct {
product_id?: number;
offer_id?: string;
product_name?: string;
price?: string;
quantity?: number;
amount?: string;
currency_code?: string;
warehouse_id?: number;
warehouse_name?: string;
cluster_from?: string;
cluster_to?: string;
region_from?: string;
region_to?: string;
}
// Базовые response типы для отчётов
interface CreateReportResponse {
result?: string; // Обычно "ok"
}
🚨 Особенности API и ограничения
Региональные ограничения
⚠️ Важно: Некоторые методы недоступны для продавцов с договором ТОО «ОЗОН Маркетплейс Казахстан»:
getRealizationReport
(v2)getRealizationReportPosting
(v1)
Временные ограничения
- Транзакции: Максимальный период в одном запросе — 1 месяц
- Выкупленные товары: Максимальный период — 31 день
- B2B JSON отчёт: Доступен только до января 2019 включительно
- Отчёты о реализации: Доступны с августа 2023 года
Rate Limiting
// ⚠️ Используйте последовательные запросы
async function getTransactionsSequentially() {
const results = [];
for (const dateRange of dateRanges) {
const transactions = await client.finance.getTransactionList({
filter: { date: dateRange },
page: 1,
page_size: 1000
});
results.push(...transactions.result?.operations || []);
// Обязательная пауза между запросами
await new Promise(resolve => setTimeout(resolve, 1000));
}
return results;
}
Несоответствие данных
⚠️ Внимание: Данные методов getTransactionList
и getTransactionTotals
могут не соответствовать информации в личном кабинете. Для точных данных используйте официальные отчёты через интерфейс кабинета.
💡 Лучшие практики
1. Управление большими объёмами данных
class FinanceDataManager {
private readonly BATCH_SIZE = 1000;
private readonly DELAY_BETWEEN_REQUESTS = 1000; // 1 секунда
async getAllTransactions(startDate: Date, endDate: Date) {
const allOperations = [];
let page = 1;
// Разбиваем большой период на месячные интервалы
const monthlyRanges = this.splitIntoMonths(startDate, endDate);
for (const range of monthlyRanges) {
console.log(`📥 Загрузка транзакций за ${range.from} - ${range.to}`);
let hasMorePages = true;
page = 1;
while (hasMorePages) {
const batch = await client.finance.getTransactionList({
filter: {
date: {
from: range.from,
to: range.to
}
},
page: page,
page_size: this.BATCH_SIZE
});
const operations = batch.result?.operations || [];
allOperations.push(...operations);
hasMorePages = operations.length === this.BATCH_SIZE;
page++;
// Обязательная пауза
await this.delay(this.DELAY_BETWEEN_REQUESTS);
}
}
console.log(`✅ Загружено всего транзакций: ${allOperations.length}`);
return allOperations;
}
private splitIntoMonths(startDate: Date, endDate: Date) {
const ranges = [];
const current = new Date(startDate);
while (current <= endDate) {
const monthStart = new Date(current.getFullYear(), current.getMonth(), 1);
const monthEnd = new Date(current.getFullYear(), current.getMonth() + 1, 0, 23, 59, 59, 999);
ranges.push({
from: monthStart.toISOString(),
to: Math.min(monthEnd.getTime(), endDate.getTime()) === endDate.getTime()
? endDate.toISOString()
: monthEnd.toISOString()
});
current.setMonth(current.getMonth() + 1);
}
return ranges;
}
private delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
2. Кэширование финансовых данных
class FinanceCache {
private cache = new Map<string, { data: any; timestamp: number; ttl: number }>();
async getTransactionsCached(filter: any, ttl = 5 * 60 * 1000) { // 5 минут по умолчанию
const cacheKey = this.generateCacheKey(filter);
const cached = this.cache.get(cacheKey);
if (cached && (Date.now() - cached.timestamp) < cached.ttl) {
console.log('📋 Данные получены из кэша');
return cached.data;
}
console.log('🔄 Загрузка данных из API...');
const data = await client.finance.getTransactionList(filter);
this.cache.set(cacheKey, {
data,
timestamp: Date.now(),
ttl
});
return data;
}
private generateCacheKey(filter: any): string {
return `transactions_${JSON.stringify(filter)}`;
}
clearCache() {
this.cache.clear();
console.log('🗑️ Кэш очищен');
}
}
3. Обработка ошибок и валидация
class FinanceValidator {
validateDateRange(startDate: Date, endDate: Date) {
const diffInMs = endDate.getTime() - startDate.getTime();
const diffInDays = diffInMs / (1000 * 60 * 60 * 24);
if (diffInDays > 31) {
throw new Error('❌ Максимальный период для запроса — 31 день');
}
if (startDate > endDate) {
throw new Error('❌ Дата начала не может быть больше даты окончания');
}
if (endDate > new Date()) {
throw new Error('❌ Нельзя запрашивать данные из будущего');
}
}
validateTransactionType(type: string) {
const validTypes = ['all', 'orders', 'returns', 'services', 'compensation', 'transferDelivery', 'other'];
if (!validTypes.includes(type)) {
throw new Error(`❌ Недопустимый тип транзакции: ${type}. Разрешены: ${validTypes.join(', ')}`);
}
}
async safeTransactionRequest<T>(
operation: () => Promise<T>,
retries = 3
): Promise<T> {
for (let attempt = 1; attempt <= retries; attempt++) {
try {
return await operation();
} catch (error: any) {
console.error(`❌ Попытка ${attempt} не удалась:`, error.message);
if (attempt === retries) {
throw new Error(`Не удалось выполнить операцию после ${retries} попыток: ${error.message}`);
}
// Экспоненциальная задержка
const delay = Math.pow(2, attempt) * 1000;
console.log(`⏳ Ожидание ${delay}мс перед повторной попыткой...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw new Error('Unexpected end of retry loop');
}
}
4. Автоматизация отчётности
class AutomatedReporting {
private scheduler = new Map<string, NodeJS.Timeout>();
scheduleMonthlyReports() {
// Генерация отчётов каждое 5 число месяца в 9:00
const cronExpression = '0 9 5 * *'; // 5 число каждого месяца в 9:00
this.scheduler.set('monthly_reports', this.createSchedule(cronExpression, async () => {
try {
const lastMonth = this.getPreviousMonth();
console.log(`📅 Автоматическая генерация отчётов за ${lastMonth.month}/${lastMonth.year}`);
// Генерация всех необходимых отчётов
await Promise.all([
this.generateTransactionSummary(lastMonth.year, lastMonth.month),
this.generateB2BReports(lastMonth.year, lastMonth.month),
this.generateRealizationReport(lastMonth.year, lastMonth.month),
this.checkCompensations(lastMonth.year, lastMonth.month)
]);
console.log('✅ Все отчёты созданы успешно');
} catch (error) {
console.error('❌ Ошибка автоматической генерации отчётов:', error);
await this.sendErrorAlert(error);
}
}));
}
private createSchedule(cronExpression: string, callback: () => Promise<void>) {
// Здесь должна быть реализация cron scheduler
// Например, с использованием библиотеки node-cron
console.log(`⏰ Запланированo выполнение: ${cronExpression}`);
// Простая имитация для примера
const now = new Date();
const nextRun = new Date(now.getFullYear(), now.getMonth(), 5, 9, 0, 0);
if (nextRun < now) {
nextRun.setMonth(nextRun.getMonth() + 1);
}
const delay = nextRun.getTime() - now.getTime();
return setTimeout(() => {
callback();
this.scheduleMonthlyReports(); // Перепланировать на следующий месяц
}, delay);
}
private getPreviousMonth() {
const now = new Date();
const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1);
return {
year: lastMonth.getFullYear(),
month: lastMonth.getMonth() + 1
};
}
stopAllSchedules() {
this.scheduler.forEach((timeout, name) => {
clearTimeout(timeout);
console.log(`⏹️ Остановлено расписание: ${name}`);
});
this.scheduler.clear();
}
}
🔗 Связанные API
- Products API — товары для финансового анализа
- FBO API — заказы FBO для сверки транзакций
- FBS API — заказы FBS для сверки транзакций
- Analytics API — аналитика продаж
- Report API — дополнительные отчёты
📞 Поддержка
Нашли ошибку или хотите улучшить документацию?
Полезные ресурсы:
🏠 Главная документация | 📚 Все категории ```