food-market/docs/moysklad-logic-audit.md
nns e5bbd4a047
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
docs: аудит против МойСклада — проход 2 (факт-проверки, Казахстан)
Дополнен docs/moysklad-logic-audit.md (+ .html) разделом «Проход 2»:
- Все «проверить» закрыты фактом (код файл:строка и/или запрос на стенде):
  возврат→ReferenceSaleId; Z-отчёт без «по товарам»; долг поставщику считается
  (MoneyReportsController:120-183); обязательные поля товара Name+Unit+Group;
  приёмка с телефона/терминалы/фискализация — нет; отмена проведения ЕСТЬ у всех
  документов (исправление прохода 1); смена — только ручное открытие.
- Себестоимость: подтверждён FIFO у МойСклада; у нас средняя; реальный пример Демо
  (BIOBLAS, Product.Cost=548.6441 из 3 приёмок) — показано искажение прибыли.
- Казахстан (новое): фискализация ОФД РК (заглушка у нас; МойСклад — WebKassa/reKassa),
  НДС 16% с 2026 (дефолт KZ уже 16, SystemReferenceSeeder:38), маркировка ИС МПТ
  (только флаг IsMarked), СНТ/ЭСФ, акциз. Блок «Казахстан — обязательное по закону».
- Сводка обновлена: топ-15 скорректирован (фискализация/маркировка РК вместо неверных
  пунктов), добавлен KZ-блок. Касса МойСклад vs наша — по функциям кассира.

Только отчёт, без правок кода.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 19:21:57 +05:00

