# 13. Дополнение к ТЗ: продуктовые требования и уточнения MVP

Статус: проект для согласования. Этот документ дополняет документы `01`–`12`; при противоречии в вопросах ниже его требования имеют приоритет.

## 0. Согласованные решения

- Целевой рынок первого запуска — Украина; весь пользовательский интерфейс и обязательные юридические тексты доступны на украинском и русском языках.
- MVP публично заявляет поддержку только VAG. Архитектура и матрица покрытия готовятся к поэтапному подключению основных марок украинского рынка, но марка становится доступной лишь после прохождения её vertical slice и проверок.
- После подбора пользователь либо открывает страницу подтверждающего источника, либо отправляет анонимную заявку менеджеру проекта. Аккаунты, гараж, избранное и история не входят в ранние этапы.
- До заключения договоров используются только публично доступные источники в пределах явно разрешённого сценария; доступность страницы сама по себе не является разрешением на автоматизированный доступ.
- Подбор на первом этапе носит рекомендательный характер. Целевой показатель пилота: не более 10% ошибочных рекомендаций в контрольной выборке, предпочтительный уровень — до 5%. Метрика считается только по результатам, для которых последующая ручная проверка или подтверждённый источник дали ground truth; она не является публичной гарантией совместимости.

## 1. Что создаёт продукт

Сервис помогает пользователю найти запчасть для конкретного автомобиля, последовательно проведя его по цепочке:

```text
VIN / FRAME / OEM
  → идентификация автомобиля
  → подтверждение варианта автомобиля
  → каталог и схема
  → OEM-деталь
  → замены / аналоги / предложения (если доступны)
```

Главная ценность — не просто поиск номера, а прозрачное объяснение, **насколько деталь применима к выбранному автомобилю** и откуда взяты сведения. Сервис не должен обещать стопроцентную совместимость, если источник не подтвердил комплектацию по конкретному VIN.

### 1.1. Границы MVP

В релиз MVP входит один полностью проверенный вертикальный сценарий VAG, включая VIN, каталог, минимум одну схему, OEM и аналоги. Другие марки и японские каталоги не объявляются поддерживаемыми до фактической проверки адаптера.

В MVP не входят без отдельного решения:

- оформление заказа, оплата и доставка;
- обещание наличия или цены без интеграции с поставщиком;
- массовое скачивание каталогов;
- загрузка, хранение или перепубликация OEM-иллюстраций без права на это;
- кабинет поставщика, CRM и рекомендации на основе покупок.

Анонимная заявка менеджеру по уже найденной детали входит в MVP. Она не является корзиной, бронированием, обещанием цены или наличия.

## 2. Матрица покрытия и честность результата

### 2.1. Реестр поддерживаемых возможностей

В админ-разделе и на публичной странице должен существовать реестр покрытия. Для каждой марки/источника он показывает:

- марку и рынок;
- поддерживаемые входные данные: VIN, FRAME, OEM, aftermarket;
- доступность групп, схем, OEM, замен и аналогов;
- статус: `active`, `degraded`, `disabled`, `planned`;
- дату последней успешной проверки.

Публичный интерфейс не должен заявлять «поиск по VIN для всех авто», пока это не подтверждено матрицей покрытия.

### 2.2. Статус применимости

Каждая строка детали и результат поиска получают машинный `fitment_status` и понятный текст:

| Статус | Значение для пользователя |
| --- | --- |
| `vin_confirmed` | Источник связал деталь с конкретным VIN/FRAME или его точной комплектацией. |
| `catalog_fit` | Деталь найдена в каталоге выбранной версии автомобиля, но отдельная VIN-проверка не подтверждена. |
| `model_range_only` | Есть применимость к модели/периоду, без подтверждения конкретной модификации. |
| `unverified` | Номер или аналог найден, но применимость не подтверждена. |
| `conflict` | Источники дают несовместимые сведения; требуется ручная проверка. |
| `not_fit` | Источник явно сообщает о неприменимости. |

Нельзя повышать статус на основании эвристики. Аналог без подтверждённой применимости всегда отображается как `unverified` или `model_range_only`.

### 2.3. Неоднозначный автомобиль

Если VIN/FRAME возвращает несколько вариантов, пользователь обязан выбрать вариант до показа каталога. Карточка выбора содержит все доступные различающие признаки: бренд, модель, поколение, период, двигатель, коробку, рынок и коды комплектации.

Выбранная связка `vehicle_id + source_id + source_key` сохраняется в сессии или читаемом URL. Нельзя строить каталог по одному только внутреннему `vehicle_id`, если у него несколько source mappings.

## 3. Пользовательские сценарии

### 3.1. VIN/FRAME

