Skip to content

Financial Reports ​

Financial reporting and transaction analysis.

Description ​

This use case demonstrates how to generate comprehensive financial reports using the Wildberries API, including balance tracking, transaction analysis, payouts, and financial reconciliation.

Get Current Balance ​

typescript
import { WildberriesSDK } from 'daytona-wildberries-typescript-sdk';

const sdk = new WildberriesSDK({
  apiKey: process.env.WB_API_KEY!
});

// Get seller balance
async function getCurrentBalance() {
  const balance = await sdk.finances.getBalance();

  console.log('Current Balance:');
  console.log(`  Available: ${balance.balance?.toLocaleString()} RUB`);

  return balance;
}

const balance = await getCurrentBalance();

Get Transactions ​

typescript
interface TransactionReport {
  period: { from: string; to: string };
  totalIncome: number;
  totalExpenses: number;
  netAmount: number;
  byType: Array<{
    type: string;
    count: number;
    amount: number;
  }>;
}

async function getTransactionReport(days = 30): Promise<TransactionReport> {
  const dateFrom = new Date(Date.now() - days * 24 * 60 * 60 * 1000)
    .toISOString();
  const dateTo = new Date().toISOString();

  const transactions = await sdk.finances.getTransactions({
    dateFrom,
    dateTo
  });

  let totalIncome = 0;
  let totalExpenses = 0;

  const typeStats = new Map<string, { count: number; amount: number }>();

  transactions.transactions?.forEach(tx => {
    const amount = tx.amount ?? 0;
    const type = tx.type ?? 'Other';

    if (amount > 0) {
      totalIncome += amount;
    } else {
      totalExpenses += Math.abs(amount);
    }

    const current = typeStats.get(type) ?? { count: 0, amount: 0 };
    current.count++;
    current.amount += amount;
    typeStats.set(type, current);
  });

  const byType = Array.from(typeStats.entries())
    .map(([type, stats]) => ({
      type,
      count: stats.count,
      amount: stats.amount
    }))
    .sort((a, b) => Math.abs(b.amount) - Math.abs(a.amount));

  return {
    period: { from: dateFrom, to: dateTo },
    totalIncome,
    totalExpenses,
    netAmount: totalIncome - totalExpenses,
    byType
  };
}

// Usage
const report = await getTransactionReport(30);
console.log(`Income: ${report.totalIncome.toLocaleString()} RUB`);
console.log(`Expenses: ${report.totalExpenses.toLocaleString()} RUB`);
console.log(`Net: ${report.netAmount.toLocaleString()} RUB`);

Sales Revenue Analysis ​

typescript
interface RevenueAnalysis {
  period: { from: string; to: string };
  totalRevenue: number;
  totalOrders: number;
  averageOrderValue: number;
  byProduct: Array<{
    nmId: number;
    name: string;
    quantity: number;
    revenue: number;
  }>;
  dailyRevenue: Array<{
    date: string;
    revenue: number;
    orders: number;
  }>;
}

