docs: complete source audit, add catalog inventory and redesign plan

- ANALYSIS.md: append the 2026-10-04 findings (94 SKUs, hidden categories, broken link, PDP gaps, shipping contradiction, guest checkout)
- CATALOG.md: full de-duplicated inventory (11 categories, 94 products with prices/badges), 3 extra PDP samples, checkout/payment field map, store constants
- PLAN.md: locked decisions, route map, data model + ERD, key flows, seed plan, layer acceptance checks and open questions
This commit is contained in:
semianiakavy 2026-10-04 17:49:41 +00:00
parent 2db94ac43b
commit 262ae126fb
3 changed files with 822 additions and 0 deletions

179
docs/ANALYSIS.md Normal file
View file

@ -0,0 +1,179 @@
# Анализ сайта-источника la-rose.by
**Дата аудита:** 2026-10-01 · **Метод:** обход живого сайта (HTML + headless-сессия для каталога и корзины).
**Статус документа:** факты о текущем сайте как есть. Плановые решения — в `docs/PLAN.md`.
## 0. Кратко
LaRose — минская доставка цветов (с 2018), ~94 уникальных товара, 11 категорий верхнего уровня,
8 информационных страниц, одностраничное оформление заказа, самовывоз + курьер по Минску.
Сайт работает на **Webasyst Shop-Script** (тема `free1`), интерфейс полностью русский, валюта — **BYN**.
Ключевой вывод для переделки: сайт **функционально полный, но структурно бедный** —
на карточке товара нет структурированных полей (состав, размеры, количество стеблей, наличие),
нет фасетных фильтров (только диапазон цены), нет блога/FAQ, аккаунт не выведен в шапку.
Это и есть главный фронт работ по «крутому» сайту.
## 1. Карта страниц (sitemap)
```mermaid
graph TD
HOME["/ — Главная"] --> CAT["Каталог — 11 категорий"]
CAT --> C1["/category/bukety-tsvetov/ (~92)"]
CAT --> C2["/category/sbornye-bukety/ (~36)"]
CAT --> C3["/category/bukety-iz-alstromerii/ (~15)"]
CAT --> C4["/category/bukety-iz-gvozdiki/ (~16)"]
CAT --> C5["/category/bukety-iz-kustovoy-rozy/ (~20)"]
CAT --> C6["/category/bukety-iz-khrizantemy/ (~37)"]
CAT --> C7["/category/svadebnye-bukety-nevesty/ (3)"]
CAT --> C8["/category/monobuket/ (~23)"]
CAT --> C9["/category/kompozitsii/ (~7)"]
CAT --> C10["/category/tsvety-v-yashchike/ (1)"]
CAT --> C11["/category/tsvety-v-korzine/ (~6)"]
C1 --> PDP["/product/<slug|id> — ~94"]
C6 --> PDP
C8 --> PDP
CAT -. вне меню .-> H1["/category/bukety-iz-roz/ (22)"]
CAT -. вне меню .-> H2["/category/bukety-iz-gortenziy/ (0)"]
CAT -. вне меню .-> H3["/category/bukety-s-tyulpanami/ (0)"]
HOME --> TOP["Сервисное меню"]
TOP --> A1["/company/ — О компании"]
TOP --> A2["/dostavka/ — Доставка по Минску"]
TOP --> A3["/vozvrat/ — Возврат"]
TOP --> A4["/contacts/ — Контакты"]
A1 --> A1a["/company/b2b/ — Корпоративным клиентам"]
A1 --> A1b["/company/politika-konfidentsialnosti/"]
A2 --> A2a["/dostavka/oplata/ — Варианты оплаты"]
A2 --> A2b["/dostavka/zakaz/ — Как заказать"]
A4 --> A4a["/contacts/vakansii/ — Вакансии"]
HOME --> SYS["Транзакционные"]
SYS --> S1["/order/ — корзина + оформление (1 страница)"]
SYS --> S2["/cart/ — корзина (alt)"]
SYS --> S3["/login/ · /signup/ · /my/"]
SYS --> S4["/search/?query=…"]
```
## 2. Каталог
- **Дерево:** 2 уровня — 11 категорий верхнего уровня → товары. Подкатегорий **нет**, фильтры не сгруппированы.
- **Товар может лежать в нескольких категориях** (кросс-листинг), поэтому сумма по категориям > числа уникальных товаров (~94).
- **Фильтры:** только диапазон цены (`price_min` / `price_max`, наблюдаемый диапазон 43–765 BYN) + «Показать» / «Очистить». Фасетов по поводу, цвету, цветку, размеру — нет.
- **Сортировки (`?sort=`):** Название, Цена, Хиты продаж, Оценка покупателей, Дата добавления, В наличии.
- **Пагинация:** номерная, ~28 товаров на страницу + селектор «Показывать по».
- **Блоки листинга:** «Хиты продаж», бейджи `ХИТ! / NEW! / СКИДКА!`, быстрая кнопка «В корзину», SEO-текст под сеткой.
- **Скрытые категории:** `bukety-iz-roz` (22 товара, есть только в SEO-тексте), `bukety-iz-gortenziy` и `bukety-s-tyulpanami` — отдают 200 и 0 товаров.
## 3. Карточка товара — реальный набор полей
Образец: `/product/buket-iz-15-roz-fridom/`
| # | Поле (как на сайте) | Примечание |
|---|---|---|
| 1 | Название (`<h1>`) | «Букет из 15 роз» |
| 2 | Бейдж | Хит! / New! / Скидка! |
| 3 | Текущая цена | `142 BYN` |
| 4 | Старая (зачёркнутая) цена | только при скидке |
| 5 | Валюта | BYN (`og:product:price:currency`) |
| 6 | Степпер количества | `мин.` + `шт.`, `name=quantity` |
| 7 | Кнопка «Купить» | добавление в корзину |
| 8 | Галерея | 4 превью + основное изображение |
| 9 | Хлебные крошки | Главная › Букеты цветов › Монобукеты |
| 10 | «Поделиться» | ya-share2 |
| 11 | Таб «Обзор» | единственный таб |
| 12 | Описание | свободный текст (SEO-заготовка) |
| 13 | «Просмотренные товары» | блок истории просмотра |
**Чего нет как структурированных данных (главный разрыв):** размеры/варианты, состав букета,
количество стеблей, высота/габариты, уход, срок доставки, наличие/остаток, допы, отзывы и рейтинг.
Состав и количество цветов спрятаны внутри названия («Белая ромашка, примерно 51 цветочек»).
## 4. Корзина и оформление
Оформление **одностраничное** — `/order/`, секции: `Корзина → Оформление → Покупатель → Доставка → Оплата`.
1. **Корзина:** строки, степпер `мин./шт.`, цена за единицу и за строку, «Удалить» / «Удалить все товары», промокод (`coupon_code` + «Активировать»), «Итого».
2. **Покупатель:** `firstname`, `phone`, `email`, **`nomer_skidochnoy_karty`** (номер скидочной карты — лояльность), `data_i_vremya_dostavki` (дата и время доставки).
3. **Доставка:** страна/регион/город предзаполнены и заблокированы (Беларусь / Минск). Типы: «Доставка курьером» (Бесплатно · Сегодня) и «Самовывоз из магазинов» (Бесплатно · Сегодня). Карточки вариантов доставки с выбором дня/интервала. Адрес: имя получателя, телефон получателя, улица, **`tekst-na-otkryt`** (текст для открытки), комментарий к заказу. Итоги: товары / доставка / итого.
4. **Оплата:** «Наличные при получении» и «Оплата картой онлайн (РБ, РФ, Мир)» — Visa, MasterCard, МИР, Google Pay, Yandex Pay, Apple Pay, ЕРИП.
**Пробелы:** нет явной опции «анонимный подарок» (только текст открытки), логика бесплатной доставки в копирайте противоречива (50 / 80 / 100 BYN в разных местах).
## 5. Информационные страницы
| URL | О чём |
|---|---|
| `/company/` | О компании: с 2018, 5 точек самовывоза, импорт из 7 стран, бонусная система, B2B, заказ 24/7 |
| `/company/b2b/` | Корпоративным: подарки сотрудникам/партнёрам, брендирование, оформление офиса, доставка за 1 час, безнал, счёт, скидка 10% |
| `/dostavka/` | Доставка по Минску: бесплатно от 100 BYN, иначе 15 BYN; за МКАД +0.50 BYN/км; срок до 2 часов; самовывоз Притыцкого 10, 09:00–21:00 |
| `/dostavka/oplata/` | Оплата: курьеру/в магазине, карта онлайн (WebPay, ЕРИП, Альфа-Банк), VISA/MasterCard/BELCART, Apple/Samsung Pay, безнал для юрлиц |
| `/dostavka/zakaz/` | Как заказать: телефон/почта/корзина, круглосуточный приём, «Подтвердить заказ» → «Оплатить заказ» |
| `/vozvrat/` | Возврат: живые цветы возврату не подлежат; претензии только в момент передачи; возврат на исходный способ оплаты, карта до 30 дней |
| `/contacts/` | ООО «Диапазон Плюс», УНП 190438401, Притыцкого 10 пом. 1Н; 2 телефона; 09:00–21:00, праздники до 23:00 |
| `/contacts/vakansii/` | Вакансии: менеджер по работе с клиентами, флорист; 2/2, метро Пушкинская |
| `/company/politika-konfidentsialnosti/` | Политика обработки персональных данных |
FAQ, блог, новостей и отдельной страницы «доставка за МКАД» — нет.
## 6. Коммерческие факты (для констант сайта)
- **Телефоны:** +375 (29) 657-30-00, +375 (33) 657-30-00 · **E-mail:** info@La-rose.by
- **Часы:** ежедневно 09:00–21:00, праздники до 23:00; приём заказов онлайн — 24/7
- **Адрес:** г. Минск, ул. Притыцкого, 10, пом. 1Н (самовывоз, метро Пушкинская)
- **География:** Минск и Минская область; за МКАД +0.50 BYN/км
- **Срок доставки:** до 2 часов (B2B — приоритет 1 час)
- **Валюта:** BYN · **Доставка:** бесплатно от 100 BYN, иначе 15 BYN
- **Оплата:** наличные курьеру/в магазине, карта онлайн (Visa, MasterCard, БЕЛКАРТ, МИР; Google/Yandex/Apple/Samsung Pay; ЕРИП), безнал для юрлиц
- **Гарантии:** импорт 2 раза в неделю, свой курьер, претензии по качеству — при передаче
## 7. Дополнительные возможности и интеграции
- Поиск по каталогу (`/search/?query=`), аккаунт Webasyst (`/login/`, `/signup/`, `/my/`) — **не выведен в шапку/футер**.
- Избранное и сравнение: компоненты темы есть, отдельной страницы в sitemap нет.
- Лояльность: скидочная карта, промокоды, «Общая скидка», бонусная система, 10% корпоративным.
- Отзывы/рейтинг: сортировка «Оценка покупателей» есть, сами отзывы на PDP не рендерятся.
- Каналы: WhatsApp `wa.me/375296573000`, Telegram-бот `t.me/bot_a_nik_vsop`, VK `vk.com/La_Rose_by`, Instagram `instagram.com/larose_by`, онлайн-чат **Bitrix24**.
- Аналитика: Yandex Metrika (72956191, webvisor + ecommerce), GA4/GTM (G-GNXX0B3PD5, G-4SN20SG8SP, AW-837118909, GTM-W8NK6WWQ), Meta Pixel 128304465805329, Mail.ru top.
## 8. Технические факты
- Платформа: **Webasyst Shop-Script** (`/wa-data/public/shop/…`, `/webasyst/` отвечает 200, `sitemap-shop.xml` сгенерирован Webasyst).
- Тема: `free1`. `<meta name="generator">` отсутствует.
- `robots.txt`: 38 запретов, включая `/my/`, `/checkout/`, `/search/`, `/order/`, `/login/`, `/cart/`, все query-string (`/*?*`), `/webasyst/`.
- `sitemap-shop.xml` не перечисляет все товары: страницы `/product/<id>/` всплывают только из листингов.
## 9. Что это значит для переделки
| Разрыв | Решение (в `docs/PLAN.md`) |
|---|---|
| Поля товара не структурированы | Ввести контракт PDP: состав, количество стеблей, высота, размер/вариант, уход, наличие, срок доставки |
| Нет фасетных фильтров | Фасеты: повод, цвет, тип цветка, размер, цена + сортировки как сейчас |
| Аккаунт спрятан | Вывести вход/кабинет в шапку, добавить историю заказов и повтор заказа |
| Противоречивые условия доставки | Один источник констант: порог бесплатной доставки, за МКАД, срок, часы |
| Нет FAQ/блога | Добавить FAQ на страницы доставки/оплаты (низкая стоимость, высокий SEO-эффект) |
| SEO-слаги и числовые ID | Сохранить существующие слаги 1:1, для `/product/<id>/` подготовить 301-карту |
| Аналитика и чат | Решить судьбу Bitrix24, сохранить паритет по Метрике/GA4/Pixel |
## 10. Дополнение от 2026-10-04 — полный инвентарь каталога и чек-аут
Аудит дособран: полный дедуплицированный инвентарь (94 товара, 11 категорий, цены, старые цены,
бейджи, кросс-листинг), 3 дополнительных образца PDP, точные поля и способы оформления/оплаты,
константы. Полные таблицы и оговорки — в приложении [`docs/CATALOG.md`](CATALOG.md).
Новые/уточнённые факты:
- **Каталог:** 256 слотов в листингах → **94 уникальных товара**; размер страницы фиксирован (28),
селектор «Показывать по» не активен; бейджи ХИТ! × 24, NEW! × 13, СКИДКА! × 5.
- **Скрытые категории:** `/category/bukety-iz-roz/` (22 товара, только прямой URL и крошки);
«Гортензии» и «Тюльпаны» — HTTP 200, но 0 товаров. Дополнительно видны `/category/rozy-po-tsvetu/`
и `/category/krasnye-rozy/` (крошки PDP).
- **Битая ссылка:** карточка `/product/1997/` в листинге ведёт на **HTTP 404**.
- **PDP:** структурированных полей нет ни у одного образца; у части товаров есть варианты
(«15/19/25 роз» → 198/240/325 BYN) с одной характеристикой «Кому дарим цветы».
- **Бесплатная доставка:** копирайт противоречив (50 / 80 / 100 BYN), а фактически в корзине
настроен **один бесплатный вариант** доставки по Минску без порога; тариф 15 BYN и «+0.50 BYN/км»
в корзине недостижимы (город заблокирован как Минск).
- **Оформление:** гостевой чекаут возможен; промокод `coupon_code` с ошибкой «Купон недействителен»;
поля получателя включают `tekst-na-otkryt` (текст открытки) и `nomer_skidochnoy_karty`.
- **Оплата:** «Наличные» и «Оплата картой онлайн» (плагин `begateway`; Visa/MasterCard/МИР,
Google/Yandex/Apple Pay, ЕРИП).
- **Не проверено:** отправка заказа и редирект оплаты, валидный промокод, тариф за МКАД.

