Вымышленный мебельный магазин: юнит-экономика артикулов на Ozon и Wildberries
| Продано, шт. | Реклама на единицу, руб. | Доля возвратов | Артикул и канал | Себестоимость на единицу, руб. | Цена за единицу, руб. | Прибыль на единицу, руб. | Логистика на единицу, руб. | Комиссия на единицу, руб. |
|---|---|---|---|---|---|---|---|---|
| 159 | 385,47 ₽ | 11,3 % | Wildberries / OS-503 | 1 952 ₽ | 3 075 ₽ | -180,38 ₽ | 180 ₽ | 738,03 ₽ |
| 86 | 738,78 ₽ | 11,6 % | Ozon / SH-403 | 4 740 ₽ | 7 602 ₽ | 7,18 ₽ | 520 ₽ | 1 597 ₽ |
| 127 | 130,02 ₽ | 12,6 % | Ozon / OS-503 | 1 952 ₽ | 3 065 ₽ | 159,46 ₽ | 180 ₽ | 643,68 ₽ |
| 175 | 0 ₽ | 8 % | Wildberries / KU-303 | 2 842 ₽ | 4 753 ₽ | 450 ₽ | 320 ₽ | 1 141 ₽ |
| 89 | 385,47 ₽ | 9 % | Wildberries / OS-502 | 3 584 ₽ | 6 166 ₽ | 456,53 ₽ | 260 ₽ | 1 480 ₽ |
| 87 | 0 ₽ | 3,5 % | Wildberries / SH-403 | 4 740 ₽ | 7 597 ₽ | 513,55 ₽ | 520 ₽ | 1 823 ₽ |
| 139 | 0 ₽ | 8,6 % | Ozon / KU-303 | 2 842 ₽ | 4 670 ₽ | 527,2 ₽ | 320 ₽ | 980,67 ₽ |
| 67 | 130,02 ₽ | 6 % | Ozon / OS-502 | 3 584 ₽ | 6 141 ₽ | 877,18 ₽ | 260 ₽ | 1 290 ₽ |
| 37 | 738,78 ₽ | 2,7 % | Ozon / SH-402 | 7 095 ₽ | 12 443 ₽ | 1 096 ₽ | 900 ₽ | 2 613 ₽ |
| 34 | 1 233 ₽ | 2,9 % | Ozon / KR-203 | 10 584 ₽ | 18 073 ₽ | 1 261 ₽ | 1 200 ₽ | 3 795 ₽ |
| 59 | 385,47 ₽ | 5,1 % | Wildberries / OS-501 | 5 247 ₽ | 9 553 ₽ | 1 348 ₽ | 280 ₽ | 2 293 ₽ |
| 54 | 0 ₽ | 9,3 % | Wildberries / SH-402 | 7 095 ₽ | 12 461 ₽ | 1 476 ₽ | 900 ₽ | 2 991 ₽ |
| 54 | 130,02 ₽ | 7,4 % | Ozon / OS-501 | 5 247 ₽ | 9 489 ₽ | 1 839 ₽ | 280 ₽ | 1 993 ₽ |
| 21 | 2 133 ₽ | 4,8 % | Wildberries / DV-103 | 21 945 ₽ | 36 938 ₽ | 1 895 ₽ | 2 100 ₽ | 8 865 ₽ |
| 41 | 0 ₽ | 9,8 % | Wildberries / KR-203 | 10 584 ₽ | 18 125 ₽ | 1 991 ₽ | 1 200 ₽ | 4 350 ₽ |
| 25 | 0 ₽ | 12 % | Wildberries / KU-302 | 13 446 ₽ | 23 417 ₽ | 2 851 ₽ | 1 500 ₽ | 5 620 ₽ |
| 35 | 2 133 ₽ | 2,9 % | Wildberries / DV-102 | 23 595 ₽ | 41 187 ₽ | 3 274 ₽ | 2 300 ₽ | 9 885 ₽ |
| 17 | 0 ₽ | 0 % | Ozon / KU-302 | 13 446 ₽ | 23 973 ₽ | 3 993 ₽ | 1 500 ₽ | 5 034 ₽ |
| 23 | 1 233 ₽ | 4,4 % | Ozon / KR-201 | 16 450 ₽ | 31 374 ₽ | 5 303 ₽ | 1 800 ₽ | 6 588 ₽ |
| 15 | 0 ₽ | 6,7 % | Ozon / DV-103 | 21 945 ₽ | 37 218 ₽ | 5 357 ₽ | 2 100 ₽ | 7 816 ₽ |
| 21 | 0 ₽ | 4,8 % | Wildberries / KR-201 | 16 450 ₽ | 31 653 ₽ | 5 806 ₽ | 1 800 ₽ | 7 597 ₽ |
| 26 | 0 ₽ | 3,9 % | Ozon / DV-102 | 23 595 ₽ | 41 557 ₽ | 6 935 ₽ | 2 300 ₽ | 8 727 ₽ |
| 11 | 0 ₽ | 0 % | Wildberries / SH-401 | 23 919 ₽ | 45 024 ₽ | 7 899 ₽ | 2 400 ₽ | 10 806 ₽ |
| 22 | 0 ₽ | 0 % | Wildberries / KR-202 | 20 335 ₽ | 39 881 ₽ | 8 074 ₽ | 1 900 ₽ | 9 571 ₽ |
| 18 | 1 233 ₽ | 0 % | Ozon / KR-202 | 20 335 ₽ | 40 157 ₽ | 8 256 ₽ | 1 900 ₽ | 8 433 ₽ |
| 16 | 738,78 ₽ | 0 % | Ozon / SH-401 | 23 919 ₽ | 45 019 ₽ | 8 507 ₽ | 2 400 ₽ | 9 454 ₽ |
| 12 | 2 133 ₽ | 0 % | Wildberries / DV-101 | 33 748 ₽ | 61 955 ₽ | 8 605 ₽ | 2 600 ₽ | 14 869 ₽ |
| 11 | 0 ₽ | 0 % | Ozon / DV-101 | 33 748 ₽ | 61 817 ₽ | 12 487 ₽ | 2 600 ₽ | 12 982 ₽ |
| 1 | 0 ₽ | 0 % | Ozon / KU-301 | 41 830 ₽ | 86 450 ₽ | 21 665 ₽ | 4 800 ₽ | 18 154 ₽ |
SQL, по которому посчитано
WITH last_month AS (
SELECT date_trunc('month', max(date)) AS month_start
FROM sales
),
sales_agg AS (
SELECT
s.channel,
s.sku,
s.category,
SUM(s.units) AS units,
SUM(s.revenue) AS revenue,
SUM(s.commission) AS commission,
SUM(s.logistics) AS logistics,
SUM(s.cost_of_goods) AS cost_of_goods,
SUM(s.returned_units) AS returned_units
FROM sales s
CROSS JOIN last_month lm
WHERE s.channel IN ('Ozon', 'Wildberries')
AND s.date >= lm.month_start
AND s.date < lm.month_start + INTERVAL 1 MONTH
GROUP BY 1, 2, 3
),
ads_month AS (
SELECT
CASE a.source
WHEN 'Продвижение Ozon' THEN 'Ozon'
WHEN 'Продвижение WB' THEN 'Wildberries'
END AS channel,
a.category,
SUM(a.cost) AS ad_cost
FROM ads a
CROSS JOIN last_month lm
WHERE a.source IN ('Продвижение Ozon', 'Продвижение WB')
AND a.date >= lm.month_start
AND a.date < lm.month_start + INTERVAL 1 MONTH
GROUP BY 1, 2
),
category_units AS (
SELECT channel, category, SUM(units) AS category_units
FROM sales_agg
GROUP BY 1, 2
),
channel_units AS (
SELECT channel, SUM(units) AS channel_units
FROM sales_agg
GROUP BY 1
),
channel_shared_ads AS (
SELECT channel, SUM(ad_cost) AS shared_ad_cost
FROM ads_month
WHERE category = 'Все'
GROUP BY 1
),
ad_alloc AS (
SELECT
cu.channel,
cu.category,
COALESCE(am.ad_cost, 0) AS direct_ad_cost,
COALESCE(csa.shared_ad_cost, 0) * cu.category_units / NULLIF(chu.channel_units, 0) AS shared_ad_cost,
cu.category_units
FROM category_units cu
LEFT JOIN ads_month am
ON am.channel = cu.channel
AND am.category = cu.category
AND am.category <> 'Все'
LEFT JOIN channel_shared_ads csa ON csa.channel = cu.channel
LEFT JOIN channel_units chu ON chu.channel = cu.channel
)
SELECT
sa.channel || ' / ' || sa.sku AS sku_channel,
sa.units AS units,
ROUND(sa.revenue / NULLIF(sa.units, 0), 2) AS price_per_unit,
ROUND(sa.commission / NULLIF(sa.units, 0), 2) AS commission_per_unit,
ROUND(sa.logistics / NULLIF(sa.units, 0), 2) AS logistics_per_unit,
ROUND(sa.cost_of_goods / NULLIF(sa.units, 0), 2) AS cogs_per_unit,
ROUND((aa.direct_ad_cost + aa.shared_ad_cost) / NULLIF(aa.category_units, 0), 2) AS ad_per_unit,
ROUND(
(sa.revenue - sa.commission - sa.logistics - sa.cost_of_goods) / NULLIF(sa.units, 0)
- (aa.direct_ad_cost + aa.shared_ad_cost) / NULLIF(aa.category_units, 0),
2
) AS profit_per_unit,
ROUND(sa.returned_units::DOUBLE / NULLIF(sa.units, 0), 4) AS return_rate
FROM sales_agg sa
JOIN ad_alloc aa
ON aa.channel = sa.channel
AND aa.category = sa.category
WHERE sa.units > 0
ORDER BY profit_per_unit ASC
LIMIT 30