async function analyzeRevenue(days = 30): Promise<RevenueAnalysis> {
  const dateFrom = new Date(Date.now() - days * 24 * 60 * 60 * 1000)
    .toISOString().split('T')[0];

  const [sales, products] = await Promise.all([
    sdk.reports.getSales(dateFrom, 0),
    sdk.products.getCardsList({ locale: 'ru' })
  ]);

  const productNames = new Map(products.map(p => [p.nmID, p.title]));

  // Aggregate by product
  const productStats = new Map<number, { quantity: number; revenue: number }>();

  // Aggregate by date
  const dailyStats = new Map<string, { revenue: number; orders: number }>();

  let totalRevenue = 0;
  let totalOrders = 0;

  sales.forEach(sale => {
    const revenue = sale.forPay ?? sale.finishedPrice ?? 0;
    const quantity = sale.quantity ?? 1;
    const date = (sale.date ?? '').split('T')[0];

    // Only count positive sales (not returns)
    if (quantity > 0) {
      totalRevenue += revenue;
      totalOrders++;

      // By product
      if (sale.nmId) {
        const current = productStats.get(sale.nmId) ?? { quantity: 0, revenue: 0 };
        current.quantity += quantity;
        current.revenue += revenue;
        productStats.set(sale.nmId, current);
      }

      // By date
      if (date) {
        const current = dailyStats.get(date) ?? { revenue: 0, orders: 0 };
        current.revenue += revenue;
        current.orders++;
        dailyStats.set(date, current);
      }
    }
  });

  const byProduct = Array.from(productStats.entries())
    .map(([nmId, stats]) => ({
      nmId,
      name: productNames.get(nmId) ?? `Product ${nmId}`,
      quantity: stats.quantity,
      revenue: stats.revenue
    }))
    .sort((a, b) => b.revenue - a.revenue)
    .slice(0, 20);

  const dailyRevenue = Array.from(dailyStats.entries())
    .map(([date, stats]) => ({ date, ...stats }))
    .sort((a, b) => a.date.localeCompare(b.date));

  return {
    period: { from: dateFrom, to: new Date().toISOString().split('T')[0] },
    totalRevenue,
    totalOrders,
    averageOrderValue: totalOrders > 0 ? Math.round(totalRevenue / totalOrders) : 0,
    byProduct,
    dailyRevenue
  };
}

// Usage
const revenue = await analyzeRevenue(30);
console.log(`Total Revenue: ${revenue.totalRevenue.toLocaleString()} RUB`);
console.log(`Total Orders: ${revenue.totalOrders}`);
console.log(`AOV: ${revenue.averageOrderValue.toLocaleString()} RUB`);

Realization Report & Fulfillment Analysis ​

The getReportDetailByPeriod() method provides detailed sales realization data including the delivery_method field for fulfillment analysis.

typescript
interface FulfillmentAnalysis {
  period: { from: string; to: string };
  byMethod: {
    FBS: { count: number; revenue: number; percentage: number };
    FBW: { count: number; revenue: number; percentage: number };
    DBS: { count: number; revenue: number; percentage: number };
  };
  totalOrders: number;
  totalRevenue: number;
}

async function analyzeFulfillmentMethods(days = 30): Promise<FulfillmentAnalysis> {
  const dateFrom = new Date(Date.now() - days * 24 * 60 * 60 * 1000)
    .toISOString().split('T')[0];
  const dateTo = new Date().toISOString().split('T')[0];

  const report = await sdk.finances.getReportDetailByPeriod({
    dateFrom,
    dateTo
  });

  const stats = {
    FBS: { count: 0, revenue: 0 },
    FBW: { count: 0, revenue: 0 },
    DBS: { count: 0, revenue: 0 }
  };

  let totalOrders = 0;
  let totalRevenue = 0;

  report.forEach(item => {
    const method = item.delivery_method as 'FBS' | 'FBW' | 'DBS';
    const revenue = item.ppvz_for_pay || 0;

    if (method && stats[method]) {
      stats[method].count++;
      stats[method].revenue += revenue;
    }

    totalOrders++;
    totalRevenue += revenue;
  });

  return {
    period: { from: dateFrom, to: dateTo },
    byMethod: {
      FBS: {
        ...stats.FBS,
        percentage: totalOrders > 0 ? (stats.FBS.count / totalOrders) * 100 : 0
      },
      FBW: {
        ...stats.FBW,
        percentage: totalOrders > 0 ? (stats.FBW.count / totalOrders) * 100 : 0
      },
      DBS: {
        ...stats.DBS,
        percentage: totalOrders > 0 ? (stats.DBS.count / totalOrders) * 100 : 0
      }
    },
    totalOrders,
    totalRevenue
  };
}

// Usage
const analysis = await analyzeFulfillmentMethods(30);

