AI Asist
v2
Д
Д

Плейбук: управленческий расчёт и бухгалтерский контроль УСН 6% по магазинам

← Заметки  ·  Раздел: Taxes  ·  Файл: data/notes/taxes/monthly-usn-playbook.md

Плейбук: управленческий расчёт и бухгалтерский контроль УСН 6% по магазинам

Актуализировано: 16.08.2026.

Важно: реализованный в системе режим buyer_cash — управленческий резерв владельца, а не автоматически готовая налоговая база для декларации. По позиции ФНС компенсация собственной скидки маркетплейса может образовывать отдельный доход на дату её получения. Финальный состав доходов, даты признания и сумму к уплате подтверждает бухгалтер по отчётам площадок и расчётным документам. Плейбук не заменяет налоговую консультацию.

Три горизонта, одна методика:

Горизонт Где живёт Чем считается Зачем
День tax_accrual (period_kind='day') scripts/refresh_taxes.py оперативный управленческий резерв, виден в прибыли на сайте
Закрытая неделя Пн–Вс (только WB) tax_accrual (period_kind='week') scripts/refresh_taxes.py закрытый управленческий резерв по отчётности WB
Закрытый месяц отчёт + JSON scripts/monthly_usn_tax.py сверка покупательской части и компенсаций для бухгалтера

Ежедневный и недельный показатели ведутся одновременно и независимо — см. раздел «Ведение налога в БД». Месячный регламент ниже — канон сбора и сверки данных, но не подмена бухгалтерского определения налоговой базы.

Зафиксированное правило управленческого расчёта

Плейбук применяется для управленческого расчёта УСН «Доходы» по Siluetta WB (shop_id=3), Siluetta Ozon (shop_id=4) и Tote WB (shop_id=5).

Текущая методика владельца от 27.07.2026 (USN_BASIS=buyer_cash):

  • база управленческого резерва магазина — сумма, фактически уплаченная покупателями;
  • продажи уменьшаются на покупательскую стоимость возвратов текущего месяца;
  • комиссия, логистика, реклама, хранение и прочие удержания площадки базу не уменьшают;
  • компенсации скидок и бонусы Ozon не смешиваются с покупательской частью и показываются отдельной контрольной суммой;
  • ставка УСН — 6%;
  • расчёт по каждому магазину хранится отдельно. Итог нескольких магазинов складывается только тогда, когда они принадлежат одному налогоплательщику.

Это расчётный резерв до уменьшения на страховые взносы. Он не становится заявляемым налогом только потому, что сохранён в tax_accrual. Официальный авансовый платёж УСН считается нарастающим итогом за I квартал, полугодие и 9 месяцев; перед его определением бухгалтер отдельно учитывает компенсации скидок и даты их признания.

⚠ Контрольная разница: что именно площадка компенсирует (проверено 30.07.2026)

Управленческое правило «база = кэш покупателя» подтверждено владельцем 30.07.2026 повторно, уже с цифрами на руках. Что именно стоит за разницей:

  • WB. retail_price — цена продавца, retail_amount — что заплатил покупатель, СПП — скидка за счёт WB. На реальной строке июня: retail_price 1970 → retail_amount 1527 (СПП 22.5%) → for_pay 1219.42, то есть 61.9% от 1970. Доля держится ~62% на всех строках независимо от retail_amount → WB платит от цены продавца и СПП компенсирует сам.
  • Ozon. По всем 241 строке отчёта за июнь без исключений выполняется amount + bonus + bank_coinvestment = seller_price × quantity (строка ELS04-black-S: 2264.76 + 3002.59 + 22.65 = 5290). В транзакциях по тому же постингу accruals_for_sale = 5290, payout = 2803.70 = 5290 − 47% комиссии → Ozon начисляет продавцу полную цену продавца.

Масштаб разницы за июнь 2026:

Магазин Кэш покупателя Начислено площадкой Разница
Siluetta WB 288 976.57 376 825.60 87 849.03 (СПП)
Tote WB 238 364.29 333 567.45 95 203.16 (СПП)
Siluetta Ozon 439 866.62 1 060 794.00 620 927.38 (баллы+банк)
Резерв 6% по двум сценариям 58 032 106 331 +48 299

Поэтому tax_accrual всегда хранит обе величины: buyer_net (база управленческого сценария) и accrued_net / platform_comp_net (контрольная разница). Переключить сценарий резерва можно без правки кода — USN_BASIS=accrued_income в .env. Ни один из двух переключателей сам по себе не определяет законную базу: platform_comp_net построен как разница начислений, а не как реестр реально признанных компенсаций по датам. Для декларации бухгалтер сверяет её с отчётами и расчётными документами площадки.