1. Пользователь вводит значение в единую строку поиска.
2. Сервис нормализует ввод, определяет тип и проверяет базовую корректность.
3. Сервис сначала ищет свежий cache, затем использует доступные источники в заданном порядке.
4. Пользователь получает один автомобиль или выбирает из вариантов.
5. На странице автомобиля видит атрибуты, источник, время данных и предупреждения о неполных данных.
6. Открывает группу, схему и OEM-деталь.

Проверка VIN не должна отклонять валидные VIN только потому, что не проходит контрольная цифра: в разных рынках и у старых автомобилей она может отсутствовать или использоваться иначе. Символы `I`, `O`, `Q` в стандартном 17-символьном VIN следует подсветить как вероятную ошибку ввода.

### 3.2. OEM / aftermarket

1. Пользователь вводит номер детали в произвольном формате.
2. Сервис сохраняет оригинальный ввод и создаёт нормализованный вариант только по правилам конкретной марки.
3. Результат показывает OEM/производитель, замены, источники, применимость и аналоги.
4. Если конкретный автомобиль уже выбран, аналоги дополнительно маркируются по `fitment_status` для него.

Поиск по OEM без выбранного автомобиля не должен утверждать, что деталь «подходит».

### 3.3. Нет результата или источник недоступен

- Не найдено: показать нормализованный запрос, краткое объяснение и альтернативный формат ввода; не возвращать пустую белую страницу.
- Источник временно недоступен: использовать fallback; при отсутствии fallback показать ранее сохранённые данные как `stale` либо понятное сообщение.
- Время ожидания одного интерактивного запроса ограничено общим budget 20–25 секунд, а не суммой таймаутов всех источников.
- Частичный результат допустим, но всегда содержит предупреждение и provenance.

## 4. Требования к данным и каталогу

### 4.1. Provenance и версия данных

У каждой отображаемой сущности обязательны `source`, `source_url`, `fetched_at`, а также `data_status` (`fresh`, `stale`, `partial`). При агрегации нельзя терять источник отдельного поля или строки.

Поле, подтверждённое разными источниками, хранит все подтверждения. Нормализованное отображаемое значение выбирается сервисом по приоритету источника, но исходные значения не перезаписываются.

### 4.2. Схемы и позиции

- Схема принадлежит конкретной source mapping автомобиля, а не только внутреннему автомобилю.
- Смешивать строки деталей из разных схем/источников в одну визуальную схему запрещено.
- Если изображение нельзя законно и технически использовать, показываются таблица позиций, источник и внешняя ссылка «Открыть оригинальную схему».
- Для позиции хранятся: номер, OEM, наименование, количество, период, примечание, коды комплектации, замена, применимость и provenance.

### 4.3. Отдельные типы данных

Постоянные свойства детали, связи OEM/analog, динамические предложения продавца, цена и остаток — разные сущности с разными TTL. Предложения нельзя сохранять в таблице `parts` и нельзя показывать без времени обновления, валюты и статуса актуальности.

Если после согласования понадобится собственная витрина предложений, добавить таблицы `sellers`, `part_offers`, `offer_prices`, `offer_stocks`; до этого MVP ограничивается ссылками на внешний источник.

### 4.4. Уточнения схемы БД

До реализации миграций необходимо дополнить исходную схему:

- внешние ключи с выбранной политикой удаления для всех связей; индексы по всем FK;
- уникальность, исключающая дубли групп, схем и позиций в рамках `source_id + source_key`;
- `source_key` или отдельная mapping-таблица у диаграмм, чтобы фиксировать источник и вариант авто;
- журнал административных изменений `source_audit_log` (кто, что, когда и почему включил/выключил);
- поле/таблица состояния источника для последовательных ошибок, `degraded` и пробного запроса;
- таблица или защищённое хранилище счётчиков public API rate limit;
- пагинация и лимит размера для потенциально больших списков деталей/аналогов.

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

## 5. Источники и правовой контроль

### 5.1. Карточка источника до разработки адаптера

Перед включением любого источника формируется запись с:

- владельцем и доменами whitelist;
- ссылкой на условия использования/robots и датой проверки;
- разрешёнными сценариями и частотой запросов;
- поддерживаемыми методами интерфейса;
- TTL, fallback priority и ответственным за адаптер;
- запретом/разрешением на thumbnail, raw HTML и предложения;
- минимальным набором fixtures и parser tests.

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

### 5.2. Конфликт конфигурации

В исходном ТЗ одновременно есть `enabled` в `config/sources.php` и `sources.enabled` в БД. Вводится правило:

- config — статический allow-list и безопасные технические параметры; `enabled=false` в нём является безусловным отключением;
- БД — операционное включение, приоритет и состояние circuit breaker;
- effective enabled = разрешён config **и** включён в БД **и** не истёк `blocked_until`.