console.log('=== Fulfillment Method Analysis ===');
console.log(`Period: ${analysis.period.from} to ${analysis.period.to}`);
console.log('');
console.log('By Delivery Method:');
Object.entries(analysis.byMethod).forEach(([method, data]) => {
  console.log(`  ${method}:`);
  console.log(`    Orders: ${data.count} (${data.percentage.toFixed(1)}%)`);
  console.log(`    Revenue: ${data.revenue.toLocaleString()}₽`);
});
console.log('');
console.log(`Total Orders: ${analysis.totalOrders}`);
console.log(`Total Revenue: ${analysis.totalRevenue.toLocaleString()}₽`);

Delivery Method Values ​

ValueDescription (EN)Description (RU)
FBSFulfillment by SellerПродажа со склада продавца
FBWFulfillment by WildberriesПродажа со склада Wildberries
DBSDelivery by SellerДоставка силами продавца

Payout Tracking ​

typescript
interface PayoutReport {
  period: { from: string; to: string };
  totalPayouts: number;
  payoutCount: number;
  payouts: Array<{
    id: string;
    date: string;
    amount: number;
    status: string;
  }>;
}

async function getPayoutReport(days = 90): Promise<PayoutReport> {
  const dateFrom = new Date(Date.now() - days * 24 * 60 * 60 * 1000)
    .toISOString().split('T')[0];

  const payouts = await sdk.finances.getPayouts({
    dateFrom
  });

  let totalPayouts = 0;

  const payoutList = payouts.payouts?.map(payout => {
    const amount = payout.amount ?? 0;
    totalPayouts += amount;

    return {
      id: payout.id ?? '',
      date: payout.date ?? '',
      amount,
      status: payout.status ?? 'unknown'
    };
  }) ?? [];

  return {
    period: { from: dateFrom, to: new Date().toISOString().split('T')[0] },
    totalPayouts,
    payoutCount: payoutList.length,
    payouts: payoutList.sort((a, b) => b.date.localeCompare(a.date))
  };
}

// Get payout details
async function getPayoutDetails(payoutId: string) {
  const details = await sdk.finances.getPayoutById(payoutId);

  console.log(`Payout ${payoutId}:`);
  console.log(`  Amount: ${details.amount?.toLocaleString()} RUB`);
  console.log(`  Date: ${details.date}`);
  console.log(`  Status: ${details.status}`);

  return details;
}

Profit & Loss Statement ​

typescript
interface ProfitLossStatement {
  period: { from: string; to: string };
  revenue: {
    sales: number;
    returns: number;
    netSales: number;
  };
  expenses: {
    commission: number;
    logistics: number;
    storage: number;
    other: number;
    totalExpenses: number;
  };
  netProfit: number;
  profitMargin: number;
}

async function generateProfitLossStatement(days = 30): Promise<ProfitLossStatement> {
  const dateFrom = new Date(Date.now() - days * 24 * 60 * 60 * 1000)
    .toISOString().split('T')[0];

  const [sales, transactions] = await Promise.all([
    sdk.reports.getSales(dateFrom, 0),
    sdk.finances.getTransactions({
      dateFrom: new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString(),
      dateTo: new Date().toISOString()
    })
  ]);

  // Calculate revenue
  let grossSales = 0;
  let returns = 0;

  sales.forEach(sale => {
    const amount = sale.forPay ?? sale.finishedPrice ?? 0;
    const quantity = sale.quantity ?? 1;

    if (quantity > 0) {
      grossSales += amount;
    } else {
      returns += Math.abs(amount);
    }
  });

  const netSales = grossSales - returns;

  // Calculate expenses from transactions
  let commission = 0;
  let logistics = 0;
  let storage = 0;
  let other = 0;

  transactions.transactions?.forEach(tx => {
    const amount = tx.amount ?? 0;
    const type = (tx.type ?? '').toLowerCase();

    if (amount < 0) {
      const absAmount = Math.abs(amount);

      if (type.includes('commission') || type.includes('комиссия')) {
        commission += absAmount;
      } else if (type.includes('logistic') || type.includes('логистик') || type.includes('delivery')) {
        logistics += absAmount;
      } else if (type.includes('storage') || type.includes('хранени')) {
        storage += absAmount;
      } else {
        other += absAmount;
      }
    }
  });

  const totalExpenses = commission + logistics + storage + other;
  const netProfit = netSales - totalExpenses;
  const profitMargin = netSales > 0 ? Math.round((netProfit / netSales) * 1000) / 10 : 0;

  return {
    period: { from: dateFrom, to: new Date().toISOString().split('T')[0] },
    revenue: {
      sales: grossSales,
      returns,
      netSales
    },
    expenses: {
      commission,
      logistics,
      storage,
      other,
      totalExpenses
    },
    netProfit,
    profitMargin
  };
}

