--- /dev/null
+План ручного тестирования — ERP-300J / Справочник закупщика
+
+Ветка: feature_fomichev_ERP-300J_suppliers_dict
+Разделы: Поставщики · Производители + Плантации · Маркировка · Маппинг товаров
+
+---
+0. Предусловия
+
+- Миграции применены: m260408_* (5 таблиц) + m260408_100500_* (junction) + m260409_120000_* (RBAC)
+- Пользователь авторизован и имеет права на раздел buyer-reference
+- Есть тестовые данные в products_1c_nomenclature (несколько товаров с category/subcategory/species)
+
+ ---
+1. Главная страница — 4 вкладки
+
+┌─────┬───────────────────────────────┬───────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│ # │ Действие │ Ожидаемый результат │
+├─────┼────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ 1.1 │ Открыть /buyer-reference │ Страница загружается, видны 4 таба: Поставщики / Производители и плантации / Маркировка / Маппинг товаров │
+├─────┼────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ 1.2 │ Переключаться между вкладками │ Каждая вкладка подгружает контент без перезагрузки страницы │
+├─────┼────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ 1.3 │ Обновить страницу на вкладке N │ Активная вкладка остаётся активной (если сохраняется в URL-хэше) │
+└─────┴────────────────────────────────┴───────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+
+---
+2. Вкладка «Поставщики»
+
+2.1 Создание
+
+┌───────┬───────────────────────────────────────────────────────────────────────────────────────────────────────────┬──────────────────────────────────────────────────────┐
+│ # │ Действие │ Ожидаемый результат │
+├───────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────┤
+│ 2.1.1 │ Нажать «Добавить поставщика», заполнить все поля корректно (name, тип, валюта, срок поставки) → Сохранить │ Запись появляется в таблице, тост «Поставщик создан» │
+├───────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────┤
+│ 2.1.2 │ Создать поставщика с существующим именем │ Ошибка: «Поставщик уже существует» │
+├───────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────┤
+│ 2.1.3 │ Отправить форму с пустым именем │ Ошибка валидации на поле name │
+├───────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────┤
+│ 2.1.4 │ Ввести lead_time_days = -1 │ Ошибка: «Срок поставки должен быть >= 0» │
+├───────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────┤
+│ 2.1.5 │ Проверить дропдауны Тип (2 опции) и Валюта (3 опции) │ Варианты: local/international; RUB/EUR/USD │
+└───────┴───────────────────────────────────────────────────────────────────────────────────────────────────────────┴──────────────────────────────────────────────────────┘
+
+2.2 Редактирование
+
+┌───────┬───────────────────────────────────────────────────┬─────────────────────────────────────────────┐
+│ # │ Действие │ Ожидаемый результат │
+├───────┼───────────────────────────────────────────────────────────┼─────────────────────────────────────────────┤
+│ 2.2.1 │ Нажать «Редактировать» на существующем поставщике │ Модалка открывается с заполненными данными │
+├───────┼───────────────────────────────────────────────────────────┼─────────────────────────────────────────────┤
+│ 2.2.2 │ Изменить name → Сохранить │ Таблица обновляется, новое имя отображается │
+├───────┼───────────────────────────────────────────────────────────┼─────────────────────────────────────────────┤
+│ 2.2.3 │ Попытаться сохранить имя другого существующего поставщика │ Ошибка уникальности │
+└───────┴───────────────────────────────────────────────────────────┴─────────────────────────────────────────────┘
+
+2.3 Деактивация (soft delete + каскад)
+
+┌───────┬─────────────────────────────────────────────────────────────────────────────────┬────────────────────────────────────────────────────────────────────────┐
+│ # │ Действие │ Ожидаемый результат │
+├───────┼─────────────────────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────┤
+│ 2.3.1 │ Деактивировать поставщика без связанных маркировок и маппингов │ Тост: «деактивирован. Связанных записей: маркировки — 0, маппинги — 0» │
+├───────┼─────────────────────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────┤
+│ 2.3.2 │ Деактивировать поставщика у которого есть активные маркировки (создать заранее) │ Тост с корректным счётчиком: «маркировки — N» │
+├───────┼─────────────────────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────┤
+│ 2.3.3 │ Проверить в БД / вкладке «Маркировка», что связанные маркировки стали is_active = false │ Маркировки деактивированы │
+├───────┼─────────────────────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────┤
+│ 2.3.4 │ Деактивировать уже неактивного поставщика │ Ошибка: «Поставщик уже деактивирован» │
+└───────┴─────────────────────────────────────────────────────────────────────────────────────────┴────────────────────────────────────────────────────────────────────────┘
+
+2.4 Фильтрация
+
+┌───────┬────────────────────────────────────────┬────────────────────────────────────────────┐
+│ # │ Действие │ Ожидаемый результат │
+├───────┼────────────────────────────────────────┼────────────────────────────────────────────┤
+│ 2.4.1 │ Ввести текст в поле поиска по name │ Таблица фильтруется │
+├───────┼────────────────────────────────────────┼────────────────────────────────────────────┤
+│ 2.4.2 │ Фильтр по типу (local / international) │ Показываются только соответствующие записи │
+└───────┴────────────────────────────────────────┴────────────────────────────────────────────┘
+
+---
+3. Вкладка «Производители и плантации»
+
+3.1 Производители — Создание / Редактирование
+
+┌───────┬──────────────────────────────────────────────┬──────────────────────────────────────────────────────────┐
+│ # │ Действие │ Ожидаемый результат │
+├───────┼──────────────────────────────────────────────┼──────────────────────────────────────────────────────────┤
+│ 3.1.1 │ Создать производителя с уникальным именем │ Запись в таблице, тост «Производитель создан» │
+├───────┼──────────────────────────────────────────────┼──────────────────────────────────────────────────────────┤
+│ 3.1.2 │ Создать производителя с дублирующимся именем │ Ошибка: «Производитель с таким названием уже существует» │
+├───────┼──────────────────────────────────────────────┼──────────────────────────────────────────────────────────┤
+│ 3.1.3 │ Отредактировать имя │ Таблица обновляется │
+└───────┴──────────────────────────────────────────────┴──────────────────────────────────────────────────────────┘
+
+3.2 Производители — Деактивация (блокировка)
+
+┌───────┬───────────────────────────────────────────────────────┬────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│ # │ Действие │ Ожидаемый результат │
+├───────┼─────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ 3.2.1 │ Деактивировать производителя без активных маркировок │ Тост с количеством деактивированных плантаций │
+├───────┼─────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ 3.2.2 │ Деактивировать производителя с активными маркировками │ Ошибка: «Нельзя деактивировать производителя: есть активные маркировки. Сначала деактивируйте их.» │
+├───────┼─────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ 3.2.3 │ В БД проверить, что плантации производителя стали is_active = false │ Каскад сработал корректно │
+└───────┴─────────────────────────────────────────────────────────────────────┴────────────────────────────────────────────────────────────────────────────────────────────────────┘
+
+3.3 Плантации
+
+┌───────┬────────────────────────────────────────────────────────────┬────────────────────────────────────────────────────────────┐
+│ # │ Действие │ Ожидаемый результат │
+├───────┼────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────┤
+│ 3.3.1 │ Создать плантацию через кнопку «Добавить плантацию» │ В форме доступен выпадающий список активных производителей │
+├───────┼────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────┤
+│ 3.3.2 │ Создать плантацию через кнопку «+» на строке производителя │ Поле producer_id предзаполнено │
+├───────┼────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────┤
+│ 3.3.3 │ Редактировать плантацию │ Данные сохраняются │
+├───────┼────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────┤
+│ 3.3.4 │ Деактивировать плантацию без связанных маркировок │ Тост «Плантация деактивирована» │
+├───────┼────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────┤
+│ 3.3.5 │ Деактивировать уже неактивную плантацию │ Ошибка: «Плантация уже деактивирована» │
+└───────┴────────────────────────────────────────────────────────────┴────────────────────────────────────────────────────────────┘
+
+---
+4. Вкладка «Маркировка»
+
+4.1 Создание
+
+┌───────┬───────────────────────────────────────────────────────────┬──────────────────────────────────────────────────────────────────┐
+│ # │ Действие │ Ожидаемый результат │
+├───────┼───────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────┤
+│ 4.1.1 │ Создать маркировку с корректным кодом (напр. SCH-RP-RN50) │ Запись создана │
+├───────┼───────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────┤
+│ 4.1.2 │ Ввести код строчными буквами (напр. sch-rp) │ Код сохраняется в верхнем регистре (SCH-RP) благодаря strtoupper │
+├───────┼───────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────┤
+│ 4.1.3 │ Ввести код с недопустимым символом (напр. SCH_RP) │ Ошибка: «Код: 3-12 символов, только A-Z, 0-9 и дефис» │
+├───────┼───────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────┤
+│ 4.1.4 │ Ввести код короче 3 символов (напр. AB) │ Ошибка валидации │
+├───────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────┤
+│ 4.1.5 │ Ввести код длиннее 12 символов │ Ошибка валидации │
+├───────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────┤
+│ 4.1.6 │ Создать маркировку с дублирующимся кодом │ Ошибка: «Маркировка с таким кодом уже существует» │
+├───────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────┤
+│ 4.1.7 │ Создать маркировку с такой же комбинацией (producer_id + plantation_id + product_name) при другом коде │ Ошибка уникального индекса │
+├───────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────┤
+│ 4.1.8 │ Выбрать производителя — проверить, что в дропдауне «Плантация» появились только его плантации │ Каскадный AJAX-запрос /marking/plantations-by-producer работает │
+└───────┴────────────────────────────────────────────────────────────────────────────────────────────────────────┴──────────────────────────────────────────────────────────────────┘
+
+4.2 Деактивация (блокировка)
+
+┌───────┬─────────────────────────────────────────────────────┬────────────────────────────────────────────────────────────────────────────────────────────────┐
+│ # │ Действие │ Ожидаемый результат │
+├───────┼─────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ 4.2.1 │ Деактивировать маркировку без привязанных маппингов │ Тост «Маркировка деактивирована» │
+├───────┼─────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ 4.2.2 │ Деактивировать маркировку с привязанными маппингами │ Ошибка: «Нельзя деактивировать маркировку: есть N привязанных маппингов. Сначала отвяжите их.» │
+└───────┴─────────────────────────────────────────────────────┴────────────────────────────────────────────────────────────────────────────────────────────────┘
+
+---
+5. Вкладка «Маппинг товаров»
+
+5.1 Список и пагинация
+
+┌───────┬───────────────────────────────────┬───────────────────────────────────────────────────────┐
+│ # │ Действие │ Ожидаемый результат │
+├───────┼───────────────────────────────────┼───────────────────────────────────────────────────────┤
+│ 5.1.1 │ Открыть вкладку │ Отображаются карточки товаров, блок аналитики в шапке │
+├───────┼───────────────────────────────────┼───────────────────────────────────────────────────────┤
+│ 5.1.2 │ Сменить per-page (50 / 100 / 500) │ Количество карточек на странице меняется │
+├───────┼───────────────────────────────────┼───────────────────────────────────────────────────────┤
+│ 5.1.3 │ Перейти на страницу 2 │ Корректная пагинация, другие товары │
+└───────┴───────────────────────────────────┴───────────────────────────────────────────────────────┘
+
+5.2 Фильтры
+
+┌───────┬──────────────────────────────────────────────────────────────────┬──────────────────────────────────────────────┐
+│ # │ Действие │ Ожидаемый результат │
+├───────┼──────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────┤
+│ 5.2.1 │ Выбрать категорию → дропдаун «Подкатегория» заполняется (каскад) │ AJAX-запрос /product-mapping/cascade-filters │
+├───────┼──────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤
+│ 5.2.2 │ Выбрать подкатегорию → дропдаун «Вид» заполняется │ Каскад второго уровня │
+├───────┼──────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────┤
+│ 5.2.3 │ Ввести текст в поиск → применить фильтр │ Показываются только товары, содержащие текст │
+├───────┼──────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────┤
+│ 5.2.4 │ Фильтр по поставщику │ Показываются только товары с маппингом на выбранного поставщика │
+├───────┼──────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────┤
+│ 5.2.5 │ Фильтр по маркировке │ Показываются только товары с маппингом, содержащим выбранную маркировку │
+├───────┼──────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────┤
+│ 5.2.6 │ Фильтр «Только без маппинга» │ Показываются только товары без записей в product_mappings │
+├───────┼──────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────┤
+│ 5.2.7 │ Комбинация 2+ фильтров │ Результат пересечения условий │
+├───────┼──────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────┤
+│ 5.2.8 │ Сбросить фильтры │ Показываются все товары │
+└───────┴──────────────────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────────────────────┘
+
+5.3 Аналитика
+
+┌───────┬─────────────────────────────────────────────────────────────────────────────┬────────────────────────────────────────────────────────────────────────┐
+│ # │ Действие │ Ожидаемый результат │
+├───────┼─────────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────┤
+│ 5.3.1 │ Применить фильтр │ Блок аналитики пересчитывается через AJAX (/product-mapping/analytics) │
+├───────┼─────────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────┤
+│ 5.3.2 │ Проверить корректность % (with_supplier, single_supplier, without_producer) │ Значения математически согласованы с данными таблицы │
+└───────┴─────────────────────────────────────────────────────────────────────────────┴────────────────────────────────────────────────────────────────────────┘
+
+5.4 Создание маппинга
+
+┌───────┬────────────────────────────────────────────────────────────────────────────────────────────┬──────────────────────────────────────────────┐
+│ # │ Действие │ Ожидаемый результат │
+├───────┼────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────┤
+│ 5.4.1 │ Нажать «+» на карточке товара → заполнить форму (поставщик, название, квант=1) → Сохранить │ Маппинг появляется в таблице внутри карточки │
+├───────┼────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────┤
+│ 5.4.2 │ Указать квант = 0 │ Ошибка: «Кратность должна быть >= 1» │
+├───────┼───────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────┤
+│ 5.4.3 │ Не заполнить «Название у поставщика» │ Ошибка обязательного поля │
+├───────┼───────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────┤
+│ 5.4.4 │ Создать дублирующий маппинг (тот же товар + поставщик + без плантации) │ Ошибка уникальности │
+├───────┼───────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────┤
+│ 5.4.5 │ Создать маппинг с плантацией, создать ещё один с тем же товаром + поставщик + та же плантация │ Ошибка уникальности │
+├───────┼───────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────┤
+│ 5.4.6 │ Создать маппинг с тем же товаром + поставщик, но разными плантациями │ Успешно (разные partial-unique индексы) │
+├───────┼───────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────┤
+│ 5.4.7 │ Создать маппинг с несколькими маркировками (multiselect) │ В БД записи в mapping_markings для всех выбранных ID │
+└───────┴───────────────────────────────────────────────────────────────────────────────────────────────┴──────────────────────────────────────────────────────┘
+
+5.5 Редактирование маппинга
+
+┌───────┬─────────────────────────────────────────────────────────┬────────────────────────────────────────────────────────────┐
+│ # │ Действие │ Ожидаемый результат │
+├───────┼─────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────┤
+│ 5.5.1 │ Открыть форму редактирования │ Форма заполнена, в multiselect отмечены текущие маркировки │
+├───────┼─────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────┤
+│ 5.5.2 │ Изменить поставщика, убрать одну маркировку → Сохранить │ Данные обновляются, в mapping_markings убрана запись │
+├───────┼─────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────┤
+│ 5.5.3 │ Снять все маркировки → Сохранить │ В mapping_markings нет записей для этого mapping_id │
+└───────┴─────────────────────────────────────────────────────────┴────────────────────────────────────────────────────────────┘
+
+5.6 Удаление маппинга
+
+┌───────┬──────────────────────────────────────────────────────┬────────────────────────────────────────────────────────────────────────────────┐
+│ # │ Действие │ Ожидаемый результат │
+├───────┼──────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤
+│ 5.6.1 │ Удалить маппинг с привязанными маркировками │ Маппинг удалён, записи в mapping_markings удалены каскадно (ON DELETE CASCADE) │
+├───────┼──────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤
+│ 5.6.2 │ Повторный запрос на удаление уже удалённого маппинга │ 404 или ошибка «Маппинг не найден» │
+└───────┴──────────────────────────────────────────────────────┴────────────────────────────────────────────────────────────────────────────────┘
+
+5.7 Экспорт в XLSX
+
+┌───────┬─────────────────────────────────────────────────────────────────────────────────────────┬─────────────────────────────────────────────────────────┐
+│ # │ Действие │ Ожидаемый результат │
+├───────┼─────────────────────────────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────┤
+│ 5.7.1 │ Нажать «Экспорт» без фильтров │ Файл product-mapping-YYYY-MM-DD_HHmmss.xlsx скачивается │
+├───────┼─────────────────────────────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────┤
+│ 5.7.2 │ Открыть файл: проверить заголовки (12 столбцов), жирный + серый фон, заморозка строки 1 │ Форматирование соответствует │
+├───────┼─────────────────────────────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────┤
+│ 5.7.3 │ Товар без маппинга в выборке │ В файле строка с пустыми полями маппинга │
+├───────┼─────────────────────────────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────┤
+│ 5.7.4 │ Товар с 2 маппингами → в файле 2 строки │ Разворачивание маппингов корректно │
+├───────┼─────────────────────────────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────┤
+│ 5.7.5 │ Экспорт с активным фильтром (напр. по поставщику) │ В файле только отфильтрованные товары │
+├───────┼─────────────────────────────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────┤
+│ 5.7.6 │ Проверить, что temp-файл удалён после отправки │ Файл в @runtime отсутствует (хук EVENT_AFTER_SEND) │
+└───────┴─────────────────────────────────────────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────┘
+
+---
+6. Консольная команда — ProductMappingCommand
+
+┌─────┬────────────────────────────────────────────────────────────────────────────────┬─────────────────────────────────────────────────────────────────────────────────────────┐
+│ # │ Команда │ Ожидаемый результат │
+├─────┼────────────────────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────┤
+│ 6.1 │ php yii product-mapping/find-orphans │ Выводит список маппингов без товара в 1С (или «Orphan-маппинги не найдены») │
+├─────┼────────────────────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────┤
+│ 6.2 │ php yii product-mapping/delete-orphans (предварительно создать orphan вручную) │ Удаляет orphan-маппинги, выводит количество; в mapping_markings каскадно удалены записи │
+└─────┴────────────────────────────────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────────────────────────────────────┘
+
+---
+7. RBAC / Права доступа
+
+┌─────┬─────────────────────────────────────────────────────────────────────┬─────────────────────┐
+│ # │ Сценарий │ Ожидаемый результат │
+├─────┼─────────────────────────────────────────────────────────────────────┼────────────────────────┤
+│ 7.1 │ Пользователь без прав на buyer-reference открывает /buyer-reference │ 403 или редирект │
+├─────┼─────────────────────────────────────────────────────────────────────┼────────────────────────┤
+│ 7.2 │ Пользователь без прав на supplier/create выполняет POST │ 403 │
+├─────┼─────────────────────────────────────────────────────────────────────┼────────────────────────┤
+│ 7.3 │ Пользователь с правами — все действия доступны │ Нет лишних ограничений │
+└─────┴─────────────────────────────────────────────────────────────────────┴────────────────────────┘
+
+---
+8. Регрессия — вкладка «Бюджет» (style.css / index.js изменены)
+
+┌─────┬────────────────────────────────────────────┬───────────────────────────────────────────────────┐
+│ # │ Действие │ Ожидаемый результат │
+├─────┼────────────────────────────────────────────┼───────────────────────────────────────────────────┤
+│ 8.1 │ Открыть раздел «Бюджет» │ Страница отображается корректно, стили не сломаны │
+├─────┼────────────────────────────────────────────┼───────────────────────────────────────────────────┤
+│ 8.2 │ Интерактивные элементы бюджета (если есть) │ Работают как прежде │
+└─────┴────────────────────────────────────────────┴───────────────────────────────────────────────────┘
+
+---
+Критические сценарии (приоритет 1)
+
+1. Каскадная деактивация поставщика → маркировки + маппинги деактивируются
+2. Блокировка деактивации производителя при наличии активных маркировок
+3. Блокировка деактивации маркировки при наличии привязанных маппингов
+4. Уникальность partial-индекса маппинга (с плантацией / без плантации)
+5. syncMarkings корректно удаляет старые связи и создаёт новые
+6. Экспорт XLSX удаляет temp-файл после отправки
+
+
+Миграции БД
+
+erp24/migrations/m260408_100000_create_suppliers_table.php
+erp24/migrations/m260408_100100_create_producers_table.php
+erp24/migrations/m260408_100200_create_plantations_table.php
+erp24/migrations/m260408_100300_create_markings_table.php
+erp24/migrations/m260408_100400_create_product_mappings_table.php
+erp24/migrations/m260408_100500_create_mapping_markings_table.php
+erp24/migrations/m260409_120000_add_erp300_rbac_permissions.php
+
+Модели (Records)
+
+erp24/records/Supplier.php
+erp24/records/SupplierSearch.php
+erp24/records/Producer.php
+erp24/records/ProducerSearch.php
+erp24/records/Plantation.php
+erp24/records/Marking.php
+erp24/records/MarkingSearch.php
+erp24/records/ProductMapping.php
+erp24/records/MappingMarking.php
+
+Контроллеры
+
+erp24/controllers/BuyerReferenceController.php
+erp24/controllers/SupplierController.php
+erp24/controllers/ProducerController.php
+erp24/controllers/MarkingController.php
+erp24/controllers/ProductMappingController.php
+erp24/commands/ProductMappingController.php
+
+Сервисы и формы
+
+erp24/services/ProductMappingService.php
+erp24/forms/ProductMappingFilterForm.php
+
+Views
+
+erp24/views/buyer-reference/index.php
+erp24/views/supplier/index.php
+erp24/views/supplier/_form.php
+erp24/views/producer/index.php
+erp24/views/producer/_producer_form.php
+erp24/views/producer/_plantation_form.php
+erp24/views/marking/index.php
+erp24/views/marking/_form.php
+erp24/views/product-mapping/index.php
+erp24/views/product-mapping/_card.php
+erp24/views/product-mapping/_form.php
+erp24/views/product-mapping/_filters.php
+erp24/views/product-mapping/_analytics.php
+
+Frontend
+
+erp24/web/js/product-mapping/index.js
+erp24/web/js/budget/index.js
+erp24/web/css/budget/style.css
+
+Тесты
+
+erp24/tests/unit/records/SupplierTest.php
+erp24/tests/unit/records/ProducerTest.php
+erp24/tests/unit/records/PlantationTest.php
+erp24/tests/unit/records/MarkingTest.php
+erp24/tests/unit/records/ProductMappingTest.php
+erp24/tests/unit/forms/ProductMappingFilterFormTest.php
+erp24/tests/unit/services/ProductMappingServiceTest.php
+erp24/tests/unit/commands/ProductMappingCommandTest.php
+erp24/tests/unit/controllers/Erp300ControllersTest.php
+erp24/tests/unit/migrations/Erp300RbacMigrationTest.php
+
+Документация
+
+docs/jira/ERP-318/debate-log.md
+docs/jira/ERP-318/interview.md
+docs/jira/ERP-318/plan.md
+docs/jira/ERP-318/prd.md
+docs/jira/ERP-318/spec.md