Изменения через `/admin/` меняют только операционное состояние в БД и фиксируются в audit log. Секреты и cookies не хранятся в БД и Git.

## 6. API-контракт и ограничения

Все API-ответы включают `request_id`, `data_status`, `warnings` и provenance. Версия контракта фиксируется в `meta.api_version`.

Минимальные правила:

- 400 — ошибка параметров; 401/403 — доступ запрещён; 404 — сущность отсутствует; 429 — лимит нашего API; 503 — нет доступных источников; 504 — исчерпан общий бюджет ожидания;
- ответ с HTTP 200 и пустыми данными допустим только для корректного поиска без результата, с явным кодом в `meta`/`error`;
- внутренние сообщения parser/HTTP не передаются клиенту;
- списки имеют `limit`, `cursor`/`page` и максимальный размер;
- внешняя ссылка отдаётся только из whitelist и не принимается от frontend;
- `source_status` и действия управления источниками доступны только admin;
- публичные search endpoints защищены rate limit, проверкой размера/формата запроса и защитой от автоматического перебора.

## 7. UX, доступность и доверие

- Поисковая строка объясняет поддерживаемые форматы, показывает пример и сохраняет исходный ввод в поле при ошибке.
- До открытия внешней страницы рядом с ссылкой всегда показывается название источника.
- Статусы `vin_confirmed`, `catalog_fit`, `unverified`, `stale` и `conflict` заметно различаются текстом, не только цветом.
- На странице детали есть краткое предупреждение: перед заказом сверить OEM, комплектацию и применимость; для `conflict` — явная рекомендация ручной проверки.
- Ошибки, загрузка и результаты доступны с клавиатуры, имеют читаемые подписи и корректный контраст.
- Все поисковые URL с VIN получают `noindex, nofollow`; страницы не содержат общего публичного списка чужих VIN-запросов.
- Язык, валюта, формат номера и выбранный рынок должны быть явно определены до UI-работ; если интерфейс мультиязычный, строки не встраиваются жёстко в PHP/JS.
- Parts-сервис использует узнаваемые брендовые элементы Tire Land, но не копирует основной сайт и не жертвует удобством каталога ради промо-блоков; детальные правила — в `14_VISUAL_IDENTITY_AND_ECOSYSTEM.md`.

## 8. Безопасность и эксплуатация

- `logs/`, raw HTML, cookie jars, резервные копии БД и `config.local.php` размещаются вне публичного document root. Если это невозможно, веб-сервер должен явно запретить к ним доступ.
- Production отключает отображение ошибок PHP; ошибки получают `request_id` и попадают в журнал.
- Используются prepared statements, escaping всех внешних строк, CSP/защитные HTTP headers, HTTPS, CSRF для admin write-actions и session cookie с `Secure`, `HttpOnly`, `SameSite`.
- Cron-задачи имеют lock, чтобы не выполняться параллельно; есть резервное копирование БД и процедура проверки восстановления.
- Health check не создаёт нагрузку на каталоги: использует минимальный разрешённый запрос или статистику пользовательских обращений.
- Сырые VIN в telemetry не нужны: используется хеш с серверным salt либо маскированное значение; срок хранения технических логов фиксируется отдельно.

## 9. Дополненные критерии приёмки

Помимо `11_ACCEPTANCE_CRITERIA.md`, MVP принимается, если:

1. Публично доступна матрица фактического покрытия, и интерфейс не обещает неподдерживаемые марки/возможности.
2. Каждая показанная деталь имеет `fitment_status`, `source`, `source_url` и время получения.
3. Неоднозначный VIN не открывает каталог без выбора варианта.
4. OEM без выбранного авто не маркируется как «подходит».
5. При блокировке основного источника за общим budget возвращается fallback, stale-результат или структурированная ошибка.
6. Внешние URL нельзя подменить запросом frontend, а private файлы недоступны по HTTP.
7. Тесты покрывают parser fixtures, негативный cache, concurrent cache miss, 403/429, изменённый HTML, `stale` и все статусы применимости.
8. Миграции разворачивают чистую БД, обновляют тестовую БД и имеют проверенный rollback/backup plan.

## 10. Дальнейшие продуктовые решения

Перед подключением продаж или новых марок отдельно согласовываются: перечень марок и порядок их запуска по фактическому спросу, поставщики/остатки/цены, SLA менеджера по заявкам, правила хранения контактных данных и способ измерения качества по реальным заказам.

Решения из раздела 0 внесены в основные документы ТЗ. Этот файл остаётся их обоснованием и источником правил для последующих релизов.