Контрольный ориентир по позиции ФНС:

доход УСН к проверке бухгалтером =
    поступления покупателей нетто возвратов
  + компенсации собственных скидок маркетплейса,
    признанные доходом на соответствующие даты

Комиссию и услуги маркетплейса при объекте УСН «Доходы» из дохода не вычитать.

Обязательные источники

Wildberries

Источник — закрытый отчёт реализации fin_report_detail.

Что Поле
Дата включения в месяц rr_date
Тип строки seller_oper_name
Покупательская сумма retail_amount — «Вайлдберриз реализовал Товар (Пр)»
Продажи seller_oper_name='Продажа'
Возвраты seller_oper_name='Возврат'

В WB retail_amount у возврата хранится положительным числом. Формула поэтому обязательно явная:

WB покупательская часть `buyer_cash` =
    SUM(retail_amount для Продажа)
  − SUM(retail_amount для Возврат)

Не использовать for_pay: это сумма после части удержаний, а не оплата покупателя.

Ozon

Источник точного расчёта покупательской части сценария buyer_cash — закрытый месячный отчёт о реализации:

POST /v1/finance/realization/posting
{"month": <1..12>, "year": <YYYY>}
Что Поле отчёта
Покупатели оплатили delivery_commission.amount
Возвраты покупателям return_commission.amount
Количество продаж delivery_commission.quantity
Количество возвратов return_commission.quantity
Цена продавца, только контроль seller_price_per_instance
Ozon покупательская часть `buyer_cash` =
    SUM(delivery_commission.amount)
  − SUM(return_commission.amount)

Для точного расчёта покупательской части нельзя брать:

  • ozon_posting_items.price;
  • ozon_finance_operations.accruals_for_sale;
  • ozon_finance_operations.amount;
  • текущую цену карточки;
  • ozon_buyer_price_daily.

Первые три значения описывают цену продавца, начисления или выплату после удержаний. Ежедневный снимок цены описывает витрину в один момент и не видит персональные скидки, баллы и купоны конкретного заказа.

Ozon формирует месячный отчёт после закрытия месяца, обычно не позднее 5-го числа следующего месяца. До его появления разрешён только отчёт «ПРЕДВАРИТЕЛЬНАЯ ОЦЕНКА»; он не заменяет закрытую сверку.

Доступность реализации

На 16.08.2026 таблица tax_accrual, app/reports/taxes.py, scripts/refresh_taxes.py и scripts/monthly_usn_tax.py есть в рабочей копии на Hetzner, но ещё не входят в origin/main и локальную кодовую базу. Ниже описано фактическое серверное поведение; локально команды запускать только после отдельной синхронизации налогового кода. Документация синхронизируется сейчас, код — нет.

Ведение налога в БД (tax_accrual)

Движок — app/reports/taxes.py, запуск — scripts/refresh_taxes.py. Одна строка = один магазин × один период. UNIQUE (shop_id, period_kind, period_start, period_end): пересчёт идёт upsert'ом, повторный прогон строк не плодит.

.venv/bin/python scripts/refresh_taxes.py                       # 35 дней + 8 недель
.venv/bin/python scripts/refresh_taxes.py --days 90 --weeks 13  # бэкфилл
.venv/bin/python scripts/refresh_taxes.py --show 14             # что лежит в БД

Локальный пересчёт не ходит в API — гонять можно сколько угодно. Единственный шаг с обращением к Ozon Seller API — --ozon-month YYYY-MM.

Два показателя

День (period_kind='day', WB + Ozon) — оперативный, пересчитывается каждым прогоном. is_closed=1 у WB-дня значит, что финреп его уже покрыл; до полудня вчерашний день висит незакрытым, это норма (WB отдаёт дневную реализацию к 12:00 МСК, см. таймер mp-finreport@).

Закрытая неделя (period_kind='week', только WB) — по отчётности WB. Строка пишется только если финреп покрывает воскресенье недели; неполную неделю в недельный показатель не пускаем вообще (не «половина недели», а пропуск). Контроль сходимости: сумма дневных строк недели = недельная строка, до копейки.

Закрытый месяц Ozon (period_kind='month') — точный отчёт о реализации. Нужен не только бухгалтеру: из него берётся доля кэша покупателя в начислении, которой оцениваются дневные Ozon-строки. Запускать раз в месяц, после 5-го числа:

.venv/bin/python scripts/refresh_taxes.py --ozon-month 2026-06

Почему Ozon по дням — оценка, а не факт