// Usage
const pnl = await generateProfitLossStatement(30);

console.log('=== Profit & Loss Statement ===');
console.log(`Period: ${pnl.period.from} to ${pnl.period.to}`);
console.log('');
console.log('REVENUE');
console.log(`  Gross Sales:    ${pnl.revenue.sales.toLocaleString()} RUB`);
console.log(`  Returns:       -${pnl.revenue.returns.toLocaleString()} RUB`);
console.log(`  Net Sales:      ${pnl.revenue.netSales.toLocaleString()} RUB`);
console.log('');
console.log('EXPENSES');
console.log(`  Commission:     ${pnl.expenses.commission.toLocaleString()} RUB`);
console.log(`  Logistics:      ${pnl.expenses.logistics.toLocaleString()} RUB`);
console.log(`  Storage:        ${pnl.expenses.storage.toLocaleString()} RUB`);
console.log(`  Other:          ${pnl.expenses.other.toLocaleString()} RUB`);
console.log(`  Total Expenses: ${pnl.expenses.totalExpenses.toLocaleString()} RUB`);
console.log('');
console.log(`NET PROFIT:       ${pnl.netProfit.toLocaleString()} RUB`);
console.log(`Profit Margin:    ${pnl.profitMargin}%`);

Financial Reconciliation ​

typescript
interface ReconciliationReport {
  period: { from: string; to: string };
  expectedRevenue: number;
  actualPayouts: number;
  pendingAmount: number;
  discrepancies: Array<{
    date: string;
    expected: number;
    actual: number;
    difference: number;
  }>;
  status: 'matched' | 'discrepancy' | 'pending';
}

async function reconcileFinances(days = 30): Promise<ReconciliationReport> {
  const dateFrom = new Date(Date.now() - days * 24 * 60 * 60 * 1000)
    .toISOString().split('T')[0];

  const [sales, payouts, balance] = await Promise.all([
    sdk.reports.getSales(dateFrom, 0),
    sdk.finances.getPayouts({ dateFrom }),
    sdk.finances.getBalance()
  ]);

  // Calculate expected revenue from sales
  let expectedRevenue = 0;
  sales.forEach(sale => {
    const amount = sale.forPay ?? 0;
    expectedRevenue += amount;
  });

  // Calculate actual payouts
  let actualPayouts = 0;
  payouts.payouts?.forEach(payout => {
    actualPayouts += payout.amount ?? 0;
  });

  const pendingAmount = balance.balance ?? 0;
  const discrepancies: ReconciliationReport['discrepancies'] = [];

  // Check for significant discrepancy
  const totalAccounted = actualPayouts + pendingAmount;
  const difference = Math.abs(expectedRevenue - totalAccounted);
  const threshold = expectedRevenue * 0.05; // 5% tolerance

  let status: ReconciliationReport['status'] = 'matched';

  if (difference > threshold) {
    status = 'discrepancy';
    discrepancies.push({
      date: new Date().toISOString().split('T')[0],
      expected: expectedRevenue,
      actual: totalAccounted,
      difference: expectedRevenue - totalAccounted
    });
  } else if (pendingAmount > 0) {
    status = 'pending';
  }

  return {
    period: { from: dateFrom, to: new Date().toISOString().split('T')[0] },
    expectedRevenue,
    actualPayouts,
    pendingAmount,
    discrepancies,
    status
  };
}