265
docs/CATALOG.md Normal file
View file

@ -0,0 +1,265 @@
# Каталог la-rose.by — полный инвентарь (приложение к аудиту)
**Источник:** https://la-rose.by/ · **Дата сбора:** 2026-10-04 · **Метод:** обход живого сайта
(HTML + headless-браузер, карточки `div.Product-grid`, пагинация `?page=`).
**Статус документа:** факты «как есть». Сводный анализ — [`docs/ANALYSIS.md`](ANALYSIS.md),
плановые решения — [`docs/PLAN.md`](PLAN.md).
> Примечание: у части карточек название в листинге может быть сокращено или отличаться от `<h1>`
> на странице товара — сверить при наполнении. Данные волатильны (цены/акции на дату сбора).
## 1. Категории — коды и объёмы
| Код | Категория | URL | Страниц | Товаров в листинге | Уникальных |
|---|---|---|---|---|---|
| BC | Букеты цветов | `/category/bukety-tsvetov/` | 4 (28+28+28+8) | 92 | 92 |
| SB | Букеты сборные | `/category/sbornye-bukety/` | 2 (28+8) | 36 | 36 |
| AL | Букеты с альстромерией | `/category/bukety-iz-alstromerii/` | 1 | 15 | 15 |
| GV | Букеты с гвоздикой | `/category/bukety-iz-gvozdiki/` | 1 | 16 | 16 |
| KR | Букеты с кустовой розой | `/category/bukety-iz-kustovoy-rozy/` | 1 | 20 | 20 |
| HZ | Букеты с хризантемами | `/category/bukety-iz-khrizantemy/` | 2 (28+9) | 37 | 37 |
| SV | Свадебные букеты невесты | `/category/svadebnye-bukety-nevesty/` | 1 | 3 | 3 |
| MB | Монобукеты | `/category/monobuket/` | 1 | 23 | 23 |
| KZ | Цветочные композиции | `/category/kompozitsii/` | 1 | 7 | 7 |
| YS | Цветы в ящике | `/category/tsvety-v-yashchike/` | 1 | 1 | 1 |
| YK | Цветы в корзине | `/category/tsvety-v-korzine/` | 1 | 6 | 6 |
| | **Итого слотов / уникальных** | | | **256** | **94** |
- Размер страницы фиксирован — 28; селектор «Показывать по» есть в разметке
(`select.cat-onpage_select`), но **не активен**.
- **Кросс-листинг массовый** (256 слотов → 94 товара): товар попадает до 6 категорий
(например, «Грезы»).
- Бейджи: `ХИТ!` × 24, `NEW!` × 13, `СКИДКА!` × 5.
## 2. Скрытые и пустые категории
| Категория | HTTP | Товаров | Доступность |
|---|---|---|---|
| `/category/bukety-iz-roz/` (Розы) | 200 | 22 (все — дубли видимых категорий) | по прямому URL и из хлебных крошек розовых PDP; **нет в меню** |
| `/category/bukety-iz-gortenziy/` (Гортензии) | 200 | 0 | только по SEO-ссылке в тексте; пустая выдача |
| `/category/bukety-s-tyulpanami/` (Тюльпаны) | 200 | 0 | только по SEO-ссылке в тексте; пустая выдача |
Дополнительная скрытая таксономия (видна в хлебных крошках PDP, нет в меню):
`/category/rozy-po-tsvetu/` (Розы по цвету), `/category/krasnye-rozy/` (Красные розы).
## 3. Полная таблица товаров (94, дедуплицировано)
| # | Название | Slug / URL | Цена BYN | Старая BYN | Бейдж | Категории |
|---|---|---|---|---|---|---|
| 1 | «Летний» | `/product/buket-nostalgiya/` | 71 | 161 | ХИТ! | BC, HZ, MB |
| 2 | Корзина с кустовой розой «Золушка» | `/product/korzina-tsvetov-zolushka/` | 257 | 351 | ХИТ! | BC, KR, YK |
| 3 | Корзина с розой и гвоздикой «Диадема» | `/product/korzina-tsvetov-diadema/` | 174 | 197 | NEW! | BC, SB, KZ, YK |
| 4 | Композиция «Совершенство» | `/product/sovershenstvo/` | 125 | 140 | — | BC, SB, HZ, KZ |
| 5 | Корзина цветов «Розовая пантера» | `/product/275/` | 135 | 150 | — | BC, KR, YK |
| 6 | Сухоцветы гипсофила и лаванда | `/product/buket-iz-gipsofily-i-lavandy/` | 105 | 127 | — | BC, SV |
| 7 | Букет из пёстрой ромашки | `/product/buket-iz-pyostroy-romashki/` | 85 | 136 | — | BC, HZ |
| 8 | Корзина «Нежность» | `/product/korzina-nezhnost/` | 320 | 370 | — | BC, GV, HZ, YK |
| 9 | Свадебный букет из красной розы на роспись | `/product/svadebnyy-buket-na-rospis/` | 215 | — | — | BC, SV |
| 10 | Свадебный букет на роспись «Люблю» | `/product/svadebnyy-buket-na-rospis-lyublyu/` | 210 | — | — | BC, SV |
| 11 | Букет из 15 гвоздик и эвкалипта | `/product/buket-iz-15-gvozdik-i-evkalipta/` | 135 | 150 | ХИТ! | BC, GV, MB |
| 12 | Композиция из чёрных роз (Эквадор) | `/product/buket-iz-chernykh-roz-ekvador/` | 765 | — | NEW! | BC |
| 13 | Букет из синих роз | `/product/nezabyvaemyy-buket-iz-sinikh-roz/` | 150 | 165 | NEW! | BC, MB |
| 14 | Нежный букет с розой и альстромерией | `/product/nezhnyy-buket-iz-rozy-s-alstromeriey/` | 183 | 197 | — | BC, SB, AL |
| 15 | Букет из красных роз с эвкалиптом | `/product/buket-iz-krasnykh-roz-s-evkaliptom/` | 198 | 295 | ХИТ! | BC, MB |
| 16 | Букет красных роз Кения | `/product/1553/` | 165 | 187 | — | BC |
| 17 | Букет из французской розы | `/product/1565/` | 150 | 250 | СКИДКА! | BC, MB |
| 18 | Безмятежное счастье | `/product/buket-bezmyatezhnoe-schaste/` | 125 | 130 | ХИТ! | BC, SB, AL, GV |
| 19 | Нежный букет с хризантемой и диантусом | `/product/nezhnyy-buket-s-khrizantemoy-i-diantusom/` | 162 | 177 | ХИТ! | BC, SB, AL, GV, HZ |
| 20 | Букет 15 белых роз | `/product/buket-15-belykh-roz-1/` | 196 | — | ХИТ! | BC, MB |
| 21 | «Элегантность в простоте» | `/product/buket-elegantnost-v-prostote/` | 104 | 189 | ХИТ! | BC, SB, KR, HZ |
| 22 | Букет «Нежность» | `/product/buket-nezhnost-v-lepestkakh/` | 157 | 164 | NEW! | BC, SB, GV, KR, HZ |
| 23 | Букет «Сахарная вата» | `/product/buket-sakharnaya-vata/` | 73 | 99 | — | BC, GV |
| 24 | Сборный букет «Вспышка» | `/product/sbornyy-buket-vspyshka/` | 183 | 212 | — | BC, SB, GV, KR |
| 25 | Букет «Небесная симфония» | `/product/1814/` | 117 | 138 | — | BC, HZ, MB |
| 26 | Букет «Зефирная нежность» | `/product/buket-zefirnaya-nezhnost/` | 98 | 156 | — | BC, KR, MB |
| 27 | Композиция в корзине | `/product/kompozitsiya-v-korzine/` | 154 | 186 | — | BC, KR, MB, KZ, YK |
| 28 | Разрешите подарить вам радугу | `/product/razreshite-podarit-vam-radugu/` | 154 | 237 | ХИТ! | BC, HZ, MB |
| 29 | Романтика в воздухе | `/product/romantika-v-vozdukhe/` | 190 | 203 | — | BC, SB, KR, HZ |
| 30 | Весеннее настроение | `/product/vesennee-nastroenie/` | 148 | 187 | — | BC, SB, GV, KR, HZ |
| 31 | Роза кустовая Рефлекс | `/product/roza-kustovaya-pionovidnaya/` | 292 | 315 | — | BC, MB |
| 32 | Летний ветер | `/product/letniy-veter/` | 65 | 115 | — | BC, HZ, MB |
| 33 | Букет из 15 роз | `/product/buket-iz-15-roz-fridom/` | 142 | 175 | — | BC, MB |
| 34 | Обнимая ароматом | `/product/obnimaya-aromatom/` | 163 | 178 | — | BC, SB |
| 35 | Грезы | `/product/grezy/` | 141 | 168 | — | BC, SB, AL, GV, KR, HZ |
| 36 | Для Самой Любимой | `/product/dlya-samoy-lyubimoy/` | 207 | 298 | СКИДКА! | BC, SB, KR, HZ |
| 37 | Хризантема с ромашкой | `/product/khrizantema-s-romashkoy/` | 115 | 145 | NEW! | BC, HZ |
| 38 | Для тебя | `/product/dlya-tebya/` | 125 | 137 | — | BC, MB |
| 39 | Букет из нежной хризантемы и гипсофилы | `/product/buket-iz-nezhnoy-khrizantemy-i-gipsofily/` | 117 | 230 | ХИТ! | BC, SB, HZ, MB |
| 40 | Очарование | `/product/ocharovanie-1/` | 187 | 193 | — | BC, SB, AL, GV |
| 41 | Композиция в рамке | `/product/kompozitsiya-v-ramke/` | 91 | 104 | — | BC, KZ |
| 42 | Солнечный ангел | `/product/solnechnyy-angel/` | 153 | 193 | — | BC, SB, AL, GV |
| 43 | Цветы в шляпной коробке | `/product/tsvety-v-shlyapnoy-korobke-2/` | 117 | 125 | — | KZ |
| 44 | Тёплые объятия | `/product/teplye/` | 372 | 374 | — | BC, SB, AL |
| 45 | Букет «Для Самой Любимой» из хризантем | `/product/mamina-nezhnot/` | 143 | 210 | ХИТ! | BC, SB, HZ |
| 46 | Розовая хризантема Ромашка | `/product/rozovaya-khrizantema-romashka/` | 65 | 115 | ХИТ! | BC, HZ, MB |
| 47 | Белая ромашка, примерно 51 цветочек | `/product/belaya-romashka-primerno-51-tsvetochek/` | 55 | 137 | — | BC, HZ, MB |
| 48 | Голубая ромашка с гипсофилой | `/product/golubaya-romashka-s-gipsofiloy/` | 98 | 167 | ХИТ! | BC, HZ, MB |
| 49 | Кустовая роза Рефлекс с альстромерией | `/product/kustovaya-roza-refleks-s-alstromeriey/` | 134 | 187 | — | BC, AL, KR |
| 50 | Нежный поцелуй | `/product/nezhnyy-potseluy-1/` | 98 | 167 | — | BC, AL |
| 51 | Вдохновение природы | `/product/vdokhnovenie-prirody/` | 169 | 245 | — | BC, HZ, MB |
| 52 | Букет из кустовой розы и кустовой хризантемы | `/product/1982/` | 153 | 197 | — | BC, SB, HZ |
| 53 | Нежно-розовый букет из дуэта хризантем | `/product/nezhno-rozovyy-buket-iz-dueta-khrizantem/` | 155 | 245 | — | BC, SB, HZ |
| 54 | Зефирное облако нежных хризантем | `/product/zefirnoe-oblako-nezhnykh-khrizantem/` | 169 | 244 | NEW! | BC, HZ |
| 55 | Нежный букет для самой любимой | `/product/nezhnyy-buket-dlya-samoy-lyubimoy/` | 179 | 218 | NEW! | BC, SB, AL, KR, HZ |
| 56 | Букет в сиренево-розовых тонах | `/product/buket-v-sirenevo-rozovykh-tonakh-iz-khrizantemy-rozy-i-diantusa/` | 115 | 167 | — | BC |
| 57 | Нежный букет из кустовой и французской розы | `/product/1993/` | 113 | 137 | NEW! | BC, KR |
| 58 | ~51 небесно-голубая ромашка | `/product/51-nebesno-golubaya-romashka/` | 59 | 115 | — | BC, HZ |
| 59 | Букет «Небесная симфония» из пушистой хризантемы | `/product/1997/` ⚠️ **404** | 82 | 115 | — | BC, HZ, MB |
| 60 | Ящик деревянный с хризантемой «Радужный» | `/product/yashchik-derevyannyy-s-khrizantemoy-raduzhnyy/` | 124 | 139 | — | BC, HZ, KZ, YS |
| 61 | Композиция в ящике «Мечтаю о тебе» | `/product/kompozitsiya-v-yashchike-mechtayu-o-tebe/` | 89 | 127 | ХИТ! | BC |
| 62 | Нежно-бирюзовая ромашка | `/product/nezhno-biryuzovaya-romashka/` | 65 | 115 | ХИТ! | BC, HZ, MB |
| 63 | Букет из диантусов | `/product/2003/` | 117 | 157 | — | BC, SB, GV |
| 64 | Букет «Приятные моменты» | `/product/buket-priyatnye-momenty-iz-kustovoy-rozy-khrizantemy-i-alstromerii/` | 135 | 149 | — | BC, SB, AL, KR, HZ |
| 65 | Букет из хризантемы и кустовой розы Рефлекс | `/product/2008/` | 107 | 193 | СКИДКА! | BC, SB, KR, HZ |
| 66 | Букет из кустовой и французской розы с альстромерией | `/product/2009/` | 117 | 183 | СКИДКА! | BC, SB, AL, KR |
| 67 | Букет из голубой хризантемы и розы «Рефлекс» | `/product/buket-iz-goluboy-khrizantemy-i-rozy-refleks/` | 98 | — | — | BC |
| 68 | Нежный букет из французской розы и эвкалипта | `/product/nezhnyy-buket-iz-frantsuzskoy-rozy-i-evkalipta/` | 63 | 98 | ХИТ! | BC, SB, MB |
| 69 | Букет «Алые паруса» из 11 красных роз | `/product/buket-alye-parusa-iz-11-krasnykh-roz-s-limoniumom-i-evkaliptom/` | 146 | 193 | ХИТ! | BC, SB, MB |
| 70 | Букет из альстромерии, роз, хризантемы и эустомы | `/product/2028/` | 167 | 189 | NEW! | BC, SB |
| 71 | 3 веточки розовой хризантемы | `/product/2033/` | 43 | 65 | ХИТ! | BC, HZ |
| 72 | Букетик сухоцвет лаванды | `/product/buketik-sukhotsvet-lavandy/` | 43 | 57 | — | BC |
| 73 | Воздушный объёмный букет с розами | `/product/vozdushnyy-obyomnyy-buket-s-frantsuzskoy-i-kustovoy-rozami/` | 179 | — | — | BC, SB, KR, HZ |
| 74 | Сборный ароматный букет с лилией и розой | `/product/sbornyy-aromatnyy-buket-s-liliey-i-rozoy/` | 194 | — | — | BC, SB, KR |
| 75 | Букет из французских и кустовых роз | `/product/2048/` | 119 | 137 | — | BC, SB |
| 76 | Букет с зеркалом «Ты — Моё Всё» | `/product/2050/` | 154 | 186 | NEW! | BC, SB, GV, KZ |
| 77 | Букет из диантусов с лизиантусами | `/product/2051/` | 183 | 275 | NEW! | BC, SB, GV |
| 78 | Букет из альстромерии и диантусов | `/product/2052/` | 117 | 143 | NEW! | BC, SB, AL, GV |
| 79 | Букет из альстромерии и диантусов с лагурусом | `/product/2053/` | 129 | 161 | NEW! | BC, SB, AL, GV |
| 80 | Букет «Для моей Принцессы» | `/product/2054/` | 157 | 198 | — | BC, SB, HZ |
| 81 | Букет из французской и кустовой розы, эустомы | `/product/buket-iz-frantsuzskoy-rozy-kustovoy-rozy-eustomy/` | 125 | 153 | — | BC |
| 82 | Букет из розовых хризантем микс | `/product/buket-iz-rozovykh-khrizantem-miks/` | 51 | 63 | — | BC |
| 83 | Букет из кустовой розы, альстромерии и гипсофилы | `/product/buket-iz-kustovoy-rozy-alstromerii-i-gipsofila/` | 126 | 158 | — | BC |
| 84 | Букет из малиновой розы | `/product/buket-iz-malinovoy-rozy/` | 107 | 137 | — | BC |
| 85 | Корзина «Мамино Сердце» | `/product/2068/` | 537 | — | — | YK |
| 86 | Сиренево-белый букет из хризантемы Алтай | `/product/2069/` | 83 | 105 | — | BC, HZ |
| 87 | Сборный микс альстромерий | `/product/2070/` | 81 | 127 | — | BC, AL |
| 88 | Букет из хризантемы Алтай и лимониума | `/product/2071/` | 98 | 168 | — | BC, HZ |
| 89 | Букет нежный из кустовой хризантемы и лимониума | `/product/2072/` | 119 | 184 | — | BC, HZ |
| 90 | Букет из красной французской розы с эвкалиптом | `/product/2073/` | 93 | 173 | — | BC |
| 91 | Букет из красных роз и эустомы | `/product/2074/` | 157 | 210 | — | BC |
| 92 | Букет из французской розы, эустомы и кустовой розы | `/product/2075/` | 123 | 169 | — | BC |
| 93 | Нежный светлый букет с розой и хризантемой | `/product/2076/` | 144 | 193 | — | BC, KR |
| 94 | Букет из французской розовой розы и эвкалипта | `/product/2077/` | 98 | 132 | — | BC |
⚠️ Строка 59 (`/product/1997/`) — **битая ссылка в листинге**: href ведёт на HTTP 404
(у темы нет слага, fallback на числовой ID не резолвится). Остальные числовые ID — HTTP 200.
## 4. Карточка товара (PDP) — реальный набор полей, 3 дополнительных образца
Базовый образец (`/product/buket-iz-15-roz-fridom/`, id 1860, «Букет из 15 роз», 142/175 BYN, без бейджа, BC+MB).
| Поле | A. Роза `/product/buket-iz-krasnykh-roz-s-evkaliptom/` | B. Хризантема `/product/buket-iz-nezhnoy-khrizantemy-i-gipsofily/` | C. Композиция/коробка `/product/tsvety-v-shlyapnoy-korobke-2/` |
|---|---|---|---|
| h1 | Букет из красных роз с эвкалиптом | Букет из нежной хризантемы и гипсофилы | Цветы в шляпной коробке |
| Бейдж | ХИТ! | ХИТ! | нет |
| Цена | 198 BYN | 117 BYN | 117 BYN |
| Старая цена | 295 BYN | 230 BYN | 125 BYN |
| Валюта | BYN | BYN | BYN |
| Степпер `input[name=quantity]` | value 1; **нет `min`/`max`/`step`** | value 1; нет min/max/step | value 1; нет min/max/step |
| Изображений в галерее | 1 | 4 | 3 |
| Хлебные крошки | Букеты цветов › Розы › Розы по цвету › Красные розы | Букеты цветов | Цветочные композиции |
| Описание | свободный SEO-блок, 614 симв., без короткого текста | короткий текст 191 симв. + «Подробное описание» + SEO 613 симв. | свободный SEO-блок, 582 симв. |
| Структурированные поля | нет | нет | нет |
| Табы | «Обзор» / «Характеристики»: **одна** характеристика «Кому дарим цветы» + варианты **15/19/25 роз → 198/240/325 BYN** | только «Кому дарим цветы»; вариантов нет | нет |
| Состав | нет | нет | нет |
| Количество стеблей | нет (только в метках вариантов «N роз») | нет | нет |
| Высота / размеры | нет | нет | нет |
| Уход | нет | нет | нет |
| Наличие / остаток | не показывается | не показывается | не показывается |
| Срок доставки | не показывается (только текст «от 80 руб») | не показывается | не показывается |
| «Просмотренные товары» | нет (первый в сессии) | да | да |
**Пробелы:** состав, количество стеблей, высота/габариты, уход, наличие/остаток и срок доставки
не выводятся как структурированные данные; состав и число цветов спрятаны в названии
(«Белая ромашка, примерно 51 цветочек»). Варианты («N роз») есть только у части товаров и меняют цену.
## 5. Корзина / оформление / оплата
Оформление одностраничное — `/order/` (секции «Корзина → Оформление → Покупатель → Доставка → Оплата»).
Добавление в корзину: `POST /cart/add/` (JSON: `product_id`, `quantity`).
**Промокод:** поле `input[name=coupon_code]` + кнопка «Активировать» (AJAX). Неверный код → ошибка
«Купон недействителен»; при успехе появляется «Отменить». Валидный код и размер скидки получить не удалось.
**Порог бесплатной доставки — противоречие подтверждено:**
| Источник | Утверждение |
|---|---|
| `/dostavka/` | бесплатно от 100 BYN, иначе 15 BYN; за МКАД 15 BYN + 0.50 BYN/км; срок до 2 ч |
| meta `/dostavka/` | бесплатная доставка при заказе от 100 BYN |
| копирайт `/category/bukety-tsvetov/` | бесплатно от 50 руб |
| meta `/category/bukety-tsvetov/` | бесплатно от 100 BYN |
| копирайт `/category/monobuket/` | бесплатно от 80 руб |
| meta `/category/monobuket/` | бесплатно от 100 BYN |
| текст PDP | бесплатно от 80 руб |
| **фактическая конфигурация (проверено)** | курьер «Бесплатно» при корзине 43 BYN и 117 BYN — минимума нет; доступен **один** вариант доставки `8.delivery` «Доставка цветов по Минску», Бесплатно; тариф 15 BYN в корзине недостижим |
**Типы доставки:**
| Тип | id | Цена | Дата | Варианты |
|---|---|---|---|---|
| Доставка курьером | `todoor` | Бесплатно | Сегодня | `8.delivery` «Доставка цветов по Минску», Бесплатно (`variants_count: 1`) |
| Самовывоз из магазинов | `pickup` | Бесплатно | Завтра | — |
Слота времени нет: дата/время — свободное текстовое поле `auth[data][data_i_vremya_dostavki]` +
вычисляемая строка «Срок доставки». Адрес — через адаптер Yandex Maps (`possible_addresses: []`).
**Поля формы (точные `name`):**
| Секция | Поле | name | Обяз. |
|---|---|---|---|
| Покупатель | Имя | `auth[data][firstname]` | да |
| Покупатель | Телефон | `auth[data][phone]` | да |
| Покупатель | Email | `auth[data][email]` | нет |
| Покупатель | Номер скидочной карты | `auth[data][nomer_skidochnoy_karty]` | нет |
| Доставка | Дата и время доставки | `auth[data][data_i_vremya_dostavki]` | нет |
| Доставка | Страна / регион / город (заблокированы) | `region[country]=blr`, `region[region]=07`, `region[city]=Минск` | — |
| Доставка | Тип | `shipping[type_id]` (`todoor`/`pickup`) | — |
| Доставка | Вариант | `shipping[variant_id]` (`8.delivery`) | — |
| Получатель | Имя получателя | `details[shipping_address][imya-poluchatel]` | нет |
| Получатель | Телефон получателя | `details[shipping_address][telefon-polucha]` | нет |
| Получатель | Адрес доставки | `details[shipping_address][street]` | да |
| Получатель | Текст на открытке | `details[shipping_address][tekst-na-otkryt]` | нет |
| Комментарий | Комментарий к заказу | `confirm[comment]` | нет |
| Hidden | Пользователь / локация | `auth[user_id]`, `region[location_id]` | — |
**Оплата (точный список):**
| value | Способ | Описание | Плагин |
|---|---|---|---|
| `1` | Наличные | «Оплата наличными при получении» | `cash` |
| `29` | Оплата картой онлайн (РБ, РФ, Мир) | Visa, MasterCard, МИР, Google Pay, Yandex Pay, Apple Pay, ЕРИП | `begateway` |
**Гостевой чекаут:** да — форма `/order/` открывается для анонимной сессии без логина (только
скрытый `auth[user_id]`; имя + телефон обязательны). Реальную отправку заказа выполнить не удалось.
## 6. Контакты и константы (проверено)
| Параметр | Значение |
|---|---|
| Телефон 1 | +375 (29) 657-30-00 (`tel:+375296573000`) |
| Телефон 2 | +375 (33) 657-30-00 (`tel:+375336573000`) |
| E-mail | info@La-rose.by (встречается info@la-rose.by) |
| WhatsApp | https://wa.me/375296573000 |
| Telegram | https://t.me/bot_a_nik_vsop |
| VK | https://vk.com/La_Rose_by |
| Instagram | https://www.instagram.com/larose_by/ |
| Часы | ежедневно 09:00–21:00; праздники/предпраздничные до 23:00; онлайн-заказы 24/7 |
| Салон (самовывоз) | г. Минск, ул. Притыцкого, 10, пом. 1Н (53.911005, 27.501101) |
| Юрлицо | ООО «Диапазон Плюс», УНП 190438401; 220073, Минск, ул. Притыцкого, 10, пом. 1Н |
| Торговый реестр | № 556600; свид. 19043840 от 18.02.2019 |
| География | Минск (Беларусь); «за МКАД» упоминается в текстах, но в корзине не настраивается |
| Аналитика | GA4 `G-GNXX0B3PD5`, `G-4SN20SG8SP`, `GTM-W8NK6WWQ`, `AW-837118909`; FB Pixel `128304465805329`; Яндекс.Метрика `72956191`; Mail.ru `3238855`; чат Bitrix24 |
## 7. Что не проверено / оговорки
- Отправка заказа и редиректы оплаты не выполнялись (нет реальных контактов/платежа) — проверена
только структура и валидация формы.
- Валидный промокод и размер скидки не получены.
- Тариф за МКАД / вне Минска не воспроизводится: город заблокирован как Минск, доступен один
бесплатный вариант доставки.
- `/product/1997/` возвращает 404, хотя это href карточки в листинге.
- Скрытые категории «Гортензии» и «Тюльпаны» отдают HTTP 200, но 0 товаров.
- Извлечение опирается на разметку `div.Product-grid`: товар, доступный только по прямому URL
и скрытый из всех листингов, в инвентарь не попал бы. Дедуплицированный итог — 94, совпадает с ожиданием.
- Цены и акции волатильны (дата сбора — 2026-10-04).