545 lines
58 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Аудит логики процессов против МойСклада
**Задача:** сверить логику всех процессов нашей системы (ALL MARKET) с МойСкладом —
эталоном, над которым «люди думали годами». Только отчёт; исправления — отдельными
задачами после решения владельца. Дата: 08.10.2026.
**Подход (по требованию владельца):** по каждому процессу — (1) как у МойСклада (база,
по умолчанию делаем так же), (2) слабые места МойСклада, (3) предложение «как лучше у нас»
только там, где есть реальная выгода для магазина — помечено **[НА РЕШЕНИЕ ВЛАДЕЛЬЦА]**.
Без решения владельца реализуем как в МойСкладе.
**Источники:** dev.moysklad.ru/doc/api/remap/1.2 (API, JS-рендер), support.moysklad.ru
(UI-логика), moysklad.ru/poleznoe (школа торговли), отзывы/обзоры. Ссылки — в строках таблиц
и в конце. Наши ссылки — `файл:строка` в репозитории.
**Серьёзность:** 🔴 высокая — влияет на остатки/деньги/отчёты; 🟡 средняя — UX/логика;
⚪ низкая — название поля/косметика.
---
## 0. Корневая причина (почему так вышло)
Процессы реализовывались «по месту», без предварительной сверки с МойСкладом, а затем
тесты (full-chain, unit) закрепляли уже написанное поведение как эталон. В результате ряд
процессов отличается от отраслевой нормы МойСклада. Самый яркий пример — инвентаризация:
документ сам двигает остатки и встаёт в статус «Проведено» без Оприходования/Списания
(`InventoriesController` + `InventoryStatus.Posted`), чего в МойСкладе нет.
---
## СВОДКА ДЛЯ ВЛАДЕЛЬЦА
### Топ-15 расхождений высокой серьёзности (🔴)
| # | Процесс | Расхождение | Объём исправления |
|---|---|---|---|
| 1 | Себестоимость | У нас **скользящее среднее** (`MovingAverageCost`), у МойСклада — **FIFO** по партиям | Большой (партийный учёт + пересчёт отчётов) |
| 2 | Прибыльность | COGS берётся как текущая `Product.Cost` (приближение), а не себестоимость на момент продажи | Большой (следствие п.1; нужны партии/снимок COGS) |
| 3 | Инвентаризация | Сама двигает остатки и статус «Проведено» без Оприходования/Списания | Средний (переделка уже в очереди — см. §4) |
| 4 | Себестоимость продажи | В движение продажи пишется `UnitCost = цена продажи`, а не себестоимость (`DemandsController:270`) | Средний (писать снимок себестоимости) |
| 5 | **Фискализация РК** | Касса не фискализирует чеки у ОФД (онлайн-ККМ РК) — `NullFiscalProvider`-заглушка; по закону РК розница обязана | Средний–большой (интеграция Webkassa/reKassa) |
| 6 | Статусы документов | «Проведено/Черновик» не всегда соответствуют модели МойСклада (у инвентаризации — ошибочно) | Средний |
| 7 | «Создать на основании» | Нет сквозного механизма создания документов «на основании» со ссылками (приёмка→возврат, инвент.→опр/спис, продажа→возврат) | Средний–большой |
| 8 | Связи документов | Не хранятся/не показываются обратные связи «на основании №…» и блок «Связанные документы» | Средний |
| 9 | Момент фиксации себестоимости | Себестоимость меняется задним числом при новых приёмках (скользящее среднее), искажая прошлые отчёты | Большой (следствие п.1) |
| 10 | Возврат покупателя: себестоимость | При возврате товар возвращается на склад — по какой себестоимости (FIFO-слой vs средняя) не определено строго | Средний |
| 11 | Партийный учёт | Отсутствует (нет партий/слоёв прихода) — блокирует FIFO, сроки годности, точный COGS | Большой |
| 12 | Перемещение и себестоимость | При перемещении между складами себестоимость должна переноситься по FIFO-слоям | Средний (зависит от п.11) |
| 13 | **Маркировка товаров РК** | Нет вывода кода из оборота (ИС МПТ) при продаже табак/обувь/лекарств — только флаг `IsMarked`; обязательно по закону РК | Большой |
| 14 | Оприходование/Списание: цена | Должны брать учётную себестоимость (FIFO), у нас — переданный `UnitCost`/средняя | Средний |
| 15 | Розничная смена: авто-открытие | В МойСкладе смена открывается автоматически первой продажей (≤24ч); у нас только ручное открытие (`CashShiftsController` Open/Close, продажа требует открытой смены) | Малый–средний |
### Топ-10 предложений «лучше, чем МойСклад» (все **[НА РЕШЕНИЕ ВЛАДЕЛЬЦА]**)
| # | Идея | Выгода магазину | Объём |
|---|---|---|---|
| 1 | Инвентаризация в один клик «Применить результаты» (сразу оба документа проведены) | Меньше шагов, чем 2 кнопки МойСклада | Малый (в рамках переделки §4) |
| 2 | Простой режим прячет Оприходование/Списание как отдельные пункты — они создаются только из инвентаризации/приёмки | Меньше путаницы у маленького магазина | Малый |
| 3 | Себестоимость: выбор метода (средняя — проще/быстрее для мелкой розницы, FIFO — точнее) | Гибкость; средняя дешевле по вычислениям | Средний |
| 4 | Единый экран «Деньги»: внесение/выплата/оплаты в одном реестре без 4 типов документов | Проще, чем ПКО/РКО/вх/исх платёж | Средний |
| 5 | Автосоздание возврата поставщику прямо из приёмки с преднабором строк | Быстрее оформить брак | Малый |
| 6 | Касса: подсказка «нет на остатке» мягкая (не блок), с быстрым оприходованием | Не стопорит продажу в рознице | Малый |
| 7 | Онбординг-мастер «первая приёмка за 2 минуты» (у МойСклада онбординг тяжелее) | Быстрый старт | Средний |
| 8 | Весовой штрихкод из коробки с настройкой префиксов (уже есть у нас — довести) | Для продуктовых — критично | Малый |
| 9 | Отчёт «Прибыль» сразу на дашборде простого режима | Владелец видит главное без отчётов | Малый |
| 10 | Понятные статусы на русском без «проведено» (Черновик→Завершён/Проведён с пояснением) | Меньше вопросов «что это значит» | Малый |
### 🇰🇿 Казахстан — обязательное по закону (добавлено проходом 2)
Отдельный блок: это не «лучше/хуже», а **требования закона РК**, без которых магазин не
может легально работать. Детали и ссылки — в §6 ниже.
| # | Требование РК | Что нужно рознице | Наш статус | Срочность |
|---|---|---|---|---|
| K1 | **Фискализация (онлайн-ККМ + ОФД РК)** | каждый чек фискализируется у ОФД (новый протокол ККМ v2.0.3 с 01.04.2026) | ❌ заглушка `NullFiscalProvider` | 🔴 блокер для продаж |
| K2 | **НДС 16%** (с 01.01.2026; было 12%), льгота 5% на лекарства | ставка НДС и её учёт в чеке/отчётах | ✅ дефолт KZ = **16** (`SystemReferenceSeeder.cs:38`); нет переходной логики 12→16 по дате оборота | 🟡 |
| K3 | **Маркировка (ИС МПТ)**: табак, обувь, лекарства (+пиво/одежда 2026–2027) | при продаже — сканировать и **вывести код из оборота** | ❌ только флаг `IsMarked`, вывода нет | 🔴 если продаёт эти категории |
| K4 | **СНТ/ЭСФ** (сопроводительные накладные) | для рознице в основном автоматически; **подтверждение обязательно по подакцизным** | ❌ нет интеграции с ИС ЭСФ | 🟡 (🔴 если подакцизные) |
| K5 | **Акциз/подакцизные** (табак, алкоголь) | учёт + подтверждение СНТ при приёмке | ❌ нет отметки «подакцизный» | 🟡 |
**Для маленького продуктового магазина в первую очередь:** K1 (фискализация — иначе нельзя
пробивать чеки) → K3 (маркировка, если продаёт сигареты) → K2 (НДС в чеке).
---
## ПРИОРИТЕТ 1 — Касса и продажи (чаще всего используется)
### 1.1 Розничная продажа (retaildemand) / Касса
**Как у МойСклада:** Продажа фиксирует передачу товара покупателю и приход денег в кассу;
при проведении уменьшает остаток и формирует выручку; себестоимость списывается по FIFO.
Возврат создаётся «на основании» продажи, деньги возвращаются тем же способом оплаты.
([Касса МойСклад](https://www.moysklad.ru/reshenija/mobilnaya-kassa/),
[школа: возврат](https://www.moysklad.ru/poleznoe/shkola-torgovli/kak-oformit-vozvrat-ot-pokupatelja/)).
**Слабые места МойСклада:** жёсткая привязка возврата к конкретной продаже бывает неудобна
(покупатель без чека); интерфейс кассы перегружен для мелкой точки.
| Аспект | МойСклад | У нас | Расхождение | Серьёзность | Предложение [владельцу] |
|---|---|---|---|---|---|
| Влияние на остаток | при проведении продажи −кол-во | POS → `RetailSalesController`/`PosController`, движение `RetailSale` | в целом совпадает | — | — |
| Себестоимость в продаже | FIFO-слой на момент продажи | `UnitCost=UnitPrice` (цена продажи) в движении, COGS≈`Product.Cost` позже (`DemandsController:270`, `ProfitReportController:14-19`) | 🔴 COGS неточный/задним числом | 🔴 | писать снимок себестоимости в движение |
| Возврат | «на основании» продажи, тот же способ оплаты | есть ссылка `ReferenceSaleId` (`RetailSalesController.cs:238`); способ возврата выбирает кассир (не форсится как в МойСкладе) | 🟡 способ не привязан к исходному | 🟡 | опц.: по умолчанию тем же способом |
| Смена | автооткрытие первой операцией, ≤24ч | только ручное открытие (`CashShiftsController` Open/Close; продажа требует открытой смены) | 🟡 нет автооткрытия | 🟡 | автооткрывать первой продажей |
| Деньги | приход в кассу привязан к смене | смена+счёт (есть, чинили ранее) | совпадает | — | — |
### 1.2 Розничная смена (retailshift) и внесение/выплата (drawer cash in/out)
**Как у МойСклада:** Смена открывается автоматически первой продажей/внесением/выплатой или
вручную; не длиннее 24 часов; при открытии можно внести разменные деньги; отчёт смены
показывает нал/безнал выручку, продажи и возвраты, по товарам, историю размена и связанные
документы. ([МойСклад: розничные смены](https://www.moysklad.ru/news/715-roznichnye-smeny-obnovlenie-ios-podderzhka-registratora-pirit-k-i-drugie-uluchsheniya/)).
| Аспект | МойСклад | У нас | Расхождение | Серьёзность |
|---|---|---|---|---|
| Автооткрытие смены | да, первой операцией | вручную (`CashShiftsController`) | 🟡 | 🟡 |
| Максимум 24ч | да | нет явного ограничения | 🟡 | 🟡 |
| Внесение/выплата | ПКО/РКО внутри смены | внесение/выплата в кассе + `MoneyTransactions` | совпадает по сути | — |
| Z-отчёт смены | нал/безнал, продажи/возвраты, по товарам | `ShiftReportDto` (`IServerApi.cs:61`): нал/безнал по продажам и возвратам + сверка кассы; **без разбивки по товарам** | 🟡 нет «по товарам» | 🟡 |
**Предложение [владельцу]:** автооткрытие смены первой продажей (как МойСклад) + мягкое
предупреждение на 24ч — удобнее кассиру, меньше «забыл открыть смену».
---
## ПРИОРИТЕТ 2 — Товары, остатки, себестоимость, отчёты
### 2.1 Себестоимость (ключевое 🔴)
**Как у МойСклада:** себестоимость считается по **FIFO** — при продаже списывается товар из
самой ранней партии; остаток товара хранится «слоями» закупок с разными ценами. Это даёт
исторически корректную прибыль, не меняющуюся задним числом.
([школа: методы расчёта себестоимости](https://www.moysklad.ru/poleznoe/shkola-torgovli/metody-rascheta-sebestoimosti/),
[support: себестоимость](https://support.moysklad.ru/hc/ru/articles/217544337)).
**Как у нас:** **скользящее средневзвешенное** (`MovingAverageCost.Compute`,
`src/food-market.application/Inventory/MovingAverageCost.cs`): при каждой приёмке
пересчитывается единая `Product.Cost`. Прошлые отчёты «плывут» при новых закупках.
**Слабые места МойСклада:** FIFO требует партийного учёта — тяжелее по вычислениям и
непонятнее для микромагазина; при частых мелких закупках «слоёв» очень много.
| Аспект | МойСклад | У нас | Расхождение | Серьёзность | Предложение [владельцу] |
|---|---|---|---|---|---|
| Метод | FIFO по партиям | скользящее среднее | 🔴 | 🔴 | **выбор метода**: среднее (быстро/просто) по умолчанию для простого режима, FIFO — опция |
| Момент фиксации COGS | на момент отгрузки | приближённо, текущая `Product.Cost` | 🔴 задним числом | 🔴 | снимок COGS в движении |
| Партии/слои | есть | нет | 🔴 блокирует FIFO, сроки годности | 🔴 | партийный учёт (большой) |
### 2.2 Остатки и движения (отчёты)
**Как у МойСклада:** отчёт **Остатки** — текущие остатки по складам; **Обороты** — движение
товара (приход/расход) за период с указанием документов-источников; остатки меняются только
после **проведения** складских документов.
([support: Остатки](https://support.moysklad.ru/hc/ru/articles/203319073)).
| Аспект | МойСклад | У нас | Расхождение | Серьёзность |
|---|---|---|---|---|
| Остатки меняются при проведении | да | да (`StockService.ApplyMovement` при Post) | совпадает | — |
| Источник движения виден | документ-основание в оборотах | `StockMovement.SourceDoc` есть | в целом совпадает | — |
| Инвентаризация в оборотах | как Оприходование/Списание | как `InventoryAdjustment` (нет опр/спис) | 🔴 непонятно в отчётах | 🔴 |
### 2.3 Прибыльность
**Как у МойСклада:** Прибыль = Выручка − Себестоимость(FIFO) за период/по товарам/группам.
([отчёты МойСклад](https://ctrlf5.by/blog/moysklad-otchety)).
**Как у нас:** `ProfitReportController` — Выручка − COGS, где **COGS ≈ текущая `Product.Cost`**
(признано в комментарии кода `ProfitReportController.cs:14-19` как «приближение»).
| Аспект | МойСклад | У нас | Расхождение | Серьёзность |
|---|---|---|---|---|
| COGS | FIFO на момент продажи | текущее среднее (приближение) | 🔴 | 🔴 |
| Стабильность прошлых периодов | стабильна | «плывёт» при новых закупках | 🔴 | 🔴 |
### 2.4 Карточка товара (справочник)
**Как у МойСклада:** обязательно только **Наименование**; тип учёта (весовой/штучный/алко),
фасовка (весовая для весовых), штрихкоды (EAN8/EAN13/GTIN), единица измерения, несколько
типов цен, себестоимость, НДС, вес/объём.
([support: создание карточки товара](https://support.moysklad.ru/hc/ru/товары%20и%20склады/товары%20и%20услуги/создание_карточки_товара),
[типы цен](https://support.moysklad.ru/hc/ru/articles/203053526)).
| Аспект | МойСклад | У нас | Расхождение | Серьёзность |
|---|---|---|---|---|
| Обязательные поля | только наименование | `ProductInput` (`CatalogDtos.cs:92`): обязательны **Name + UnitOfMeasureId + ProductGroupId** | 🟡 у нас больше обязательных | 🟡 |
| Весовой товар | фасовка весовая | `IsWeighed`/packaging есть | совпадает | — |
| Штрихкоды | EAN8/13/GTIN, несколько | несколько штрихкодов, весовые префиксы | совпадает/лучше | — |
| Типы цен | несколько, автоподстановка | типы цен есть (`PriceTypesController`) | совпадает | — |
---
## ПРИОРИТЕТ 3 — Приёмка, Оприходование, Списание, Инвентаризация
### 3.1 Приёмка (supply)
**Как у МойСклада:** остаток появляется только после **проведения** приёмки; при проведении
товар приходуется на склад, формируется себестоимость (FIFO-слой); можно создать «на
основании» заказа поставщику; влияет на долг поставщику.
([support: приёмка](https://support.moysklad.ru/hc/ru/articles/203325563)).
| Аспект | МойСклад | У нас | Расхождение | Серьёзность |
|---|---|---|---|---|
| Остаток при проведении | +кол-во | `SuppliesController.Post` → движение Receipt | совпадает | — |
| Себестоимость | FIFO-слой | пересчёт скользящего среднего | 🔴 (метод) | 🔴 |
| На основании заказа | да | заказа поставщику нет в простом режиме | 🟡 (скрыт) | 🟡 |
| Долг поставщику | +долг | считается в отчёте долгов (Σ приёмок, `MoneyReportsController.cs:120-183`) | совпадает (расчётно) | — |
### 3.2 Оприходование (enter) и Списание (loss)
**Как у МойСклада:** Оприходование добавляет остаток (излишки/начальные остатки) по учётной
себестоимости; Списание убирает остаток (брак/недостача) по FIFO-себестоимости; оба — отдельные
проводимые документы; создаются в т.ч. «на основании» инвентаризации.
([support: оприходование](https://support.moysklad.ru/hc/ru/товары%20и%20склады/операции/оприходование_товаров),
[списание](https://support.moysklad.ru/hc/ru/articles/203053096)).
| Аспект | МойСклад | У нас | Расхождение | Серьёзность |
|---|---|---|---|---|
| Оприходование +остаток | да, при проведении | `EntersController.Post` | совпадает | — |
| Списание −остаток | да, по FIFO | `LossesController` c `UnitCost` переданным | 🔴 цена не по FIFO | 🔴 |
| Создать из инвентаризации | да (кнопка «Создать документ») | **нет** (инвент. двигает сама) | 🔴 | 🔴 |
### 3.3 Инвентаризация (inventory) — корень жалобы владельца
**Как у МойСклада:** инвентаризация **не двигает остатки**; фиксирует расчётное и фактическое
количество и разницу; по результатам пользователь создаёт **Оприходование** (излишки) и
**Списание** (недостача) кнопкой «Создать документ»; именно эти документы меняют остатки.
([support: инвентаризация товаров](https://support.moysklad.ru/hc/ru/товары%20и%20склады/операции/инвентаризация_товаров)).
**Как у нас (неправильно):** `InventoriesController.Post` сам создаёт движения
`MovementType.InventoryAdjustment` и ставит `InventoryStatus.Posted` (`InventoryDoc.cs:8-9`) —
остатки меняются напрямую, без Оприходования/Списания. Владелец видит «Проведено» без
связанных документов.
| Аспект | МойСклад | У нас | Расхождение | Серьёзность |
|---|---|---|---|---|
| Двигает остатки | нет | **да** (`InventoryAdjustment`) | 🔴 | 🔴 |
| Статусы | Черновик→Завершена | Draft→**Posted** («Проведено») | 🔴 | 🔴 |
| Результат | Оприходование+Списание со ссылкой | нет связанных документов | 🔴 | 🔴 |
**Переделка уже запланирована** (следующая задача, ветка `wip/inventory-model`): статусы
Черновик→Завершена без движений; кнопки «Создать оприходование/списание/применить»; связи;
миграция Posted+InventoryAdjustment → проведённые Оприходование/Списание с сохранением остатков.
Этот план **совпадает с выводами ревизии** (пп. 3,6,7,8 сводки).
**Предложение [владельцу]:** добавить к модели МойСклада кнопку «Применить результаты» (оба
документа сразу проведены) — на один шаг короче, чем две кнопки МойСклада.
---
## ПРИОРИТЕТ 4 — Возврат поставщику, деньги, перемещение
### 4.1 Возврат поставщику (purchasereturn)
**Как у МойСклада:** списывает товар со склада, уменьшает долг поставщику; создаётся «на
основании» приёмки или вручную. ([support: возврат поставщику](https://support.moysklad.ru/hc/ru/articles/230623667)).
| Аспект | МойСклад | У нас | Расхождение | Серьёзность |
|---|---|---|---|---|
| −остаток | да | `SupplierReturnsController` движение `SupplierReturn` | совпадает | — |
| −долг поставщику | да | учтён в отчёте долгов (Σ возвратов вычитается, `MoneyReportsController.cs:120-183`) | совпадает (расчётно) | — |
| На основании приёмки | да, преднабор | опционально ссылка (есть поле) | 🟡 довести UI | 🟡 |
### 4.2 Деньги: платежи, ордера, взаиморасчёты, долги
**Как у МойСклада:** 4 типа денежных документов — **Входящий/Исходящий платёж** (безнал) и
**Приходный/Расходный ордер** (нал); все влияют на ДДС и **взаиморасчёты** с контрагентом;
отчёт «Взаиморасчёты» сводит долги/авансы, печатает акт сверки.
([support: платежи](https://support.moysklad.ru/hc/ru/articles/203025346),
[ДДС](https://support.moysklad.ru/hc/ru/articles/360000199328),
[взаиморасчёты](https://support.moysklad.ru/hc/ru/отчеты/взаиморасчеты)).
| Аспект | МойСклад | У нас | Расхождение | Серьёзность | Предложение [владельцу] |
|---|---|---|---|---|---|
| 4 типа документов | вх/исх платёж + ПКО/РКО | `MoneyTransactions` (обобщённо) + внесение/выплата | 🟡 модель проще | 🟡 | **упростить осознанно**: один реестр «Деньги» вместо 4 типов — выгода для мелкой розницы |
| Взаиморасчёты | единый реестр документ+платёж | `CounterpartyAdjustments` + долги | 🟡 не сквозной реестр | 🟡 | свести все документы+оплаты в один баланс контрагента |
| Корректировка взаиморасчётов | есть | `CounterpartyAdjustmentsController` (WeOwe/TheyOwe) | совпадает по сути | — | — |
### 4.3 Перемещение (move)
**Как у МойСклада:** перемещает товар между складами при проведении; переносит
себестоимость (FIFO-слои). ([складской учёт](https://support.moysklad.ru/hc/ru/articles/360014957717)).
| Аспект | МойСклад | У нас | Расхождение | Серьёзность |
|---|---|---|---|---|
| −склад источник / +склад назначение | да | `TransfersController` (TransferOut/In) | совпадает | — |
| Перенос себестоимости | по FIFO-слоям | по средней (`Product.Cost`) | 🔴 (метод) | 🔴 |
| Видимость в простом режиме | есть | скрыт | ок (простой режим) | — |
---
## ПРИОРИТЕТ 5 — Справочники, касса, мобильное, режимы
### 5.1 Контрагенты, склады, точки, сотрудники/роли, счета
| Аспект | МойСклад | У нас | Расхождение | Серьёзность |
|---|---|---|---|---|
| Контрагенты | тип (покупатель/поставщик), реквизиты, баланс | `CounterpartiesController` | совпадает | — |
| Склады | несколько, остатки по складам | склады/точки (`StoresController`/`RetailPointsController`) | совпадает | — |
| Сотрудники/роли/права | роли и права, точки | роли+права+привязка точек (есть) | совпадает/детальнее | — |
| Счета (касса/банк/эквайринг) | счета и ДДС | `MoneyAccountsController` | совпадает | — |
### 5.2 Касса ALL MARKET (Avalonia) vs Касса МойСклад
**Касса МойСклад:** продажи, сдача, возвраты, открытие/закрытие смены, офлайн, весовой товар,
сканирование штрихкода камерой, смешанная оплата (нал+карта), 54-ФЗ (АТОЛ/ШТРИХ-М/Viki Print),
скидки/бонусы, кроссплатформенность (Win/Android/iOS/Linux).
([касса для мобильного](https://www.moysklad.ru/reshenija/mobilnaya-kassa/),
[АРМ кассира](https://www.moysklad.ru/programmy-dlja-biznesa/arm-kassira/)).
| Функция | Касса МойСклад | Наша касса (Avalonia) | Расхождение | Серьёзность |
|---|---|---|---|---|
| Офлайн | да | да (очередь синка) | совпадает | — |
| Весовой товар | да | да (весовые ШК) | совпадает | — |
| Смешанная оплата | да | да | совпадает | — |
| Возврат по чеку | да | да | совпадает | — |
| Смена/Z-отчёт | да | да | совпадает | — |
| Сканер камерой | да (смартфон) | сканер-HID/весы по COM | 🟡 (нет камеры) | 🟡 |
| Фискализация (ОФД РК; у МС — WebKassa/reKassa) | да | **заглушка** `NullFiscalProvider` — не реализовано (`Fiscal.cs`) | 🔴 блокер по закону РК (см. П2.3) | 🔴 |
| Платёжные терминалы (эквайринг) | интеграция (PAX/Verifone) | нет — ручной ввод суммы карты | 🟡 | 🟡 |
### 5.3 Мобильное «FM Склад» vs мобильное МойСклад
**Мобильный МойСклад:** приёмка, инвентаризация, остатки, сканирование, отгрузка — с телефона.
| Функция | МойСклад моб. | FM Склад (iOS) | Расхождение | Серьёзность |
|---|---|---|---|---|
| Остатки по складу | да | да (`/api/inventory/stock` с groupId) | совпадает | — |
| Приёмка с телефона | да | **нет** (нет mobile-эндпоинтов приёмки; только stock/barcodes/me) | 🟡 | 🟡 |
| Инвентаризация с телефона | да | post/unpost есть — переделать под новую модель | 🔴 (после §3.3) | 🔴 |
| Сканирование | да | штрихкоды + весовые | совпадает | — |
### 5.4 Простой/расширенный режим, настройки, импорт, онбординг
| Аспект | МойСклад | У нас | Расхождение | Серьёзность | Предложение [владельцу] |
|---|---|---|---|---|---|
| Простой режим | нет единого «простого режима» — много всего сразу | `IsAdvancedMode` + гейты | 🟢 лучше нас для старта | — | сохранить и усилить |
| Импорт | Excel/CSV, маркетплейсы | Excel/CSV + накладные по фото (OCR) | совпадает/лучше | — | — |
| Онбординг | есть, но тяжёлый | мастер онбординга | 🟢 возможность быть проще | — | «первая приёмка за 2 мин» |
---
## Наши тесты, закрепляющие НЕПРАВИЛЬНОЕ поведение (переписать под МойСклад)
| Тест | Что закрепляет | Как должно быть |
|---|---|---|
| full-chain **шаг 10** (инвентаризация) | «провёл инвентаризацию → остатки сразу изменились» | создать инвентаризацию → завершить → применить → остатки = факт через Оприходование/Списание |
| unit-тесты инвентаризации (`InventoriesController` движения) | движения `InventoryAdjustment` при Post | нет движений от инвентаризации; движения — у связанных опр/спис |
| unit `MovingAverageCost` | скользящее среднее как эталон COGS | при выборе FIFO — тесты на FIFO-слои и COGS на момент продажи |
| profit-тесты (если берут `Product.Cost`) | COGS = текущая средняя | COGS = снимок себестоимости на момент продажи |
| mobile API post/unpost инвентаризации | post = двигает остатки | post = завершить+применить (создать и провести опр/спис) |
Правило на будущее: **сначала сверка с МойСкладом (эта методика), потом реализация, потом
тесты под сверенное поведение** — а не тесты под уже написанный код.
---
# ПРОХОД 2 — факт-проверки, Казахстан, исправление неточностей прохода 1
Mac-Claude: проход 1 полезный, но поверхностный. Ниже — все «проверить» закрыты фактом
(код `файл:строка` и/или запрос на стенде `test.admin.food-market.kz`), казахстанская
специфика и корректировки неточностей.
## П2.1 Закрытие всех «проверить» (факты)
| Пункт (проход 1) | Факт (код/стенд) | Вывод |
|---|---|---|
| Возврат: ссылка-основание | `RetailSalesController.cs:44,90,238-239,296` — возврат несёт `ReferenceSaleId` + `ReferenceSaleNumber` на исходную продажу | ✅ ссылка-основание есть |
| Возврат: способ возврата | У возврата своё поле `Payment` (нал/карта); кассир выбирает. МойСклад принудительно тем же способом | 🟡 у нас гибче, но нет гарантии «тем же способом» |
| Z-отчёт: состав | `ShiftReportDto` (`IServerApi.cs:61`): sales(cash,card), returns(cash,card), expectedCash, actualCash, cashDifference | ✅ нал/безнал + сверка; ❌ **нет разбивки «по товарам»** (у МойСклада есть) |
| Долг поставщику при приёмке | `MoneyReportsController.cs:120-183` — долг = Σ приёмок(Posted) − Σ возвратов − Σ оплат + Σ корректировок | ✅ приёмка увеличивает долг (считается в отчёте) |
| −долг при возврате поставщику | тот же расчёт, Σ возвратов вычитается | ✅ возврат уменьшает долг |
| Обязательные поля товара | `ProductInput` (`CatalogDtos.cs:92`): `[Required] Name`, `UnitOfMeasureId`, `ProductGroupId` — обязательны | 🟡 **больше, чем у МойСклада** (там только наименование; ед. и группа — нет) |
| Приёмка с телефона | мобильных endpoint’ов приёмки нет (`docs/mobile-api.md` — только stock/barcodes/me) | ❌ приёмки с телефона нет (у МойСклада есть) |
| Платёжные терминалы | интеграций PAX/Verifone нет; «карта» — ручной способ оплаты в кассе | ❌ нет интеграции терминала |
| Фискализация | `Fiscal.cs` — `IFiscalProvider` + `NullFiscalProvider` (заглушка, чек без фискального номера) | ❌ фискализации нет (см. §П2.3) |
| «Нет единообразной отмены проведения» (утв. прохода 1) | **НЕВЕРНО.** Unpost есть у ВСЕХ: Supplies:518, Enters:284, Losses:313, Inventories:525, SupplierReturns:327, Transfers:324, Demands:290, RetailSales:828 | ✅ отмена проведения единообразна — **исправляю проход 1** |
| Розничная смена: автооткрытие | `CashShiftsController` Open/Close; продажа требует открытой смены; автооткрытия нет | 🟡 только ручное |
## П2.2 Себестоимость — факт-проверка и реальный пример Демо-магазина
**«У МойСклада FIFO» — подтверждено их справкой:** «В МоёмСкладе себестоимость считается по
принципу FIFO… при продаже в первую очередь списывается товар из самой ранней приемки»
([школа торговли, методы расчёта себестоимости](https://www.moysklad.ru/poleznoe/shkola-torgovli/metody-rascheta-sebestoimosti/);
[support 217544337](https://support.moysklad.ru/hc/ru/articles/217544337)).
**«В движение продажи пишется UnitCost = цена продажи» — проверено по коду:** да, причём **и в
опте, и в рознице/кассе**:
- опт: `DemandsController.cs:270,307` — `UnitCost: line.UnitPrice`;
- розница/касса: `RetailSalesController.cs:680,858` (RetailSale) и `:986,1039` (CustomerReturn) — `UnitCost: line.UnitPrice`.
НО: `StockMovement.UnitCost` при продаже = **цена продажи** и для прибыли **не используется** —
`ProfitReportController.cs:116-118` берёт COGS = `Quantity × Product.Cost` (текущее скользящее
среднее). То есть поле `UnitCost` в движении продажи бесполезно/вводит в заблуждение, а реальная
себестоимость в отчёте — **текущая средняя на момент построения отчёта**, а не на момент продажи.
**Реальный пример со стенда (Демо/импорт-каталог, запрос `/api/reports/profit`):**
- Товар «BIOBLAS шампунь д/волос 360мл»: текущая `Product.Cost` = **548.6441 ₸** (дробная →
смешана из 3 приёмок с разными ценами; `supplies total = 3`).
- Профит-отчёт по нему: выручка 3100, **себест 1097.2882** (= 2 × 548.6441), прибыль 2002.7118.
**В чём искажение:** 548.6441 — средняя **после всех 3 приёмок**. Если эти 2 единицы были проданы
**до** третьей (более дорогой/дешёвой) приёмки, FIFO отнёс бы на них цену ранней партии, а не
текущую среднюю. Поэтому:
1. показанная прибыль по прошлым продажам **не историческая** (считается ценой, которой на момент
продажи ещё не было);
2. при каждой новой приёмке того же товара средняя меняется → **прибыль за закрытые периоды
«плывёт» задним числом**.
Величина искажения = (текущая средняя − фактическая FIFO-цена партии) × проданное количество; на
товарах с разбросом закупочных цен (как BIOBLAS: 3 цены) она ненулевая и накапливается. На товарах
с единственной ценой закупки искажения нет. Точное число «насколько» получить от работающей системы
нельзя — FIFO у нас не реализован; это и есть аргумент за реализацию (или за осознанный выбор средней).
## П2.3 Касса и фискализация — Казахстан, а не 54-ФЗ
**Поправка:** в проходе 1 стоял «54-ФЗ» — это закон **РФ**. Мы в Казахстане; применяется закон РК.
**Как МойСклад работает в Казахстане:** есть отдельный **moysklad.kz** с интеграциями ОФД/ККМ РК.
Касса МойСклад встраивает облачную фискализацию: **WebKassa** (данные чека автоматически уходят в
WebKassa → ОФД) и **reKassa** (бесплатная облачная онлайн-ККМ РК, фискализация и электронные чеки);
упоминается Kassa24. С **1 апреля 2026** — переход на новый протокол ККМ **v2.0.3** для передачи в ОФД.
([МойСклад: интеграции для Казахстана](https://www.moysklad.ru/integratsii/dlya-kazakhstana/),
[WebKassa](https://www.moysklad.ru/integratsii/apps/webkkm-smartinnovations/),
[reKassa](https://www.moysklad.ru/integratsii/apps/rekassa-smartinnovations/),
[новый протокол ККМ](https://www.moysklad.kz/news/noviy-protokol-kkm/),
[онлайн-касса в РК 2026](https://www.moysklad.kz/poleznoe/shkola-torgovli/online-kassa-v-kazakhstane/)).
**Что требуется по закону РК для розничной кассы:** онлайн-ККМ с передачей данных в ОФД (фискальный
чек, QR). Без этого легально пробивать чеки нельзя.
**Наш статус:** `NullFiscalProvider` — фискализации нет, чек проходит без фискального номера
(`Fiscal.cs:5-15`, см. `docs/pos.md` «Фискализация»). 🔴 **блокер для легальной розницы РК.**
**Что умеет «Касса МойСклад» из реально нужного кассиру — и что у нас:**
| Функция кассира | Касса МойСклад | Наша касса (Avalonia) | Факт у нас |
|---|---|---|---|
| Скидки на позицию/чек | да | да (скидка на строку) | `SaleView`/`PaymentView` |
| Бонусы/лояльность | да | сервер есть (`LoyaltyCards/Programs`), в кассе — не подключено | 🟡 нет в кассе |
| Отложенные чеки (парковка) | да | да («Отложить»/«Отложенные») | `ParkCommand/ResumeCommand` |
| Поиск товара | да | да (поиск + сканер + весовые ШК) | `ScanCommand` |
| Весы | да | да (COM/serial) | `Hardware` |
| Печать чека | да | да (ESC/POS) | `Printing.cs` |
| Смешанная оплата | да | да (нал+карта) | `PaymentView` |
| Фискализация ОФД | да (WebKassa/reKassa) | ❌ заглушка | 🔴 |
| Сканер камерой смартфона | да | ❌ (внешний сканер/COM) | 🟡 |
| Платёжный терминал (эквайринг) | да | ❌ ручной ввод суммы карты | 🟡 |
**Вывод:** по функциям для кассира мы покрываем почти всё (скидки, отложенные, поиск, весы, печать,
смешанная оплата). Критичные пробелы — **фискализация ОФД РК** (🔴 закон) и **бонусы в кассе** (🟡),
плюс эквайринг-терминал и камера-сканер (🟡).
## П2.4 Казахстанская специфика по закону (ЭСФ/СНТ, маркировка, НДС, акциз)
Этого в проходе 1 не было вовсе. Для РК — высокая важность.
### НДС
С **01.01.2026** базовая ставка НДС в РК — **16 %** (повышена с 12 %, Налоговый кодекс №214-VIII);
льготная **5 %** на лекарства/медизделия; порог постановки на учёт — 10 000 МРП. Переход: оборот до
31.12.2025 — 12 %, с 01.01.2026 — 16 %.
([НДС 16% 2026](https://finproverka.kz/blog/nds-v-kazahstane-v-2026-godu-novaya-stavka-16-snizhennyj-porog-i-obnovlyonnye-pravila-registraczii/),
[ставки НДС и ККМ](https://promo.webkassa.kz/tpost/00bbm0fsr1-stavki-nds-s-2026-goda-chto-vazhno-uches)).
**У нас:** дефолт НДС для KZ = **16 %** (`SystemReferenceSeeder.cs:38` — `new("KZ","Казахстан","KZT",16m)`),
`Product.Vat` берётся из `Country.VatRate` при создании товара. То есть ставка **актуальна**.
Единственный нюанс — нет переходной логики «12 % до 31.12.2025 / 16 % после» по дате оборота
(для магазина, работающего в 2026, несущественно). **У МойСклада** ставка НДС на товаре/в документе — есть.
### Маркировка (ИС МПТ)
Обязательная маркировка в РК: **табак/сигареты** и жидкости для вейпов, **обувь** (с 01.11.2021),
**лекарства** (с 01.07.2024), **пиво** (кеги/бутылки с 01.02.2026, банки с 01.01.2027), **одежда/лёгкая
промышленность** и **моторные масла** (поэтапно 2026–2027). Розница при продаже маркированного товара
обязана **сканировать код и вывести его из оборота** (передать в ИС МПТ).
([маркировка в РК 2026](https://birsan.kz/blog/tpost/fetidpm511-markirovka-tovarov-v-kazahstane-s-2026-g),
[перечень товаров](https://adilet.zan.kz/rus/docs/P2000000568)).
**У МойСклада** — есть работа с маркировкой (создание карточек маркированного товара, вывод из оборота).
**У нас** — только флаг `Product.IsMarked` (`CatalogDtos.cs:92`), **вывода из оборота нет**. 🔴 если
магазин продаёт табак/обувь/лекарства.
### СНТ и ЭСФ
**СНТ** (сопроводительная накладная на товары) оформляется электронно в системе **ЭСФ**
(esf.gov.kz). Для розницы обязательна только по узкому перечню (~3 категории); в рознице процедуры
по СНТ в основном **автоматические без участия розничного продавца**; **обязательное подтверждение —
только по подакцизным товарам**; добровольно — до 10 числа следующего месяца.
([СНТ: Әділет](https://adilet.zan.kz/rus/docs/V2500037317),
[КГД: СНТ](https://www.gov.kz/memleket/entities/kgd/press/news/details/593973?lang=ru),
[правила СНТ 2026](https://pro1c.kz/articles/snt/pravila-snt/)).
**У нас** интеграции с ИС ЭСФ нет. Для маленького магазина без подакцизных — не блокер (авто); при
продаже подакцизных (табак/алкоголь) понадобится подтверждение СНТ. **У МойСклада KZ** интеграция с
ЭСФ/СНТ есть.
### Акциз/подакцизные
Табак, алкоголь — подакцизные; по ним обязательно подтверждение СНТ и (для табака/пива) маркировка.
**У нас** отметки «подакцизный товар» нет. 🟡
**Что малому магазину нужно в первую очередь:** (1) фискализация ОФД (K1 — иначе нельзя пробивать
чеки); (2) маркировка, если торгует сигаретами (K3); (3) корректная ставка НДС 16 % в чеке (K2).
СНТ/ЭСФ и акциз — по мере появления подакцизных категорий.
## П2.5 Исправления неточностей прохода 1
| Утверждение прохода 1 | На самом деле |
|---|---|
| «Нет единообразной отмены проведения» (топ-15 №5) | **Есть** у всех документов (unpost-эндпоинты, см. П2.1) — заменил пункт на «Фискализация РК» |
| «Взаиморасчёты — не сквозной реестр» (топ-15 №13) | Долги **считаются** из документов (приёмки−возвраты−оплаты+корректировки, `MoneyReportsController:120-183`); это близко к МойСкладу. Понизил до средней/UX; пункт топ-15 заменил на «Маркировка РК» |
| «54-ФЗ» в разделе кассы | Это закон РФ; у нас РК — см. П2.3 (ОФД РК, WebKassa/reKassa) |
| «Долг поставщику при приёмке / −долг при возврате — проверить» | Подтверждено: оба учитываются в отчёте долгов |
| «НДС 12%» (из задания) | С 01.01.2026 в РК — **16 %** (5 % лекарства); у нас ставка из `Country.VatRate` |
---
## Итоговая оценка объёма исправлений
- **Инвентаризация** (§3.3) — средний, уже в очереди (`wip/inventory-model`), план совпал с ревизией.
- **Себестоимость FIFO + партии + COGS-снимок** (§2.1–2.3, 3.2, 4.3) — **большой**, самый влиятельный
блок (остатки/прибыль/движения). Рекомендация: отдельная крупная задача после инвентаризации;
рассмотреть компромисс «среднее по умолчанию, FIFO — опция» [НА РЕШЕНИЕ ВЛАДЕЛЬЦА].
- **Связи «на основании» + статусы + отмена проведения** (§1,3,4) — средний, сквозная унификация.
- **Деньги/взаиморасчёты единый реестр** (§4.2) — средний, можно сделать проще МойСклада осознанно.
- **Касса/мобильное** (§5.2–5.3) — точечные дополнения (камера-сканер, терминалы),
средняя серьёзность.
- **🇰🇿 Казахстан по закону** (K1–K5, §П2.3–П2.4): **фискализация ОФД РК** (K1) — 🔴 блокер,
интеграция Webkassa/reKassa (средний–большой); **маркировка ИС МПТ** (K3) — большой, если
торгует табак/обувь/лекарствами; **НДС 16%** (K2) — малый (проверить дефолт/дату оборота);
**СНТ/ЭСФ** (K4) и **акциз** (K5) — средний, по подакцизным категориям.
## Источники (MoySklad)
- Себестоимость/методы: moysklad.ru/poleznoe/shkola-torgovli/metody-rascheta-sebestoimosti; support 217544337
- Приёмка: support 203325563 · Списание: support 203053096 · Возврат поставщику: support 230623667
- Инвентаризация: support (товары и склады/операции/инвентаризация_товаров); moysklad.ru/poleznoe/.../izlishki-pri-inventarizacii
- Остатки: support 203319073 · Складской учёт: support 360014957717
- Платежи: support 203025346 · ДДС: support 360000199328 · Взаиморасчёты: support (отчеты/взаиморасчеты)
- Розничные смены: moysklad.ru/news/715 · Возврат покупателя: moysklad.ru/poleznoe/.../kak-oformit-vozvrat-ot-pokupatelja
- Карточка товара: support (товары и склады/.../создание_карточки_товара); Типы цен: support 203053526
- Касса МойСклад: moysklad.ru/reshenija/mobilnaya-kassa; programmy-dlja-biznesa/arm-kassira
- API: dev.moysklad.ru/doc/api/remap/1.2 (#/documents/inventory, /enter, /loss)
## Источники (Проход 2 — Казахстан)
- МойСклад KZ: moysklad.kz · интеграции РК: moysklad.ru/integratsii/dlya-kazakhstana
- WebKassa: moysklad.ru/integratsii/apps/webkkm-smartinnovations; online-webkassa.kz · reKassa: moysklad.ru/integratsii/apps/rekassa-smartinnovations; rekassa.kz
- Новый протокол ККМ (01.04.2026): moysklad.kz/news/noviy-protokol-kkm · онлайн-касса РК: moysklad.kz/poleznoe/shkola-torgovli/online-kassa-v-kazakhstane
- НДС 16% (01.01.2026): finproverka.kz/blog/nds-v-kazahstane-v-2026-godu…; promo.webkassa.kz/…stavki-nds-s-2026-goda
- СНТ/ЭСФ: adilet.zan.kz/rus/docs/V2500037317; gov.kz/…/kgd/…/593973; pro1c.kz/articles/snt/pravila-snt
- Маркировка РК: birsan.kz/…markirovka-tovarov-v-kazahstane-s-2026; adilet.zan.kz/rus/docs/P2000000568
- Код (наш): RetailSalesController:680/858/986/1039 · DemandsController:270 · ProfitReportController:116-118 · MoneyReportsController:120-183 · CatalogDtos.cs:92 · Fiscal.cs · IServerApi.cs:61 · unpost: Supplies:518/Enters:284/Losses:313/Inventories:525/SupplierReturns:327/Transfers:324/Demands:290/RetailSales:828
- Стенд: /api/reports/profit (Демо) — BIOBLAS шампунь Product.Cost=548.6441, 3 приёмки