// Usage
const reconciliation = await reconcileFinances(30);
console.log(`Expected Revenue: ${reconciliation.expectedRevenue.toLocaleString()} RUB`);
console.log(`Actual Payouts:   ${reconciliation.actualPayouts.toLocaleString()} RUB`);
console.log(`Pending Balance:  ${reconciliation.pendingAmount.toLocaleString()} RUB`);
console.log(`Status: ${reconciliation.status}`);

Monthly Financial Summary ​

typescript
interface MonthlySummary {
  month: string;
  revenue: number;
  expenses: number;
  profit: number;
  profitMargin: number;
  orderCount: number;
  returnCount: number;
  returnRate: number;
}

async function getMonthlyFinancialSummary(months = 6): Promise<MonthlySummary[]> {
  const summaries: MonthlySummary[] = [];

  for (let i = 0; i < months; i++) {
    const startDate = new Date();
    startDate.setMonth(startDate.getMonth() - i);
    startDate.setDate(1);

    const endDate = new Date(startDate);
    endDate.setMonth(endDate.getMonth() + 1);
    endDate.setDate(0);

    const dateFrom = startDate.toISOString().split('T')[0];

    const sales = await sdk.reports.getSales(dateFrom, 0);

    let revenue = 0;
    let orderCount = 0;
    let returnCount = 0;

    sales.forEach(sale => {
      const saleDate = new Date(sale.date ?? '');
      if (saleDate >= startDate && saleDate <= endDate) {
        const amount = sale.forPay ?? sale.finishedPrice ?? 0;
        const quantity = sale.quantity ?? 1;

        if (quantity > 0) {
          revenue += amount;
          orderCount++;
        } else {
          returnCount++;
        }
      }
    });

    // Estimate expenses (typically 30-40% of revenue for WB)
    const estimatedExpenses = revenue * 0.35;
    const profit = revenue - estimatedExpenses;

    summaries.push({
      month: startDate.toISOString().slice(0, 7), // YYYY-MM format
      revenue,
      expenses: estimatedExpenses,
      profit,
      profitMargin: revenue > 0 ? Math.round((profit / revenue) * 1000) / 10 : 0,
      orderCount,
      returnCount,
      returnRate: orderCount > 0 ? Math.round((returnCount / orderCount) * 1000) / 10 : 0
    });
  }

  return summaries.reverse(); // Oldest first
}

// Usage
const monthlySummaries = await getMonthlyFinancialSummary(6);
console.log('Monthly Financial Summary');
console.log('=========================');
monthlySummaries.forEach(summary => {
  console.log(`${summary.month}:`);
  console.log(`  Revenue: ${summary.revenue.toLocaleString()} RUB`);
  console.log(`  Profit: ${summary.profit.toLocaleString()} RUB (${summary.profitMargin}%)`);
  console.log(`  Orders: ${summary.orderCount}, Returns: ${summary.returnCount} (${summary.returnRate}%)`);
});

Export Financial Data ​

typescript
// Export transactions to CSV format
async function exportTransactionsCSV(days = 30): Promise<string> {
  const dateFrom = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
  const dateTo = new Date().toISOString();

  const transactions = await sdk.finances.getTransactions({
    dateFrom,
    dateTo
  });

  const headers = ['Date', 'Type', 'Amount', 'Description'];
  const rows = transactions.transactions?.map(tx => [
    tx.date ?? '',
    tx.type ?? '',
    (tx.amount ?? 0).toString(),
    tx.description ?? ''
  ]) ?? [];

  const csv = [
    headers.join(','),
    ...rows.map(row => row.map(cell => `"${cell}"`).join(','))
  ].join('\n');

  return csv;
}

// Usage
const csv = await exportTransactionsCSV(30);
// Save to file or send to user
console.log(csv);

Back to Examples | Previous: Inventory Reports

Made with ❤️ for the Wildberries developer community