378
docs/PLAN.md Normal file
View file

@ -0,0 +1,378 @@
# La Rose — план переделки сайта (`docs/PLAN.md`)
**Проект:** `SemianiakaVY/la-rose` · **Шаблон:** `online-shop` · **Стек:** Next.js
**Источники:** [`SPEC.md`](../SPEC.md) (контракт), [`docs/ANALYSIS.md`](ANALYSIS.md) (факты об
источнике), [`docs/CATALOG.md`](CATALOG.md) (полный инвентарь каталога).
**Дата:** 2026-10-04.
## 0. Этап и рамки
Текущий этап — **исследование, структура, схемы и карточки задач**. На этом этапе:
- **Нового дизайна НЕ делаем** (решение зафиксировано в `.w4c/project.json`) — отдельная
дизайн-спецификация не выпускается; UI следует структуре источника и токенам, выбранным на
слое фронтенда.
- **Реальные платежи не подключаем** — провайдер за интерфейсом, реализация `mock`.
- Код приложения на этом этапе не пишется: результатом этапа являются этот план, mermaid-схемы
и карточки задач на доске.
## 1. Цель продукта
Интернет-магазин доставки цветов и подарков по Минску (переделка `la-rose.by`): каталог с
категориями и фасетными фильтрами, карточка товара со структурированными полями, корзина и
одностраничное оформление, личный кабинет с историей заказов и закрытая админ-панель.
Продукт должен **работать end-to-end на сид-данных** (SPEC.md §1), UI — русский.
## 2. Зафиксированные решения
| # | Решение | Значение |
|---|---|---|
| R1 | Стек | **Next.js** (App Router, TypeScript) — из диалога создания проекта |
| R2 | Деньги | Только целые минорные единицы (`priceMinor`, копейки); никаких float |
| R3 | Форматирование чисел | `Intl.NumberFormat` / `toLocaleString('en-US')` с кодом валюты (SPEC.md §4) |
| R4 | Валюта | Целевая валюта магазина — **BYN**; в настройках шаблона доступны только USD/EUR/RUB, поэтому работаем через одну константу `STORE_CURRENCY` (интерим `RUB`). Переключение на BYN — правка одной константы; курсовой пересчёт в рамки не входит, цены сидов берём из аудита как есть |
| R5 | Платежи | Провайдер за одним интерфейсом `PaymentProvider`; при `None (mock)` — мок-адаптер, flow работает без ключей (SPEC.md §6) |
| R6 | Оформление | Оформление — **одностраничное** `/checkout`, как у источника (`/order/`): корзина → покупатель → доставка → оплата. Аккаунт: по SPEC.md §2 чекаут требует аккаунта, у источника гостевой чекаут возможен — берём SPEC как контракт (чекаут после входа), гостю показываем логин с сохранением корзины |
| R7 | Доставка | **Один источник констант** (`lib/delivery/constants.ts`): порог бесплатной доставки, тариф, за МКАД, срок, часы. Противоречие источника (50/80/100 BYN, см. ANALYSIS.md §4) снимается на одном значении |
| R8 | Фильтры | Фасеты: **повод, цвет, тип цветка, размер/диапазон цены** + сортировки как у источника (название, цена, хиты, оценка, дата, наличие) |
| R9 | PDP-контракт | Обязательные структурированные поля: состав, количество стеблей, высота/габариты, вариант/размер, уход, наличие, срок доставки (сейчас отсутствуют — ANALYSIS.md §3, CATALOG.md §4) |
| R10 | URL | Существующие человекочитаемые слаги категорий и товаров сохраняем 1:1; для товаров с числовыми `/product/<id>/` готовим **301-карту** на новые слаги (CATALOG.md §3) |
| R11 | Контент | Информационные страницы источника переносим: О компании, B2B, Доставка, Оплата, Как заказать, Возврат, Контакты, Вакансии, Политика; добавляем **FAQ** (нет у источника) |
| R12 | Аналитика и чат | Паритет по Яндекс.Метрике/GA4/FB Pixel закладываем; судьба чата Bitrix24 — открытый вопрос (см. §9) |
## 3. Информационная архитектура (маршруты)
### Витрина
| Маршрут | Экран | Источник |
|---|---|---|
| `/` | Главная: hero, категории, хиты | SPEC.md §3 |
| `/catalog` | Все товары, фильтры, сортировка, пагинация | SPEC.md §3, R8 |
| `/catalog/[categorySlug]` | Товары категории (11 категорий источника + скрытые, CATALOG.md §1–2) | SPEC.md §3 |
| `/product/[slug]` | Карточка: галерея, цена/старая цена, бейдж, варианты, PDP-контракт R9, в корзину | SPEC.md §3, R9 |
| `/cart` | Строки, количество, промокод, итоги | SPEC.md §3, ANALYSIS.md §4 |
| `/checkout` | Оформление (одна страница): покупатель → доставка → оплата | SPEC.md §5.2, R6 |
| `/checkout/success/[orderId]` | Подтверждение с номером заказа | SPEC.md §3 |
| `/account/orders`, `/account/orders/[orderId]` | История и детали заказа | SPEC.md §3 |
| `/login`, `/register`, `/logout` | Аутентификация | SPEC.md §3 |
| `/search?query=` | Поиск по названию/описанию | SPEC.md §6 |
| `/pages/[slug]` | Информационные страницы (R11) | ANALYSIS.md §5 |
### Админ (за ролью `admin`)
| Маршрут | Экран |
|---|---|
| `/admin` | Дашборд: последние заказы, низкий остаток, выручка за период |
| `/admin/products` | Список/создание/правка/архив, изображения, цена и остаток |
| `/admin/categories` | Список/создание/правка/порядок |
| `/admin/orders`, `/admin/orders/[orderId]` | Список со статусами, детали и смена статуса |
### Карта страниц
```mermaid
graph TD
HOME["/ — Главная"] --> CAT["/catalog — все товары (фасеты, сортировки)"]
CAT --> C1["/catalog/bukety-tsvetov (92)"]
CAT --> C2["/catalog/sbornye-bukety (36)"]
CAT --> C3["/catalog/bukety-iz-alstromerii (15)"]
CAT --> C4["/catalog/bukety-iz-gvozdiki (16)"]
CAT --> C5["/catalog/bukety-iz-kustovoy-rozy (20)"]
CAT --> C6["/catalog/bukety-iz-khrizantemy (37)"]
CAT --> C7["/catalog/svadebnye-bukety-nevesty (3)"]
CAT --> C8["/catalog/monobuket (23)"]
CAT --> C9["/catalog/kompozitsii (7)"]
CAT --> C10["/catalog/tsvety-v-yashchike (1)"]
CAT --> C11["/catalog/tsvety-v-korzine (6)"]
C1 --> PDP["/product/[slug] — 94 товара"]
PDP --> CART["/cart — корзина + промокод"]
CART --> CHECKOUT["/checkout — одна страница"]
CHECKOUT --> OK["/checkout/success/[orderId]"]
HOME --> AUTH["/login · /register"]
AUTH --> ACC["/account/orders"]
ACC --> ORD["/account/orders/[orderId]"]
HOME --> SRC["/search?query="]
HOME --> PAGES["/pages/[slug] — о компании, доставка, оплата, как заказать, возврат, контакты, b2b, вакансии, политика, FAQ"]
HOME --> ADMIN["/admin — дашборд (роль admin)"]
ADMIN --> AP["/admin/products"]
ADMIN --> AC["/admin/categories"]
ADMIN --> AO["/admin/orders"]
```
## 4. Модель данных
Расширяем модель SPEC.md §4 полями, которые требует источник (кросс-листинг, бейджи, старая цена,
варианты, промокоды, контент). Ключевые дополнения выделены.
- **Category** — id, slug, name, description, position, parentId?, `visible` (скрытые категории
источника: розы/гортензии/тюльпаны — CATALOG.md §2).
- **Product** — id, slug, name, description, `priceMinor` (**R2**), `oldPriceMinor?` (старая цена),
`currency`, `badge` (`hit`|`new`|`sale`|null), `stock`, status (`draft`|`active`|`archived`),
createdAt, **`composition`**, **`stems?`**, **`heightCm?`**, **`care`**, **`deliveryTime`** (**R9/R11**).
- **ProductCategory** — productId, categoryId (кросс-листинг: 256 слотов → 94 товара).
- **ProductImage** — productId, url, position.
- **ProductVariant** — productId, label («15 роз»), priceMinor, stock (у источника: 15/19/25 роз → 198/240/325 BYN).
- **ProductFeature** — productId, key («Кому дарим цветы»), value[] (характеристики/фасеты, R8).
- **Cart** / **CartItem** — productId, quantity, цена всегда из продукта.
- **Customer** / **Address** — email, passwordHash, name, addresses[].
- **Order** — number, customerId, status (`pending`|`paid`|`fulfilled`|`cancelled`),
subtotalMinor, shippingMinor, `discountMinor`, totalMinor, currency, shippingAddress (получатель,
телефон, `giftNote` = текст открытки, deliveryDate/Time), promoCodeId?, createdAt.
- **OrderItem** — orderId, productId, name, unitPriceMinor, quantity (**иммутабельный снимок**).
- **Payment** — orderId, provider, providerRef, status, amountMinor.
- **PromoCode** — code, type, value, activeFrom/To (`coupon_code` источника).
- **ContentPage** — slug, title, body (R11; FAQ — блоком или страницей).
- **DeliveryOption** — id, name (`todoor`/`pickup`), priceMinor, freeOverMinor (`R7`).
```mermaid
erDiagram
CATEGORY {
int id PK
string slug
string name
int parentId FK
bool visible
}
PRODUCT {
int id PK
string slug
string name
int priceMinor
int oldPriceMinor
string currency
string badge
int stock
string status
string composition
int stems
int heightCm
}
PRODUCT_CATEGORY {
int productId FK
int categoryId FK
}
PRODUCT_IMAGE {
int id PK
int productId FK
string url
int position
}
PRODUCT_VARIANT {
int id PK
int productId FK
string label
int priceMinor
int stock
}
PRODUCT_FEATURE {
int id PK
int productId FK
string key
string value
}
CART {
int id PK
int customerId FK
}
CART_ITEM {
int id PK
int cartId FK
int productId FK
int quantity
}
CUSTOMER {
int id PK
string email
string passwordHash
string name
}
ADDRESS {
int id PK
int customerId FK
string street
}
ORDER {
int id PK
string number
int customerId FK
string status
int subtotalMinor
int shippingMinor
int discountMinor
int totalMinor
string currency
string giftNote
int promoCodeId FK
datetime createdAt
}
ORDER_ITEM {
int id PK
int orderId FK
int productId FK
string name
int unitPriceMinor
int quantity
}
PAYMENT {
int id PK
int orderId FK
string provider
string providerRef
string status
int amountMinor
}
PROMO_CODE {
int id PK
string code
string type
int value
}
CONTENT_PAGE {
int id PK
string slug
string title
string body
}
DELIVERY_OPTION {
string id PK
string name
int priceMinor
int freeOverMinor
}
CATEGORY ||--o{ CATEGORY : "parent of"
CATEGORY ||--o{ PRODUCT_CATEGORY : lists
PRODUCT ||--o{ PRODUCT_CATEGORY : "listed in"
PRODUCT ||--o{ PRODUCT_IMAGE : has
PRODUCT ||--o{ PRODUCT_VARIANT : has
PRODUCT ||--o{ PRODUCT_FEATURE : has
PRODUCT ||--o{ CART_ITEM : "added as"
PRODUCT ||--o{ ORDER_ITEM : "snapshot in"
CUSTOMER ||--o| CART : owns
CART ||--o{ CART_ITEM : contains
CUSTOMER ||--o{ ADDRESS : has
CUSTOMER ||--o{ ORDER : places
PROMO_CODE ||--o{ ORDER : discounts
ORDER ||--|{ ORDER_ITEM : contains
ORDER ||--o{ PAYMENT : "paid by"
```
## 5. Ключевые потоки
### 5.1 Витрина: каталог → корзина
```mermaid
flowchart LR
A["Каталог / фасеты"] --> B["Карточка товара"]
B --> C{"В наличии?"}
C -- нет --> D["Кнопка disabled + причина"]
C -- да --> E["POST /api/cart — добавить"]
E --> F["Бейдж корзины обновлён"]
F --> B
```
### 5.2 Чекаут и оплата
```mermaid
sequenceDiagram
actor C as Покупатель (вошёл)
participant UI as /checkout
participant API as API
participant PAY as PaymentProvider (mock)
participant DB as БД
C->>UI: Заполняет получателя, доставку, промокод
UI->>API: POST /api/checkout (idempotency-key)
API->>DB: Ревалидация цен и остатков серверно
alt Товар распродан
API-->>UI: 409 + какой товар недоступен
else Всё доступно
API->>DB: Создать Order (status=pending) + OrderItem (снимок)
API->>PAY: charge(amountMinor)
alt Оплата успешна
PAY-->>API: succeeded, providerRef
API->>DB: Order=paid, Payment=succeeded
API-->>UI: 201 {orderNumber}
UI-->>C: /checkout/success/[orderId]
else Оплата не прошла
PAY-->>API: failed
API->>DB: Order остаётся pending
API-->>UI: 402 + путь повтора
end
end
```
### 5.3 Админ: жизненный цикл товара и заказа
```mermaid
stateDiagram-v2
[*] --> draft: создать товар
draft --> active: опубликовать
active --> archived: архивировать
archived --> active: вернуть
note right of archived
Архивный товар исчезает из витрины,
но остаётся в прошлых заказах.
end note
```
```mermaid
stateDiagram-v2
[*] --> pending: заказ создан
pending --> paid: оплата подтверждена
pending --> cancelled: отклонён
paid --> fulfilled: выдан/доставлен
paid --> cancelled: возврат
```
## 6. Данные для наполнения (сиды из аудита)
- **Категории:** 11 видимых (+ 3 скрытых: розы/гортензии/тюльпаны, CATALOG.md §2).
- **Товары:** 94 уникальных с ценами, старыми ценами и бейджами (CATALOG.md §3); кросс-листинг
через `ProductCategory`.
- **Варианты:** у части товаров («N роз» → цена) — `ProductVariant`.
- **Константы магазина:** телефоны, e-mail, часы, адрес самовывоза, юрлицо, мессенджеры
(CATALOG.md §6) → `lib/store/constants.ts`.
- **PDP-контракт R9** заполняется вручную/скриптом при переносе: состав, стебли, высота, уход,
наличие, срок доставки (в источнике отсутствуют).
## 7. Слои сборки и приёмка (definition of done)
| # | Слой | Владелец | Проверка (команда / runtime) |
|---|---|---|---|
| 1 | План и рамки | Startup Creator | `docs/PLAN.md` + `docs/CATALOG.md` в репозитории; каждая карточка слоя содержит проверку |
| 2 | Диаграмма архитектуры | Diagram Expert | ERD/архитектура на `/diagrams` открываются без потерь; сущности = §4 |
| 3 | БД и формы | DataTables Expert | миграции + сид на чистом клоне; агрегатный запрос = пересчитанному вручную итогу |
| 4 | Бэкенд и API | Code Expert | `npm run lint`/`typecheck` + неавторизованный запрос к админ-маршруту → 401/403 |
| 5 | Фронтенд | Code Expert | `npm run build` + browse → cart → checkout → confirmation проходится на мок-провайдере |
| 6 | Админ-панель | Code Expert | нон-админ на `/admin/*` получает отказ серверно; смена статуса логируется |
| 7 | Control Panel | Control Panel Builder | виджеты показывают данные или пустое состояние; нет спиннера вечно; нет гориз. скролла 390×844 |
| 8 | Workflows | Elsa Workflows Manager | тестовый вебхук создаёт задачу и запись письма; неоплаченная ветка пропускает письмо |
| 9 | Качество и поставка | Code Expert | `npm run ci` (install + lint + typecheck + test + build) зелёный; README quickstart на чистом клоне |
| 10 | Деплой | Server Ops | публичный URL отдаёт приложение; рестарт контейнера восстанавливает сервис |
Полные критерии приёмки — `SPEC.md` §8.
## 8. Поэтапный порядок сборки
1. Каркас Next.js: install → dev-server → пустая оболочка → коммит.
2. Схема и миграции по §4, сид из §6.
3. Каталог и карточка (витрина становится демонстрируемой).
4. Корзина + чекаут + оплата (сначала мок).
5. Аккаунт + история заказов.
6. Админ-панель.
7. Качество: тесты, CI, README, `.env.example`, доступность.
## 9. Открытые вопросы (нужно решение пользователя)
| # | Вопрос | Варианты |
|---|---|---|
| Q1 | Валюта витрины на этом этапе | интерим `RUB` с константой (рекомендую) ↔ сразу включить `BYN`, если платформа позволит |
| Q2 | Скрытые категории источника | перенести как невидимые (рекомендую) ↔ удалить ↔ сделать видимыми |
| Q3 | SEO-слаги | сохранить 1:1 (рекомендую) ↔ новая схема + 301 |
| Q4 | Чат Bitrix24 | сохранить ↔ заменить на WhatsApp/Telegram ↔ убрать |
| Q5 | FAQ/блог | FAQ-страница на этом этапе (рекомендую) ↔ отложить блог |
| Q6 | Числовой ID `/product/1997/` (404) | выяснить живой URL у владельца ↔ исключить товар |
## 10. Вне рамок
Мультивендор-маркетплейс, подписки, склад/инвентарные интеграции, налоговые движки,
мультиязычная витрина (SPEC.md §1); реальные платёжные шлюзы; новый визуальный дизайн на этом
этапе; перенос аналитики и чата — только решение и закладки (R12).