Покупательскую часть Ozon отдаёт только в месячном отчёте о реализации. В дневных транзакциях (ozon_finance_operations) есть лишь accruals_for_sale = цена продавца. Поэтому дневная строка Ozon считается как начисление × доля кэша из последнего закрытого месяца, is_closed всегда 0, а способ расчёта пишется в note. Точность на июне 2026: оценка 442 188.72 против факта 439 866.62 — +0.53% (расхождение из-за того, что operation_date относит часть операций к другому месяцу, чем отчёт о реализации). Пока закрытого месяца нет, работает константа OZON_CASH_RATIO_FALLBACK.

Возвраты учитываются: у возвратных операций (ClientReturnAgentOperation, OperationAgentStornoDeliveredToCustomer) accruals_for_sale отрицательный и суммируется как есть.

Автообновление

scripts/run_collect.py гоняет шаг taxes последним в каждом сборе (ночной WB, ночной Ozon и полуденный --only-finreport). Шаг локальный и идемпотентный, ~4 с. Отдельного таймера намеренно нет: налог обязан пересчитаться сразу после полуденного finreport, иначе вчерашний WB-день останется недосчитанным.

Управленческий резерв в прибыли на wb-ozon-ai.ru

С 30.07.2026 на сервере метрика tax есть в app/reports/aggregate.py (METRIC_FIELDS), и profit считается после выбранного налогового резерва — одинаково в вебе и в Telegram-кэше. Это управленческая прибыль, а не подтверждение суммы налога в декларации.

Где видно:

  • / — KPI-карточка «Налог УСН», колонка «Налог ₽» в таблице по магазинам, своя статья в «Структуре расходов», упоминание в AI-сводке;
  • /finance — KPI-карточка + колонка «Налог ₽» на уровне магазина и SKU;
  • Telegram: колонка «Налог» во второй таблице отчёта «Прибыль» и в rich-своде.

Разноска по SKU. Налог пропорционален покупательской базе, поэтому по SKU разносится множителем tax_factor():

  • WBrevenue в отчёте это уже retail_amount нетто, то есть сама покупательская часть сценария buyer_cash → множитель прямой;
  • Ozonrevenue это начисление (цена продавца) и оно брутто возвратов (возвраты режут только orders). Поэтому база управленческого резерва считается отдельным накопителем по accruals_for_sale нетто возвратов и домножается на долю кэша. Не менять revenue под налог: сдвинутся выручка и ДРР на всех страницах.

Контроль: сумма tax по SKU за окно = сумма tax_accrued дневных строк tax_accrual за то же окно (сверено до копейки на окнах 1/7/30 дн по трём магазинам). Расхождение = разъехались методики, надо смотреть.

В «Структуре расходов» налог обязан быть своей статьёй: остаток «Комиссия и прочее» считается как выручка − себес − логистика − реклама − налог − прибыль, и без вычета налога он бы целиком осел в комиссии.

Ежемесячный регламент

Оптимальный день запуска — 5–7 число следующего месяца.

1. Зафиксировать параметры

  • закрытый месяц YYYY-MM;
  • магазины и принадлежность к ИП/ООО;
  • ставка УСН;
  • освобождение от НДС или действующая ставка НДС;
  • страховые взносы для последующего уменьшения налога.

Если продавец платит НДС и он включён в покупательскую цену:

Управленческая база `buyer_cash` без НДС =
    покупательская часть / (1 + ставка НДС / 100)

2. Проверить и при необходимости обновить WB

У WB отчёт должен покрывать последний календарный день месяца. Сначала проверить MAX(period_end); API без необходимости не вызывать из-за квоты.

SELECT shop_id, MAX(period_end), MAX(fetched_at)
FROM fin_report_detail
WHERE shop_id IN (3, 5)
GROUP BY shop_id;

Если покрытия нет, по одному разу обновить каждый магазин:

.venv/bin/python scripts/ingest_wb.py --shop-id 3 finreport \
  --from 2026-07-01 --to 2026-07-31 --period daily

.venv/bin/python scripts/ingest_wb.py --shop-id 5 finreport \
  --from 2026-07-01 --to 2026-07-31 --period daily

WB finance API имеет жёсткую квоту. Не повторять запрос, если нужный закрытый отчёт уже есть в корневом mp.db.

3. Запустить закрытый управленческий расчёт

При освобождении от НДС:

.venv/bin/python scripts/monthly_usn_tax.py \
  --month 2026-07 --usn-rate 6 --vat-rate 0

Если в цене есть НДС 5%:

.venv/bin/python scripts/monthly_usn_tax.py \
  --month 2026-07 --usn-rate 6 --vat-rate 5

Сохранить машиночитаемый контрольный отчёт:

.venv/bin/python scripts/monthly_usn_tax.py \
  --month 2026-07 --usn-rate 6 --vat-rate 0 \
  --json-out data/exports/taxes/2026-07-usn.json

