Digital API
Digital API
API Π΄Π»Ρ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ ΡΠΈΡΡΠΎΠ²ΡΠΌΠΈ ΡΠΎΠ²Π°ΡΠ°ΠΌΠΈ Π² OZON Seller API.
ΠΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΠΌΠ΅ΡΠΎΠ΄ΠΎΠ²: 4 ΠΌΠ΅ΡΠΎΠ΄Π° (1 Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡΠ΅Π»ΡΠ½ΡΠΉ Π΄Π»Ρ ΡΠΎΠ²ΠΌΠ΅ΡΡΠΈΠΌΠΎΡΡΠΈ)
ΠΠ±Π·ΠΎΡ
Digital API ΠΏΡΠ΅Π΄ΠΎΡΡΠ°Π²Π»ΡΠ΅Ρ ΡΡΠ½ΠΊΡΠΈΠΎΠ½Π°Π»ΡΠ½ΠΎΡΡΡ Π΄Π»Ρ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ ΡΠΈΡΡΠΎΠ²ΡΠΌΠΈ ΡΠΎΠ²Π°ΡΠ°ΠΌΠΈ:
- π¦ ΠΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΡΠΏΠΈΡΠΊΠ° ΠΎΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠΉ Ρ ΡΠΈΡΡΠΎΠ²ΡΠΌΠΈ ΡΠΎΠ²Π°ΡΠ°ΠΌΠΈ
- π ΠΠ°Π³ΡΡΠ·ΠΊΠ° ΠΊΠΎΠ΄ΠΎΠ² ΡΠΈΡΡΠΎΠ²ΡΡ ΡΠΎΠ²Π°ΡΠΎΠ² (Π»ΠΈΡΠ΅Π½Π·ΠΈΠΈ, ΠΊΠ»ΡΡΠΈ Π°ΠΊΡΠΈΠ²Π°ΡΠΈΠΈ)
- π ΠΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ ΠΎΡΡΠ°ΡΠΊΠΎΠ² ΡΠΈΡΡΠΎΠ²ΡΡ ΡΠΎΠ²Π°ΡΠΎΠ²
- β° Π‘ΠΎΠ±Π»ΡΠ΄Π΅Π½ΠΈΠ΅ Π΄Π΅Π΄Π»Π°ΠΉΠ½ΠΎΠ² Π·Π°Π³ΡΡΠ·ΠΊΠΈ ΠΊΠΎΠ΄ΠΎΠ² (24 ΡΠ°ΡΠ°)
ΠΡΠ½ΠΎΠ²Π½ΡΠ΅ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠΈ
π― Π£ΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ ΠΎΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡΠΌΠΈ
- ΠΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΡΠΏΠΈΡΠΊΠ° ΠΎΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠΉ, ΡΡΠ΅Π±ΡΡΡΠΈΡ Π·Π°Π³ΡΡΠ·ΠΊΠΈ ΠΊΠΎΠ΄ΠΎΠ²
- Π€ΠΈΠ»ΡΡΡΠ°ΡΠΈΡ ΠΏΠΎ Π΄Π°ΡΠ΅ ΠΈ ΡΡΠ°ΡΡΡΡ
- ΠΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ Π°Π½Π°Π»ΠΈΡΠΈΡΠ΅ΡΠΊΠΈΡ ΠΈ ΡΠΈΠ½Π°Π½ΡΠΎΠ²ΡΡ Π΄Π°Π½Π½ΡΡ
π Π£ΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ ΠΊΠΎΠ΄Π°ΠΌΠΈ
- ΠΠ°Π³ΡΡΠ·ΠΊΠ° ΡΠΈΡΡΠΎΠ²ΡΡ ΠΊΠΎΠ΄ΠΎΠ² (Π»ΠΈΡΠ΅Π½Π·ΠΈΠΈ, ΠΊΠ»ΡΡΠΈ, ΠΊΠΎΠ΄Ρ Π°ΠΊΡΠΈΠ²Π°ΡΠΈΠΈ)
- ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° Π½Π΅Π΄ΠΎΡΡΡΠΏΠ½ΡΡ ΠΊΠΎΠ΄ΠΎΠ²
- ΠΠΎΠ½ΡΡΠΎΠ»Ρ ΠΊΠ°ΡΠ΅ΡΡΠ²Π° Π·Π°Π³ΡΡΠΆΠ΅Π½Π½ΡΡ ΠΊΠΎΠ΄ΠΎΠ²
π¦ Π£ΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ ΠΎΡΡΠ°ΡΠΊΠ°ΠΌΠΈ
- ΠΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ ΠΎ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²Π΅ Π΄ΠΎΡΡΡΠΏΠ½ΡΡ ΡΠΈΡΡΠΎΠ²ΡΡ ΡΠΎΠ²Π°ΡΠΎΠ²
- ΠΠ°ΠΊΠ΅ΡΠ½ΡΠ΅ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΡ ΠΎΡΡΠ°ΡΠΊΠΎΠ²
- ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΠΎΡΠΈΠ±ΠΎΠΊ Π²Π°Π»ΠΈΠ΄Π°ΡΠΈΠΈ
β οΈ ΠΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΡ
- ΠΠΎΡΡΡΠΏΠ½ΠΎ ΡΠΎΠ»ΡΠΊΠΎ ΠΏΡΠΎΠ΄Π°Π²ΡΠ°ΠΌ, ΡΠ°Π±ΠΎΡΠ°ΡΡΠΈΠΌ Ρ ΡΠΈΡΡΠΎΠ²ΡΠΌΠΈ ΡΠΎΠ²Π°ΡΠ°ΠΌΠΈ
- ΠΠΎΠ΄Ρ Π΄ΠΎΠ»ΠΆΠ½Ρ Π±ΡΡΡ Π·Π°Π³ΡΡΠΆΠ΅Π½Ρ Π² ΡΠ΅ΡΠ΅Π½ΠΈΠ΅ 24 ΡΠ°ΡΠΎΠ² Ρ ΠΌΠΎΠΌΠ΅Π½ΡΠ° ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΡ Π·Π°ΠΊΠ°Π·Π°
- Π Π΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡΠ΅ΠΌΡΠΉ ΡΠ°Π·ΠΌΠ΅Ρ Π±Π°ΡΡΠ° Π΄Π»Ρ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΡ ΠΎΡΡΠ°ΡΠΊΠΎΠ²: 100 ΡΠΎΠ²Π°ΡΠΎΠ²
ΠΠ΅ΡΠΎΠ΄Ρ API
getDigitalPostingsList()
ΠΠ°Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅: ΠΠΎΠ»ΡΡΠΈΡΡ ΡΠΏΠΈΡΠΎΠΊ ΠΎΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠΉ Ρ ΡΠΈΡΡΠΎΠ²ΡΠΌΠΈ ΡΠΎΠ²Π°ΡΠ°ΠΌΠΈ
interface DigitalListPostingCodesRequest {
dir?: 'ASC' | 'DESC';
filter?: {
since?: string;
to?: string;
cutoff_from?: string;
cutoff_to?: string;
};
limit?: number;
offset?: number;
with?: {
financial_data?: boolean;
analytics_data?: boolean;
legal_info?: boolean;
};
}
uploadDigitalCodes()
ΠΠ°Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅: ΠΠ°Π³ΡΡΠ·ΠΈΡΡ ΠΊΠΎΠ΄Ρ ΡΠΈΡΡΠΎΠ²ΡΡ ΡΠΎΠ²Π°ΡΠΎΠ² Π΄Π»Ρ ΠΎΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ
interface DigitalUploadPostingCodesRequest {
posting_number: string;
exemplars_by_sku: Array<{
sku: number;
exemplar_qty: number;
not_available_exemplar_qty: number;
exemplar_keys: string[];
}>;
}
updateDigitalStocks()
ΠΠ°Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅: ΠΠ±Π½ΠΎΠ²ΠΈΡΡ ΠΎΡΡΠ°ΡΠΊΠΈ ΡΠΈΡΡΠΎΠ²ΡΡ ΡΠΎΠ²Π°ΡΠΎΠ²
interface DigitalStocksImportRequest {
stocks: Array<{
offer_id: string;
stock: number;
}>;
}
ΠΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈΠ΅ ΠΏΡΠΈΠΌΠ΅ΡΡ
ΠΠ°Π·ΠΎΠ²ΠΎΠ΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅
import { OzonSellerAPI } from 'bmad-ozon-seller-api';
const api = new OzonSellerAPI({
clientId: 'your-client-id',
apiKey: 'your-api-key'
});
// ΠΠΎΠ»ΡΡΠΈΡΡ ΠΎΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ, ΠΎΠΆΠΈΠ΄Π°ΡΡΠΈΠ΅ Π·Π°Π³ΡΡΠ·ΠΊΠΈ ΠΊΠΎΠ΄ΠΎΠ²
const weekAgo = new Date();
weekAgo.setDate(weekAgo.getDate() - 7);
const digitalPostings = await api.digital.getDigitalPostingsList({
filter: {
since: weekAgo.toISOString().split('T')[0],
to: new Date().toISOString().split('T')[0]
},
limit: 100,
with: {
financial_data: true,
analytics_data: true
}
});
// ΠΠ°Π³ΡΡΠ·ΠΈΡΡ ΠΊΠΎΠ΄Ρ Π΄Π»Ρ ΠΎΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ
await api.digital.uploadDigitalCodes({
posting_number: '12345-0001-1',
exemplars_by_sku: [{
sku: 123456789,
exemplar_qty: 3,
not_available_exemplar_qty: 0,
exemplar_keys: [
'GAME_KEY_001_ABC123',
'GAME_KEY_002_DEF456',
'GAME_KEY_003_GHI789'
]
}]
});
// ΠΠ±Π½ΠΎΠ²ΠΈΡΡ ΠΎΡΡΠ°ΡΠΊΠΈ ΡΠΈΡΡΠΎΠ²ΡΡ
ΡΠΎΠ²Π°ΡΠΎΠ²
await api.digital.updateDigitalStocks({
stocks: [
{
offer_id: 'GAME_DIGITAL_001',
stock: 50
},
{
offer_id: 'SOFTWARE_LICENSE_002',
stock: 25
}
]
});
ΠΡΠΎΠ΄Π²ΠΈΠ½ΡΡΡΠ΅ ΡΡΠ΅Π½Π°ΡΠΈΠΈ
ΠΠ΅Π½Π΅Π΄ΠΆΠ΅Ρ ΡΠΈΡΡΠΎΠ²ΡΡ Π·Π°ΠΊΠ°Π·ΠΎΠ²
class DigitalOrderManager {
constructor(private api: OzonSellerAPI) {}
async processDigitalOrders(): Promise<void> {
// ΠΠΎΠ»ΡΡΠΈΡΡ Π²ΡΠ΅ Π·Π°ΠΊΠ°Π·Ρ, ΠΎΠΆΠΈΠ΄Π°ΡΡΠΈΠ΅ Π·Π°Π³ΡΡΠ·ΠΊΠΈ ΠΊΠΎΠ΄ΠΎΠ²
const pendingOrders = await this.api.digital.getDigitalPostingsList({
filter: {
since: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0]
},
limit: 1000,
with: {
analytics_data: true,
financial_data: true
}
});
const ordersNeedingCodes = pendingOrders.result?.filter(posting =>
posting.status === 'awaiting_packaging' &&
posting.products?.some(product => product.required_qty_for_digital_code > 0)
) || [];
console.log(`ΠΠ°ΠΉΠ΄Π΅Π½ΠΎ ${ordersNeedingCodes.length} Π·Π°ΠΊΠ°Π·ΠΎΠ², ΡΡΠ΅Π±ΡΡΡΠΈΡ
Π·Π°Π³ΡΡΠ·ΠΊΠΈ ΠΊΠΎΠ΄ΠΎΠ²`);
for (const order of ordersNeedingCodes) {
await this.uploadCodesForOrder(order);
// ΠΠ°Π΄Π΅ΡΠΆΠΊΠ° ΠΌΠ΅ΠΆΠ΄Ρ ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠΎΠΉ Π·Π°ΠΊΠ°Π·ΠΎΠ²
await new Promise(resolve => setTimeout(resolve, 500));
}
}
private async uploadCodesForOrder(order: any): Promise<void> {
try {
const codesByProduct = [];
for (const product of order.products || []) {
if (product.required_qty_for_digital_code > 0) {
const codes = await this.generateCodesForProduct(
product.sku,
product.required_qty_for_digital_code
);
codesByProduct.push({
sku: product.sku,
exemplar_qty: codes.length,
not_available_exemplar_qty: 0,
exemplar_keys: codes
});
}
}
if (codesByProduct.length > 0) {
const result = await this.api.digital.uploadDigitalCodes({
posting_number: order.posting_number,
exemplars_by_sku: codesByProduct
});
this.validateUploadResults(order.posting_number, result);
}
} catch (error) {
console.error(`ΠΡΠΈΠ±ΠΊΠ° ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠΈ Π·Π°ΠΊΠ°Π·Π° ${order.posting_number}:`, error);
}
}
private async generateCodesForProduct(sku: number, quantity: number): Promise<string[]> {
// ΠΠΎΠ³ΠΈΠΊΠ° Π³Π΅Π½Π΅ΡΠ°ΡΠΈΠΈ ΠΈΠ»ΠΈ ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΡ ΠΊΠΎΠ΄ΠΎΠ² ΠΈΠ· Π²Π½Π΅ΡΠ½Π΅ΠΉ ΡΠΈΡΡΠ΅ΠΌΡ
const codes: string[] = [];
for (let i = 0; i < quantity; i++) {
codes.push(`${sku}_KEY_${Date.now()}_${Math.random().toString(36).substring(7).toUpperCase()}`);
}
return codes;
}
private validateUploadResults(postingNumber: string, result: any): void {
let totalReceived = 0;
let totalRejected = 0;
result.exemplars_by_sku?.forEach((skuResult: any) => {
totalReceived += skuResult.received_qty || 0;
totalRejected += skuResult.rejected_qty || 0;
if (skuResult.failed_exemplars?.length > 0) {
console.warn(`ΠΡΠΊΠ»ΠΎΠ½Π΅Π½Π½ΡΠ΅ ΠΊΠΎΠ΄Ρ Π΄Π»Ρ SKU ${skuResult.sku} Π² Π·Π°ΠΊΠ°Π·Π΅ ${postingNumber}:`);
skuResult.failed_exemplars.forEach((error: any) => {
console.warn(` ${error.code}: ${error.message}`);
});
}
});
console.log(`ΠΠ°ΠΊΠ°Π· ${postingNumber}: ΠΏΡΠΈΠ½ΡΡΠΎ ΠΊΠΎΠ΄ΠΎΠ² ${totalReceived}, ΠΎΡΠΊΠ»ΠΎΠ½Π΅Π½ΠΎ ${totalRejected}`);
}
}
Π‘ΠΈΡΡΠ΅ΠΌΠ° ΠΌΠΎΠ½ΠΈΡΠΎΡΠΈΠ½Π³Π° ΠΎΡΡΠ°ΡΠΊΠΎΠ²
class DigitalStockMonitor {
constructor(
private api: OzonSellerAPI,
private lowStockThreshold: number = 10
) {}
async monitorAndUpdateStocks(): Promise<void> {
// ΠΠΎΠ»ΡΡΠΈΡΡ ΡΠ΅ΠΊΡΡΠΈΠ΅ ΠΎΡΡΠ°ΡΠΊΠΈ ΠΈΠ· Π²Π½Π΅ΡΠ½Π΅ΠΉ ΡΠΈΡΡΠ΅ΠΌΡ
const stockData = await this.getExternalStockData();
// ΠΠ°ΠΉΡΠΈ ΡΠΎΠ²Π°ΡΡ Ρ Π½ΠΈΠ·ΠΊΠΈΠΌΠΈ ΠΎΡΡΠ°ΡΠΊΠ°ΠΌΠΈ
const lowStockItems = stockData.filter(item => item.availableStock <= this.lowStockThreshold);
if (lowStockItems.length > 0) {
console.warn(`ΠΠ±Π½Π°ΡΡΠΆΠ΅Π½ΠΎ ${lowStockItems.length} ΡΠΎΠ²Π°ΡΠΎΠ² Ρ Π½ΠΈΠ·ΠΊΠΈΠΌΠΈ ΠΎΡΡΠ°ΡΠΊΠ°ΠΌΠΈ`);
await this.sendLowStockAlert(lowStockItems);
}
// ΠΠ±Π½ΠΎΠ²ΠΈΡΡ ΠΎΡΡΠ°ΡΠΊΠΈ Π² OZON
await this.updateStocksBatch(stockData);
// ΠΠ΅Π½Π΅ΡΠ°ΡΠΈΡ ΠΎΡΡΠ΅ΡΠ° ΠΏΠΎ ΠΎΡΡΠ°ΡΠΊΠ°ΠΌ
await this.generateStockReport(stockData);
}
private async updateStocksBatch(stockData: any[]): Promise<void> {
const batchSize = 100;
let successCount = 0;
let errorCount = 0;
for (let i = 0; i < stockData.length; i += batchSize) {
const batch = stockData.slice(i, i + batchSize);
try {
const result = await this.api.digital.updateDigitalStocks({
stocks: batch.map(item => ({
offer_id: item.offerId,
stock: item.availableStock
}))
});
// ΠΠΎΠ΄ΡΡΠ΅Ρ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠΎΠ²
result.status?.forEach(status => {
if (status.updated) {
successCount++;
} else {
errorCount++;
console.error(`ΠΡΠΈΠ±ΠΊΠ° ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΡ ${status.offer_id}:`, status.errors);
}
});
console.log(`ΠΠ±ΡΠ°Π±ΠΎΡΠ°Π½ Π±Π°ΡΡ ${Math.floor(i / batchSize) + 1}/${Math.ceil(stockData.length / batchSize)}`);
// ΠΠ°Π΄Π΅ΡΠΆΠΊΠ° ΠΌΠ΅ΠΆΠ΄Ρ Π±Π°ΡΡΠ°ΠΌΠΈ
if (i + batchSize < stockData.length) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
} catch (error) {
console.error(`ΠΡΠΈΠ±ΠΊΠ° ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΡ Π±Π°ΡΡΠ°:`, error);
errorCount += batch.length;
}
}
console.log(`ΠΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ ΠΎΡΡΠ°ΡΠΊΠΎΠ² Π·Π°Π²Π΅ΡΡΠ΅Π½ΠΎ: ΡΡΠΏΠ΅ΡΠ½ΠΎ ${successCount}, ΠΎΡΠΈΠ±ΠΎΠΊ ${errorCount}`);
}
private async getExternalStockData(): Promise<any[]> {
// ΠΠΌΡΠ»ΡΡΠΈΡ ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΡ Π΄Π°Π½Π½ΡΡ
ΠΈΠ· Π²Π½Π΅ΡΠ½Π΅ΠΉ ΡΠΈΡΡΠ΅ΠΌΡ
return [
{ offerId: 'GAME_DIGITAL_001', availableStock: 50 },
{ offerId: 'SOFTWARE_LICENSE_002', availableStock: 5 }, // ΠΠΈΠ·ΠΊΠΈΠΉ ΠΎΡΡΠ°ΡΠΎΠΊ
{ offerId: 'DIGITAL_BOOK_003', availableStock: 100 },
{ offerId: 'MUSIC_ALBUM_004', availableStock: 2 }, // ΠΡΠΈΡΠΈΡΠ΅ΡΠΊΠΈ Π½ΠΈΠ·ΠΊΠΈΠΉ ΠΎΡΡΠ°ΡΠΎΠΊ
];
}
private async sendLowStockAlert(lowStockItems: any[]): Promise<void> {
// ΠΠΎΠ³ΠΈΠΊΠ° ΠΎΡΠΏΡΠ°Π²ΠΊΠΈ ΡΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠΉ ΠΎ Π½ΠΈΠ·ΠΊΠΈΡ
ΠΎΡΡΠ°ΡΠΊΠ°Ρ
console.log('π¨ Π£Π²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠ΅ ΠΎ Π½ΠΈΠ·ΠΊΠΈΡ
ΠΎΡΡΠ°ΡΠΊΠ°Ρ
:');
lowStockItems.forEach(item => {
const severity = item.availableStock <= 5 ? 'π΄ ΠΠ ΠΠ’ΠΠ§ΠΠ' : 'π‘ ΠΠΠΠΠΠΠΠ';
console.log(`${severity} ${item.offerId}: ${item.availableStock} ΡΡ.`);
});
}
private async generateStockReport(stockData: any[]): Promise<void> {
const totalItems = stockData.length;
const totalStock = stockData.reduce((sum, item) => sum + item.availableStock, 0);
const avgStock = Math.round(totalStock / totalItems);
const lowStockCount = stockData.filter(item => item.availableStock <= this.lowStockThreshold).length;
console.log('\nπ ΠΡΡΠ΅Ρ ΠΏΠΎ ΠΎΡΡΠ°ΡΠΊΠ°ΠΌ ΡΠΈΡΡΠΎΠ²ΡΡ
ΡΠΎΠ²Π°ΡΠΎΠ²:');
console.log(`ΠΡΠ΅Π³ΠΎ ΡΠΎΠ²Π°ΡΠΎΠ²: ${totalItems}`);
console.log(`ΠΠ±ΡΠΈΠΉ ΠΎΡΡΠ°ΡΠΎΠΊ: ${totalStock} ΡΡ.`);
console.log(`Π‘ΡΠ΅Π΄Π½ΠΈΠΉ ΠΎΡΡΠ°ΡΠΎΠΊ: ${avgStock} ΡΡ.`);
console.log(`Π’ΠΎΠ²Π°ΡΠΎΠ² Ρ Π½ΠΈΠ·ΠΊΠΈΠΌ ΠΎΡΡΠ°ΡΠΊΠΎΠΌ: ${lowStockCount} (${Math.round(lowStockCount / totalItems * 100)}%)`);
}
}
ΠΠ½Π°Π»ΠΈΡΠΈΡΠ΅ΡΠΊΠ°Ρ ΡΠΈΡΡΠ΅ΠΌΠ° ΠΎΡΡΠ΅ΡΠ½ΠΎΡΡΠΈ
class DigitalAnalyticsReporter {
constructor(private api: OzonSellerAPI) {}
async generateDigitalSalesReport(dateFrom: string, dateTo: string): Promise<void> {
const digitalPostings = await this.api.digital.getDigitalPostingsList({
filter: {
since: dateFrom,
to: dateTo
},
limit: 1000,
with: {
financial_data: true,
analytics_data: true
}
});
const analytics = this.analyzeDigitalSales(digitalPostings.result || []);
this.generateReport(analytics, dateFrom, dateTo);
}
private analyzeDigitalSales(postings: any[]): any {
const analytics = {
totalOrders: postings.length,
totalRevenue: 0,
totalCommission: 0,
productStats: new Map(),
regionStats: new Map(),
statusStats: new Map(),
codeUploadStats: {
totalRequired: 0,
uploaded: 0,
pending: 0
}
};
postings.forEach(posting => {
// Π€ΠΈΠ½Π°Π½ΡΠΎΠ²Π°Ρ Π°Π½Π°Π»ΠΈΡΠΈΠΊΠ°
if (posting.financial_data) {
analytics.totalRevenue += posting.financial_data.order_amount || 0;
analytics.totalCommission += posting.financial_data.commission || 0;
}
// Π‘ΡΠ°ΡΠΈΡΡΠΈΠΊΠ° ΡΡΠ°ΡΡΡΠΎΠ²
const currentCount = analytics.statusStats.get(posting.status) || 0;
analytics.statusStats.set(posting.status, currentCount + 1);
// Π Π΅Π³ΠΈΠΎΠ½Π°Π»ΡΠ½Π°Ρ Π°Π½Π°Π»ΠΈΡΠΈΠΊΠ°
if (posting.analytics_data?.region) {
const regionCount = analytics.regionStats.get(posting.analytics_data.region) || 0;
analytics.regionStats.set(posting.analytics_data.region, regionCount + 1);
}
// ΠΠ½Π°Π»ΠΈΡΠΈΠΊΠ° ΠΏΠΎ ΠΏΡΠΎΠ΄ΡΠΊΡΠ°ΠΌ ΠΈ ΠΊΠΎΠ΄Π°ΠΌ
posting.products?.forEach((product: any) => {
const sku = product.sku.toString();
if (!analytics.productStats.has(sku)) {
analytics.productStats.set(sku, {
name: product.name,
totalSold: 0,
totalRevenue: 0,
codesRequired: 0
});
}
const productStat = analytics.productStats.get(sku);
productStat.totalSold += product.quantity;
productStat.totalRevenue += (product.price || 0) * product.quantity;
productStat.codesRequired += product.required_qty_for_digital_code || 0;
analytics.codeUploadStats.totalRequired += product.required_qty_for_digital_code || 0;
if (product.required_qty_for_digital_code > 0 && posting.status === 'awaiting_packaging') {
analytics.codeUploadStats.pending += product.required_qty_for_digital_code;
} else if (product.required_qty_for_digital_code > 0) {
analytics.codeUploadStats.uploaded += product.required_qty_for_digital_code;
}
});
});
return analytics;
}
private generateReport(analytics: any, dateFrom: string, dateTo: string): void {
console.log(`\nπ ΠΡΡΠ΅Ρ ΠΏΠΎ ΡΠΈΡΡΠΎΠ²ΡΠΌ ΡΠΎΠ²Π°ΡΠ°ΠΌ (${dateFrom} - ${dateTo})`);
console.log('='.repeat(60));
// ΠΠ±ΡΠ°Ρ ΡΡΠ°ΡΠΈΡΡΠΈΠΊΠ°
console.log('\nπ ΠΠ±ΡΠ°Ρ ΡΡΠ°ΡΠΈΡΡΠΈΠΊΠ°:');
console.log(`ΠΡΠ΅Π³ΠΎ Π·Π°ΠΊΠ°Π·ΠΎΠ²: ${analytics.totalOrders}`);
console.log(`ΠΠ±ΡΠ°Ρ Π²ΡΡΡΡΠΊΠ°: ${analytics.totalRevenue.toFixed(2)} ΡΡΠ±.`);
console.log(`ΠΠ±ΡΠ°Ρ ΠΊΠΎΠΌΠΈΡΡΠΈΡ: ${analytics.totalCommission.toFixed(2)} ΡΡΠ±.`);
console.log(`Π§ΠΈΡΡΠ°Ρ ΠΏΡΠΈΠ±ΡΠ»Ρ: ${(analytics.totalRevenue - analytics.totalCommission).toFixed(2)} ΡΡΠ±.`);
console.log(`Π‘ΡΠ΅Π΄Π½ΠΈΠΉ ΡΠ΅ΠΊ: ${(analytics.totalRevenue / analytics.totalOrders).toFixed(2)} ΡΡΠ±.`);
// Π‘ΡΠ°ΡΠΈΡΡΠΈΠΊΠ° ΠΏΠΎ ΠΊΠΎΠ΄Π°ΠΌ
console.log('\nπ Π‘ΡΠ°ΡΠΈΡΡΠΈΠΊΠ° ΠΏΠΎ ΠΊΠΎΠ΄Π°ΠΌ:');
console.log(`ΠΡΠ΅Π³ΠΎ ΡΡΠ΅Π±ΡΠ΅ΡΡΡ ΠΊΠΎΠ΄ΠΎΠ²: ${analytics.codeUploadStats.totalRequired}`);
console.log(`ΠΠ°Π³ΡΡΠΆΠ΅Π½ΠΎ ΠΊΠΎΠ΄ΠΎΠ²: ${analytics.codeUploadStats.uploaded}`);
console.log(`ΠΠΆΠΈΠ΄Π°Π΅Ρ Π·Π°Π³ΡΡΠ·ΠΊΠΈ: ${analytics.codeUploadStats.pending}`);
if (analytics.codeUploadStats.totalRequired > 0) {
const uploadRate = (analytics.codeUploadStats.uploaded / analytics.codeUploadStats.totalRequired * 100).toFixed(1);
console.log(`ΠΡΠΎΡΠ΅Π½Ρ Π·Π°Π³ΡΡΠ·ΠΊΠΈ: ${uploadRate}%`);
}
// Π’ΠΎΠΏ ΡΠΎΠ²Π°ΡΡ
console.log('\nπ Π’ΠΎΠΏ-5 ΡΠΎΠ²Π°ΡΠΎΠ² ΠΏΠΎ ΠΏΡΠΎΠ΄Π°ΠΆΠ°ΠΌ:');
const topProducts = Array.from(analytics.productStats.entries())
.sort(([,a], [,b]) => b.totalRevenue - a.totalRevenue)
.slice(0, 5);
topProducts.forEach(([sku, stats], index) => {
console.log(`${index + 1}. ${stats.name} (SKU: ${sku})`);
console.log(` ΠΡΠΎΠ΄Π°Π½ΠΎ: ${stats.totalSold} ΡΡ., ΠΡΡΡΡΠΊΠ°: ${stats.totalRevenue.toFixed(2)} ΡΡΠ±.`);
});
// Π‘ΡΠ°ΡΠΈΡΡΠΈΠΊΠ° ΠΏΠΎ ΡΠ΅Π³ΠΈΠΎΠ½Π°ΠΌ
if (analytics.regionStats.size > 0) {
console.log('\nπ Π’ΠΎΠΏ-5 ΡΠ΅Π³ΠΈΠΎΠ½ΠΎΠ²:');
const topRegions = Array.from(analytics.regionStats.entries())
.sort(([,a], [,b]) => b - a)
.slice(0, 5);
topRegions.forEach(([region, count], index) => {
const percentage = (count / analytics.totalOrders * 100).toFixed(1);
console.log(`${index + 1}. ${region}: ${count} Π·Π°ΠΊΠ°Π·ΠΎΠ² (${percentage}%)`);
});
}
// Π‘ΡΠ°ΡΠΈΡΡΠΈΠΊΠ° ΠΏΠΎ ΡΡΠ°ΡΡΡΠ°ΠΌ
console.log('\nπ¦ Π‘ΡΠ°ΡΡΡΡ Π·Π°ΠΊΠ°Π·ΠΎΠ²:');
Array.from(analytics.statusStats.entries()).forEach(([status, count]) => {
const percentage = (count / analytics.totalOrders * 100).toFixed(1);
console.log(`${status}: ${count} Π·Π°ΠΊΠ°Π·ΠΎΠ² (${percentage}%)`);
});
}
}
ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΠΎΡΠΈΠ±ΠΎΠΊ
try {
await api.digital.uploadDigitalCodes({
posting_number: '12345-0001-1',
exemplars_by_sku: [/* ... */]
});
} catch (error) {
if (error.response?.status === 400) {
console.error('ΠΡΠΈΠ±ΠΊΠ° Π²Π°Π»ΠΈΠ΄Π°ΡΠΈΠΈ Π΄Π°Π½Π½ΡΡ
:', error.response.data);
} else if (error.response?.status === 403) {
console.error('ΠΠ΅Ρ Π΄ΠΎΡΡΡΠΏΠ° ΠΊ ΡΠΈΡΡΠΎΠ²ΡΠΌ ΡΠΎΠ²Π°ΡΠ°ΠΌ');
} else if (error.response?.status === 404) {
console.error('ΠΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½ΠΎ');
} else {
console.error('ΠΠ΅ΠΎΠΆΠΈΠ΄Π°Π½Π½Π°Ρ ΠΎΡΠΈΠ±ΠΊΠ°:', error.message);
}
}
Π Π΅ΠΊΠΎΠΌΠ΅Π½Π΄Π°ΡΠΈΠΈ ΠΏΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ
π― ΠΡΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΡ
- ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ ΠΏΠ°ΠΊΠ΅ΡΠ½ΡΠ΅ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΡ ΠΎΡΡΠ°ΡΠΊΠΎΠ² (Π΄ΠΎ 100 ΡΠΎΠ²Π°ΡΠΎΠ² Π·Π° Π·Π°ΠΏΡΠΎΡ)
- ΠΠΎΠ±Π°Π²Π»ΡΠΉΡΠ΅ Π·Π°Π΄Π΅ΡΠΆΠΊΠΈ ΠΌΠ΅ΠΆΠ΄Ρ Π·Π°ΠΏΡΠΎΡΠ°ΠΌΠΈ Π΄Π»Ρ ΠΈΠ·Π±Π΅ΠΆΠ°Π½ΠΈΡ rate limiting
- ΠΠ΅ΡΠΈΡΡΠΉΡΠ΅ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΡ ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΡ ΡΠΏΠΈΡΠΊΠ° ΠΎΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠΉ
π ΠΠ΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎΡΡΡ
- Π₯ΡΠ°Π½ΠΈΡΠ΅ ΡΠΈΡΡΠΎΠ²ΡΠ΅ ΠΊΠΎΠ΄Ρ Π² Π·Π°ΡΠΈΡΡΠΎΠ²Π°Π½Π½ΠΎΠΌ Π²ΠΈΠ΄Π΅
- ΠΠΎΠ³ΠΈΡΡΠΉΡΠ΅ Π²ΡΠ΅ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΈ Ρ ΠΊΠΎΠ΄Π°ΠΌΠΈ Π΄Π»Ρ Π°ΡΠ΄ΠΈΡΠ°
- ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ ΡΠ½ΠΈΠΊΠ°Π»ΡΠ½ΡΠ΅ ΠΈ ΡΠ»ΠΎΠΆΠ½ΡΠ΅ ΠΊΠΎΠ΄Ρ Π΄Π»Ρ ΠΏΡΠ΅Π΄ΠΎΡΠ²ΡΠ°ΡΠ΅Π½ΠΈΡ ΠΌΠΎΡΠ΅Π½Π½ΠΈΡΠ΅ΡΡΠ²Π°
π ΠΠΎΠ½ΠΈΡΠΎΡΠΈΠ½Π³
- ΠΡΡΠ»Π΅ΠΆΠΈΠ²Π°ΠΉΡΠ΅ Π΄Π΅Π΄Π»Π°ΠΉΠ½Ρ Π·Π°Π³ΡΡΠ·ΠΊΠΈ ΠΊΠΎΠ΄ΠΎΠ² (24 ΡΠ°ΡΠ°)
- ΠΠΎΠ½ΠΈΡΠΎΡΡΡΠ΅ Π½ΠΈΠ·ΠΊΠΈΠ΅ ΠΎΡΡΠ°ΡΠΊΠΈ ΡΠΈΡΡΠΎΠ²ΡΡ ΡΠΎΠ²Π°ΡΠΎΠ²
- ΠΠ½Π°Π»ΠΈΠ·ΠΈΡΡΠΉΡΠ΅ ΡΡΠ°ΡΠΈΡΡΠΈΠΊΡ ΠΎΡΠΊΠ»ΠΎΠ½Π΅Π½Π½ΡΡ ΠΊΠΎΠ΄ΠΎΠ²
π ΠΠ²ΡΠΎΠΌΠ°ΡΠΈΠ·Π°ΡΠΈΡ
- ΠΠ²ΡΠΎΠΌΠ°ΡΠΈΠ·ΠΈΡΡΠΉΡΠ΅ ΠΏΡΠΎΡΠ΅ΡΡ Π³Π΅Π½Π΅ΡΠ°ΡΠΈΠΈ ΠΈ Π·Π°Π³ΡΡΠ·ΠΊΠΈ ΠΊΠΎΠ΄ΠΎΠ²
- ΠΠ°ΡΡΡΠΎΠΉΡΠ΅ ΡΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΡ ΠΎ Π½ΠΈΠ·ΠΊΠΈΡ ΠΎΡΡΠ°ΡΠΊΠ°Ρ
- ΠΠ½ΡΠ΅Π³ΡΠΈΡΡΠΉΡΠ΅ Ρ Π²Π½Π΅ΡΠ½ΠΈΠΌΠΈ ΡΠΈΡΡΠ΅ΠΌΠ°ΠΌΠΈ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ Π»ΠΈΡΠ΅Π½Π·ΠΈΡΠΌΠΈ
Digital API ΠΏΡΠ΅Π΄ΠΎΡΡΠ°Π²Π»ΡΠ΅Ρ ΠΏΠΎΠ»Π½ΡΠΉ ΠΊΠΎΠ½ΡΡΠΎΠ»Ρ Π½Π°Π΄ ΡΠΈΡΡΠΎΠ²ΡΠΌΠΈ ΡΠΎΠ²Π°ΡΠ°ΠΌΠΈ, ΠΎΡ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ ΠΎΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡΠΌΠΈ Π΄ΠΎ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΡ ΠΎΡΡΠ°ΡΠΊΠΎΠ², ΠΎΠ±Π΅ΡΠΏΠ΅ΡΠΈΠ²Π°Ρ ΡΡΡΠ΅ΠΊΡΠΈΠ²Π½ΡΡ ΡΠ°Π±ΠΎΡΡ Ρ ΡΠΈΡΡΠΎΠ²ΡΠΌ ΠΊΠΎΠ½ΡΠ΅Π½ΡΠΎΠΌ Π½Π° ΠΏΠ»Π°ΡΡΠΎΡΠΌΠ΅ OZON.