По одному налогоплательщику или магазину:

# Только Siluetta: WB + Ozon
.venv/bin/python scripts/monthly_usn_tax.py \
  --month 2026-07 --wb-shop 3 --ozon-shop 4

# Только Tote WB
.venv/bin/python scripts/monthly_usn_tax.py \
  --month 2026-07 --wb-shop 5 --no-ozon

Скрипт намеренно отклоняет текущий незакрытый месяц и Ozon-отчёт с неверными границами периода.

4. Проверить результат

Для каждого магазина должны быть видны:

  • продажи и возвраты в штуках;
  • сколько заплатили покупатели;
  • покупательская сумма возвратов;
  • чистая покупательская база сценария buyer_cash;
  • резерв УСН 6% по выбранному сценарию;
  • источник и закрытая граница периода.

Для Ozon дополнительно выводятся:

  • цена продавца net;
  • разница между ценой продавца и покупательской частью.

Эта разница — контроль, защищающий от повторения ошибки, когда за покупательскую цену принимается seller_price_per_instance или accruals_for_sale. Одновременно это сигнал бухгалтеру проверить компенсацию скидки; автоматически прибавлять всю разницу к базе нельзя без сверки её природы и даты признания.

5. Сохранить и передать бухгалтеру

Хранить один JSON на месяц:

data/exports/taxes/YYYY-MM-usn.json

Передавать бухгалтеру:

  1. расчёт по каждому магазину;
  2. объединение по конкретному ИП/ООО;
  3. отдельные buyer_net, accrued_net и platform_comp_net, а также документы площадки для сверки фактически признанных компенсаций;
  4. ставку и статус НДС;
  5. страховые взносы, которыми уменьшается начисленный УСН.

Контрольные формулы

Покупательская часть = продажи покупателям − возвраты покупателям

Управленческая база buyer_cash без НДС =
    покупательская часть / (1 + НДС / 100)

Управленческий резерв buyer_cash =
    управленческая база buyer_cash без НДС × 6%

Контроль дохода для бухгалтера =
    покупательская часть
  + подтверждённые компенсации собственных скидок маркетплейса

Денежные суммы внутри расчёта хранятся с копейками. Итог управленческого резерва округляется до полного рубля только после суммирования магазинов одного налогоплательщика. Итог к уплате рассчитывает бухгалтер нарастающим итогом.

Юридическая контрольная точка

По письму ФНС от 08.05.2024 № СД-4-3/5416@ вся сумма поступлений покупателя учитывается в доходах, а комиссия маркетплейса не уменьшает доход при объекте УСН «Доходы». В разъяснении ФНС от 01.12.2025 отдельно указано: компенсация собственной скидки маркетплейса включается в доход на дату её получения, а возврат уменьшает доход в периоде возврата.

Следствие для этой системы: buyer_cash допустим как прозрачный нижний управленческий сценарий, но не как готовая цифра декларации. Перед подачей отчётности бухгалтер обязан определить, какая часть platform_comp_net действительно является компенсацией, и разнести её по датам. Сценарий accrued_income тоже не объявляется автоматически законным: начисление цены продавца и признанный налоговый доход могут расходиться по составу и моменту.

Официальные ориентиры:

Чек-лист ежедневного/недельного ведения

  • [ ] Шаг taxes прошёл в последнем сборе (виден в Telegram-алерте run_collect).
  • [ ] У вчерашнего WB-дня is_closed=1 (если нет — финреп ещё не пришёл, ждать полдня).
  • [ ] Сумма дневных строк недели = недельная строка.
  • [ ] Сумма tax по SKU на /finance = сумма tax_accrued за то же окно.
  • [ ] Доля кэша Ozon взята из закрытого месяца, а не из фолбэк-константы (--show печатает источник).
  • [ ] После 5-го числа прогнан --ozon-month за прошлый месяц.

Чек-лист закрытия месяца

  • [ ] Месяц полностью закрыт.
  • [ ] WB period_end покрывает последний день месяца.
  • [ ] Ozon вернул месячный отчёт с точными start_date и stop_date.
  • [ ] Покупательская часть рассчитана отдельно от цены продавца.
  • [ ] Возвраты вычтены.
  • [ ] Статус НДС подтверждён.
  • [ ] Магазины сгруппированы по правильному ИП/ООО.
  • [ ] Компенсации/разница площадок показаны бухгалтеру отдельно и сверены по датам.
  • [ ] buyer_cash не выдан за финальную базу декларации без решения бухгалтера.
  • [ ] Страховые взносы применены только после расчёта начисленного УСН.
  • [ ] JSON сохранён в data/exports/taxes/.
Фильтры