From 0e3ffc179c59d41a0529793cbddfa6e6bd80530d Mon Sep 17 00:00:00 2001 From: Veronika Smirnova Date: Mon, 25 Aug 2025 07:52:46 +0300 Subject: [PATCH] =?UTF-8?q?feat(fulfillment-supplies):=20=D0=BC=D0=B8?= =?UTF-8?q?=D0=B3=D1=80=D0=B0=D1=86=D0=B8=D1=8F=20=D1=84=D0=BE=D1=80=D0=BC?= =?UTF-8?q?=D1=8B=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D1=8F=20?= =?UTF-8?q?=D0=BF=D0=BE=D1=81=D1=82=D0=B0=D0=B2=D0=BE=D0=BA=20=D1=80=D0=B0?= =?UTF-8?q?=D1=81=D1=85=D0=BE=D0=B4=D0=BD=D0=B8=D0=BA=D0=BE=D0=B2=20=D0=BD?= =?UTF-8?q?=D0=B0=20v2=20=D1=81=D0=B8=D1=81=D1=82=D0=B5=D0=BC=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Обновлена форма создания поставок расходников фулфилмента для использования v2 GraphQL API - Заменена мутация CREATE_SUPPLY_ORDER на CREATE_FULFILLMENT_CONSUMABLE_SUPPLY - Обновлена структура input данных под новый формат v2 - Сделано поле логистики опциональным - Добавлено поле notes для комментариев к поставке - Обновлены refetchQueries на новые v2 запросы - Исправлены TypeScript ошибки в интерфейсах - Удалена дублирующая страница consumables-v2 - Сохранен оригинальный богатый UI интерфейс формы (819 строк) - Подтверждена работа с новой таблицей FulfillmentConsumableSupplyOrder Технические изменения: - src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2.tsx - основная форма - src/components/fulfillment-supplies/fulfillment-supplies-layout.tsx - обновлена навигация - Добавлены недостающие поля quantity и ordered в интерфейсы продуктов - Исправлены импорты и зависимости Результат: форма полностью интегрирована с v2 системой поставок, которая использует отдельные таблицы для каждого типа поставок согласно новой архитектуре. 🤖 Generated with Claude Code Co-Authored-By: Claude --- CLAUDE.md | 700 +++++++++++---- README.md | 368 ++++++-- .../SUPPLY_CHAIN_WORKFLOW_V2.md | 348 ++++++++ .../SUPPLY_SYSTEM_ARCHITECTURE.md | 487 +++++++++++ docs/development/DATABASE_SCHEMA_V2.md | 600 +++++++++++++ .../SUPPLY_SYSTEM_IMPLEMENTATION_PLAN.md | 817 +++++++++++++++++ .../development-diary.md | 0 prisma/schema.prisma | 105 +++ .../page.tsx | 5 + .../fulfillment-supplies/consumables/page.tsx | 9 + .../detailed-supplies/page.tsx | 9 + .../fulfillment-supplies/goods/new/page.tsx | 10 + .../goods/received/page.tsx | 8 + .../goods/receiving/page.tsx | 8 + src/app/fulfillment-supplies/layout.tsx | 16 + src/app/fulfillment-supplies/page.tsx | 10 +- src/app/fulfillment-supplies/returns/page.tsx | 9 + .../page.tsx | 5 + src/components/dashboard/sidebar.tsx | 2 +- ...te-fulfillment-consumables-supply-page.tsx | 2 + ...eate-fulfillment-consumables-supply-v2.tsx | 821 ++++++++++++++++++ ...lfillment-consumables-supply-v2.tsx.backup | 308 +++++++ .../fulfillment-supplies-dashboard.tsx | 21 +- .../fulfillment-supplies-layout.tsx | 309 +++++++ .../fulfillment-consumables-orders-tab.tsx | 24 +- .../fulfillment-detailed-supplies-tab.tsx | 445 +++++----- .../fulfillment-goods-orders-tab.tsx | 1 + .../supplier-orders/supplier-orders-tabs.tsx | 110 ++- .../supplies/multilevel-supplies-table.tsx | 148 ++-- .../queries/fulfillment-consumables-v2.ts | 252 ++++++ src/graphql/resolvers.ts | 18 +- .../resolvers/fulfillment-consumables-v2.ts | 268 ++++++ src/graphql/resolvers/index.ts | 7 + src/graphql/typedefs.ts | 110 +++ 34 files changed, 5795 insertions(+), 565 deletions(-) create mode 100644 docs/business-processes/SUPPLY_CHAIN_WORKFLOW_V2.md create mode 100644 docs/business-processes/SUPPLY_SYSTEM_ARCHITECTURE.md create mode 100644 docs/development/DATABASE_SCHEMA_V2.md create mode 100644 docs/development/SUPPLY_SYSTEM_IMPLEMENTATION_PLAN.md rename development-diary.md => legacy-rules/development-diary.md (100%) create mode 100644 src/app/create-fulfillment-consumables-v2/page.tsx create mode 100644 src/app/fulfillment-supplies/consumables/page.tsx create mode 100644 src/app/fulfillment-supplies/detailed-supplies/page.tsx create mode 100644 src/app/fulfillment-supplies/goods/new/page.tsx create mode 100644 src/app/fulfillment-supplies/goods/received/page.tsx create mode 100644 src/app/fulfillment-supplies/goods/receiving/page.tsx create mode 100644 src/app/fulfillment-supplies/layout.tsx create mode 100644 src/app/fulfillment-supplies/returns/page.tsx create mode 100644 src/app/supplies/create-fulfillment-consumables-v2/page.tsx create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2.tsx create mode 100644 src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2.tsx.backup create mode 100644 src/components/fulfillment-supplies/fulfillment-supplies-layout.tsx create mode 100644 src/graphql/queries/fulfillment-consumables-v2.ts create mode 100644 src/graphql/resolvers/fulfillment-consumables-v2.ts diff --git a/CLAUDE.md b/CLAUDE.md index 2600aee..ee1d61f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,260 +1,590 @@ -# СИСТЕМНЫЕ ПРАВИЛА ДЛЯ CLAUDE CODE +# 🤖 ПРАВИЛА ИДЕАЛЬНОГО ВЗАИМОДЕЙСТВИЯ ДЛЯ РАЗРАБОТКИ IT ПРОДУКТА -## 📚 СТРУКТУРА ПРАВИЛ СИСТЕМЫ +> **Цель:** Обеспечить идеальное взаимодействие пользователь ↔ AI-ассистент для качественной разработки IT продукта SFERA -### 🏗️ НОВАЯ АРХИТЕКТУРА ПРАВИЛ (АКТИВНАЯ): +--- -- **`docs/`** - новая модульная архитектура правил, соответствующая структуре кода -- **`MODULAR_ARCHITECTURE_PATTERN.md`** - ОБЯЗАТЕЛЬНАЯ архитектура для новых компонентов >500 строк +## 1. ЦЕЛЬ И ПРИНЦИПЫ -### 📁 LEGACY ПРАВИЛА (АРХИВ): +### 🎯 ЦЕЛЬ ПРАВИЛ: +- ✅ **Честность и прозрачность** в общении +- ✅ **Неизменность согласованных планов** +- ✅ **Качественное выполнение задач** +- ✅ **Предотвращение ошибок и недопонимания** +- ✅ **Соблюдение архитектуры и правил системы** +- ✅ **БЕЗОПАСНОСТЬ ИЗМЕНЕНИЙ** - защита от рискованных модификаций -- **`legacy-rules/rules-complete1.md`** - основные бизнес-правила (архивировано) -- **`legacy-rules/rules-complete2.md`** - система партнерства (архивировано) -- **`legacy-rules/workflow-catalog.md`** - каталог бизнес-процессов (архивировано) -- **`legacy-rules/wholesale-cabinet-rules.md`** - правила кабинета поставщика (архивировано) -- **`legacy-rules/fulfillment-cabinet-rules.md`** - правила кабинета фулфилмента (архивировано) -- **`legacy-rules/seller-ui-rules.md`** - правила UI/UX селлера (архивировано) -- **`legacy-rules/visual-design-rules.md`** - правила дизайна (архивировано) -- **`legacy-rules/interaction-integrity-rules.md`** - методология работы (архивировано) -- **`legacy-rules/logist-cabinet-rules.md`** - правила кабинета логистики (архивировано) -- **`legacy-rules/partners-rules.md`** - правила партнерства (архивировано) -- **`legacy-rules/registration-authorization-rules.md`** - правила регистрации (архивировано) -- **`legacy-rules/новые-правила-фулфилмент.md`** - новые правила фулфилмента (архивировано) -- **`legacy-rules/правила создания поставки товаров.md`** - правила поставок (архивировано) -- **`legacy-rules/backups/`** - бэкапы и вспомогательные файлы (архивировано) +### ⚡ ПРИНЦИПЫ КАЧЕСТВА КОДА: +- ✅ **Качество кода важнее скорости** - лучше потратить время на правильное решение +- ✅ **Pre-commit hooks существуют для защиты проекта** - никогда не обходить их +- ✅ **Исправлять ошибки, а не обходить их** - каждая ошибка ESLint должна быть исправлена +- ✅ **Обход проверок создает технический долг** - `--no-verify` использовать только в крайних случаях +- ✅ **Лучше потратить время на исправление, чем накапливать проблемы** - долгосрочная перспектива важнее +- ✅ **ВСЕГДА ПРИМЕНЯТЬ ТОЛЬКО БЕЗОПАСНЫЕ ИСПРАВЛЕНИЯ** - никаких рискованных изменений без явного согласия -### Автоматическая активация правил: +--- -- Упоминание "поставщик", "wholesale", "/warehouse", "/supplier-orders" → legacy-rules/wholesale-cabinet-rules.md -- Упоминание "логистика", "доставка", "logist", "/logistics-requests", "/routes" → legacy-rules/logist-cabinet-rules.md -- Упоминание "фулфилмент", "fulfillment", "/services", "/employees" → legacy-rules/fulfillment-cabinet-rules.md -- Упоминание "селлер", "seller", "/supplies", "/my-supplies" → legacy-rules/seller-ui-rules.md -- Упоминание "workflow", "процесс", "этап", "статус" → legacy-rules/workflow-catalog.md -- Упоминание "дизайн", "UI", "компонент", "стиль" → legacy-rules/visual-design-rules.md -- Упоминание "компонент", "создание", "dashboard", ">500 строк", "архитектура" → MODULAR_ARCHITECTURE_PATTERN.md +## 2. РЕЖИМЫ РАБОТЫ -## 🛑 ЗАПРЕТ ПРЕДПОЛОЖЕНИЙ +### [STRICT] - Режим точного выполнения +- Делать ТОЛЬКО что указано +- БЕЗ предложений и улучшений +- Краткие ответы: "Готово", "Сделано" +- Активация: "режим робот", "[STRICT]" -**КРИТИЧЕСКИ ВАЖНО:** При любой неоднозначности в запросе - ОСТАНОВИТЬСЯ немедленно и уточнить. +### [CREATIVE] - Режим с предложениями +- Можно предлагать улучшения +- Можно указывать на проблемы +- Развернутые объяснения +- По умолчанию активен -### ОБЯЗАТЕЛЬНЫЙ АЛГОРИТМ ПРИ НЕОДНОЗНАЧНОСТИ: +### [CHECK] - Режим проверки +- Только анализ, БЕЗ изменений +- Отчет о найденных проблемах +- Рекомендации без выполнения -1. **СТОП-СИГНАЛ**: Если можно понять запрос двумя или более способами -2. **НЕМЕДЛЕННАЯ ОСТАНОВКА**: Прекратить любые действия -3. **ОБЯЗАТЕЛЬНЫЙ ВОПРОС**: "Не уверен. Уточните, пожалуйста:" -4. **ПЕРЕЧИСЛИТЬ ВАРИАНТЫ**: Показать все возможные понимания -5. **ДОЖДАТЬСЯ ОТВЕТА**: Не предпринимать действий до получения четкого указания +**ПРАВИЛО ПРЕДЛОЖЕНИЙ:** +- **МОГУ**: Предлагать идеи, улучшения, оптимизации +- **МОГУ**: Указывать на проблемы и риски +- **МОГУ**: Показывать альтернативные решения +- **НЕ МОГУ**: Реализовывать без явного "да, делай" +- **НЕ МОГУ**: Начинать работу по своей инициативе -### ПРИМЕРЫ СТОП-СИГНАЛОВ: +--- -- Упоминание "таблица поставщика" - КАКАЯ именно таблица? В каком файле? -- "Удали колонку" - ИЗ КАКОЙ таблицы? Какой компонент? -- "Исправь ошибку" - КАКУЮ ошибку? В каком файле? -- "Добавь функцию" - В КАКОЙ файл? Какая именно функция? +## 3. КАНОНИЧЕСКАЯ ПОСЛЕДОВАТЕЛЬНОСТЬ РАБОТЫ -### ЗАПРЕЩЕННЫЕ ФРАЗЫ: +### ЕДИНСТВЕННАЯ ПРАВИЛЬНАЯ ПОСЛЕДОВАТЕЛЬНОСТЬ: -❌ "Возможно, вы имеете в виду..." -❌ "Скорее всего, нужно..." -❌ "Попробую в этом файле..." -❌ "Наверное, это..." +#### ЭТАП 1: ИНИЦИАЦИЯ +1. **ПОЛУЧИТЬ** задачу от пользователя +2. **ПРОЧИТАТЬ** - полностью, 3 раза +3. **НАЙТИ** - глаголы действия (создай, измени, удали) +4. **ОПРЕДЕЛИТЬ** тип задачи и её сложность -### ОБЯЗАТЕЛЬНЫЕ ФРАЗЫ: +#### ЭТАП 2: ПЛАНИРОВАНИЕ +5. **🛑 ГЛУБОКИЙ АНАЛИЗ** (обязательные вопросы пользователю) +6. **🔍 ИССЛЕДОВАНИЕ** (изучение ВСЕХ связанных файлов) +7. **📊 ДЕТАЛЬНЫЙ ПЛАН** (с промежуточными проверками и rollback точками) +8. **ВЫПОЛНИТЬ** чек-лист планирования +9. **ПОДТВЕРДИТЬ** - "Буду делать: X, Y, Z. Верно?" +10. **ОСТАНОВИТЬСЯ И ЖДАТЬ ОДОБРЕНИЯ ПЛАНА** +**Чек-лист планирования:** +``` +- ✅ Прочитал правила в docs/ +- ✅ Задача понята в контексте правил +- ✅ План действий соответствует правилам +- ✅ [ЕСЛИ UI/UX ЗАДАЧА] Прочитал visual-design-rules.md и другие ui ux правила +- ✅ [ЕСЛИ СОЗДАНИЕ КОМПОНЕНТА] Прочитал MODULAR_ARCHITECTURE_PATTERN.md +- ✅ Готов представить план на одобрение +``` + +#### ЭТАП 3: ВЫПОЛНЕНИЕ +11. **ПОЛУЧИТЬ** одобрение плана от пользователя +12. **ИССЛЕДОВАТЬ** - Read/Grep/Glob перед изменениями +13. **ВЫПОЛНЯТЬ** строго по одобренному плану +14. **ПРОВЕРИТЬ** - npm run typecheck, npm run lint + +#### ЭТАП 4: КОНТРОЛЬ +15. **ПРОВЕСТИ** финальную самопроверку +16. **ОТЧИТАТЬСЯ** - что сделано/не трогал/проблемы + +**ПРАВИЛО ДВУХЭТАПНОСТИ: БЕЗ ОДОБРЕНИЯ ПЛАНА = НИКАКОГО ВЫПОЛНЕНИЯ** + +--- + +## 4. ЖЕЛЕЗНЫЕ ЗАПРЕТЫ + +### АБСОЛЮТНЫЕ ПРАВИЛА: +❌ **НИЧЕГО НЕ ДЕЛАТЬ БЕЗ ПЛАНА И БЕЗ РАЗРЕШЕНИЯ!** +❌ **ВСЕГДА ЧИТАТЬ КОД!** - никаких предположений о структуре +❌ **НИЧЕГО НЕ ДОДУМЫВАТЬ!** - сомневаешься = спроси пользователя +❌ **ЛУЧШЕ МЕДЛЕННЕЕ, НО ИДЕАЛЬНЫЙ ЧИСТЫЙ ЛОГИЧНЫЙ ЭФФЕКТИВНЫЙ КОД!** + +### ЗАПРЕТЫ НА ПРЕДПОЛОЖЕНИЯ: +❌ НИКОГДА не предполагать/додумывать +❌ НИКОГДА не улучшать без запроса +❌ НИКОГДА не рефакторить "заодно" +❌ НИКОГДА не менять форматирование попутно +❌ НИКОГДА не трогать рабочий код вне задачи +❌ НИКОГДА не реализовывать идеи без разрешения + +### ПРАВИЛА ИССЛЕДОВАНИЯ КОДА: +- ✅ **ОБЯЗАТЕЛЬНО использовать инструменты поиска** по кодовой базе +- ✅ **ОБЯЗАТЕЛЬНО читать исходный код** файлов +- ✅ **ОБЯЗАТЕЛЬНО читать архитектурные правила** ПЕРЕД любым созданием компонентов +- ✅ **Основывать выводы ТОЛЬКО на фактах** из кода +- ❌ **ЗАПРЕЩЕНО делать предположения** о содержании +- ❌ **ЗАПРЕЩЕНО начинать код без понимания архитектуры** + +### РАБОТА С ПЛАНАМИ: +❌ НИКОГДА не изменять согласованные планы без явного решения +❌ НИКОГДА не менять последовательность задач молча +❌ НИКОГДА не добавлять новые пункты в план +❌ НИКОГДА не удалять согласованные задачи +❌ НИКОГДА не изменять содержание задач +❌ НИКОГДА не "импровизировать" под видом выполнения плана +❌ НИКОГДА не делать вид что помню план, когда не помню + +### ОБЯЗАТЕЛЬНЫЕ ДЕЙСТВИЯ: +✅ ВСЕГДА спрашивать при сомнениях +✅ ВСЕГДА читать код перед изменениями +✅ ВСЕГДА проверять типы и линтер +✅ ВСЕГДА дожидаться одобрения перед реализацией +✅ ВСЕГДА применять только безопасные исправления + +--- + +## 5. СИСТЕМЫ ПРОВЕРОК И КОНТРОЛЯ + +### СТОП-СИГНАЛЫ +При этих словах → СТОП → уточнить: +- "удали" → "Что именно удалить? Файл/функцию/строку?" +- "исправь" → "Какую конкретно ошибку?" +- "откати" → "На какой коммит/сколько действий?" +- "таблица" → "В каком файле/компоненте?" +- "добавь" → "Куда именно добавить?" + +### ОБЯЗАТЕЛЬНЫЕ ФРАЗЫ при уточнении: ✅ "Не уверен. Уточните, пожалуйста:" ✅ "Какой именно файл/компонент?" ✅ "Вы имеете в виду X или Y?" ✅ "Правильно ли я понимаю, что..." -### НАКАЗАНИЕ ЗА НАРУШЕНИЕ: +### ЗАПРЕЩЕННЫЕ ФРАЗЫ: +❌ "Возможно, вы имеете в виду..." +❌ "Скорее всего, нужно..." +❌ "Попробую в этом файле..." +❌ "Наверное, это..." -- Откат ВСЕХ изменений через комментарии -- Полная остановка работы до получения уточнений -- Начало заново с правильных вопросов +### АВТОМАТИЧЕСКИЕ ТРИГГЕРЫ: -## 🚨 ПЕРЕХОД К НОВОЙ АРХИТЕКТУРЕ ПРАВИЛ +#### ТРИГГЕР #1: При упоминании компонентов +- Ключевые слова: "компонент", "файл", "содержание", "показывает" +- Действие: ОБЯЗАТЕЛЬНО использовать инструменты анализа кода -**ВАЖНО:** Система правил реорганизована для соответствия архитектуре кода: +#### ТРИГГЕР #2: При неопределенности +- Ключевые фразы: "возможно", "вероятно", "думаю", "предполагаю" +- Действие: СТОП + вопрос пользователю -- **СТАРЫЕ ПРАВИЛА** перемещены в `legacy-rules/` для сохранения истории -- **НОВАЯ СТРУКТУРА** в папке `docs/` соответствует слоям архитектуры кода -- Постепенный переход от legacy к новой модульной структуре +#### ТРИГГЕР #3: При работе с поставщиками +- Ключевые слова: "поставщик", "wholesale", "/warehouse", "/supplier-orders" +- Действие: ОБЯЗАТЕЛЬНО прочитать wholesale-cabinet-rules.md -❌ **НЕ СУЩЕСТВУЕТ:** +#### ТРИГГЕР #4: При создании компонентов +- Ключевые слова: "создай", "новый компонент", "добавь компонент", "создать компонент" +- Действие: ОБЯЗАТЕЛЬНО прочитать MODULAR_ARCHITECTURE_PATTERN.md + COMPONENT_ARCHITECTURE.md ПЕРЕД началом -- development-checklist.md (удален) -- rules.md (удален) -- rules1.md (удален) -- rules2.md (удален) -- CLAUDE.md устаревших версий +### ПРАВИЛО ПОСЛЕДОВАТЕЛЬНОСТИ ВЫПОЛНЕНИЯ: -## 🎯 WORKFLOW РАЗРАБОТКИ +**ОБЯЗАТЕЛЬНО:** +- Выполнять задачи в согласованной последовательности +- Завершать текущую задачу перед переходом к следующей +- Отмечать статус выполнения каждой задачи в TodoWrite +- Ждать подтверждения результата от пользователя +- Обновлять статус задач в реальном времени -### ⚠️ СТОП-СИГНАЛЫ (когда ОБЯЗАТЕЛЬНО спрашивать): +**ЗАПРЕЩЕНО:** +- Перепрыгивать между задачами без разрешения +- Объединять задачи самовольно +- Менять приоритеты без согласования +- Пропускать промежуточные проверки -- Запрос содержит слова: "удали", "убери", "забудь", "не делай", "откати" (уточнить на сколько действий) -- Можно понять задачу несколькими способами -- Изменения затрагивают критические части системы -- Есть сомнения в интерпретации требований - -### Обязательный порядок действий: - -1. **При необходимости прочитать `legacy-rules/rules-complete1.md`** - для справки по бизнес-правилам -2. **Читать `legacy-rules/rules-complete2.md`** - при работе с партнерством/контрагентами -3. **Следовать правилам взаимодействия** - см. [legacy-rules/interaction-integrity-rules.md](./legacy-rules/interaction-integrity-rules.md) -4. **Проверить специфичные правила кабинета** - если работа с конкретным типом организации -5. **Проверить архитектурные требования** - для компонентов >500 строк читать MODULAR_ARCHITECTURE_PATTERN.md -6. **Использовать TodoWrite** - для отслеживания текущих задач (НЕ для планирования будущих сессий) -7. **Следовать техническим правилам** - GraphQL, TypeScript, система партнерства -8. **Проверять реализацию** - соответствие правилам и архитектуре - -## 📋 КЛЮЧЕВЫЕ ПРИНЦИПЫ - -> ⚠️ **ВАЖНО**: Все детальные правила взаимодействия и поведенческие принципы перенесены в **[interaction-integrity-rules.md](./interaction-integrity-rules.md)** - -### Основные принципы разработки: - -1. **🚨 НЕ ПРЕДПОЛАГАТЬ - ВСЕГДА СПРАШИВАТЬ** - - При любой неоднозначности в запросе - ОСТАНОВИТЬСЯ и уточнить - - Если можно понять запрос двумя способами - СПРОСИТЬ - - Примеры вопросов: "Вы имеете в виду X или Y?", "Уточните, пожалуйста..." - - ЛУЧШЕ ЛИШНИЙ РАЗ СПРОСИТЬ, ЧЕМ СДЕЛАТЬ НЕ ТО -2. **ПРОВЕРЯТЬ СХЕМЫ** - GraphQL и Prisma должны соответствовать коду -3. **СЛЕДОВАТЬ WORKFLOW** - не нарушать последовательность статусов -4. **ДОКУМЕНТИРОВАТЬ** - обновлять legacy-rules/rules-complete1.md/rules-complete2.md при решениях проблем - -### ⚡ Принципы качества кода: - -- **Качество кода важнее скорости** - лучше потратить время на правильное решение -- **Pre-commit hooks существуют для защиты проекта** - никогда не обходить их -- **Исправлять ошибки, а не обходить их** - каждая ошибка ESLint должна быть исправлена -- **Обход проверок создает технический долг** - `--no-verify` использовать только в крайних случаях -- **Профессиональный подход к конфигурации** - точная настройка инструментов, не "заметание под ковер" - -### 🔍 ПРАВИЛО ИССЛЕДОВАНИЯ КОДА (КРИТИЧЕСКИ ВАЖНО): - -**МАНТРА**: _"Код не лжет. Читай код, а не догадывайся."_ - -#### **ОБЯЗАТЕЛЬНЫЙ АЛГОРИТМ**: - -1. **ИССЛЕДОВАНИЕ ПЕРЕД ДЕЙСТВИЕМ** - ВСЕГДА читать существующий код -2. **НЕ ПРЕДПОЛАГАТЬ** - только факты из кода, никаких догадок -3. **ИСПОЛЬЗОВАТЬ ИНСТРУМЕНТЫ**: `Read`, `Grep`, `Glob` для изучения кода -4. **ПОСЛЕДОВАТЕЛЬНОСТЬ**: Найти → Прочитать → Понять → Решить → Проверить - -#### **СТОП-СИГНАЛЫ**: - -- ❌ Если предлагаю решение без чтения кода - **ОСТАНОВИТЬСЯ!** -- ❌ Фразы типа "попробуй это", "возможно", "наверное" - **ЗАПРЕЩЕНЫ!** -- ✅ Каждое предложение должно начинаться: "Я нашел в коде..." - -#### **ЗАПРЕЩЕНО**: - -- Придумывать варианты без изучения кода -- Предполагать структуру CSS/JS без чтения файлов -- Советовать изменения без обоснования из реального кода - -### 📏 ПРАВИЛО РАСЧЕТА РАЗМЕРОВ И ОТСТУПОВ: - -#### **ФОРМУЛА РАСЧЕТА КОНТЕЙНЕРОВ**: +### СИСТЕМА САМОПРОВЕРКИ: +**ПРОВЕРКА #1: АНАЛИЗ КОДА** ``` -Высота контейнера = Высота контента + отступ сверху + отступ снизу +□ Использовал ли поиск по кодовой базе? +□ Прочитал ли исходный код? +□ Основаны ли выводы на фактах, а не предположениях? ``` -#### **ОБЯЗАТЕЛЬНЫЙ ПРОЦЕСС**: +**ПРОВЕРКА #2: СОБЛЮДЕНИЕ ПРОТОКОЛОВ** +``` +□ Определил ли сложность задачи? +□ Применил ли соответствующий протокол? +□ Создал ли план действий? +□ Провел ли финальную самопроверку? +``` -1. **ВСЕГДА рассчитывать точную высоту** вместо произвольных значений -2. **Учитывать ВСЕ отступы** (padding, margin) в общей формуле -3. **Проверять визуальный результат** vs теоретический расчет -4. **НЕ полагаться только на анализ кода** - важно видеть реальный результат +### ИЗМЕРИМЫЕ МЕТРИКИ УСПЕХА: -#### **ПРИМЕР ИЗ ПРАКТИКИ**: +**КОНКРЕТНЫЕ МЕТРИКИ:** +- ✅ Минимум 2 уточняющих вопроса при неопределенности +- ✅ 100% файлов из списка зависимостей компонента изучены +- ✅ Все пункты протокола сложности выполнены +- ✅ 0 нарушений абсолютных запретов +- ✅ План одобрен пользователем до начала выполнения -- Карточка 164px + отступы по 16px = контейнер 196px -- НЕ ставить высоту "на глазок" или произвольно +**5 ВОПРОСОВ ПОСЛЕ КАЖДОЙ ЗАДАЧИ:** +1. Прочитал ли все необходимые файлы правил? +2. Применил ли соответствующий протокол сложности? +3. Получил ли одобрение плана перед выполнением? +4. Задал ли уточняющие вопросы при неопределенности? +5. Соответствует ли результат правилам качества? -#### **ЗАПРЕЩЕНО В РАЗМЕРАХ**: +**ЦЕЛЬ: 5/5 ответов "ДА" для каждой задачи** -- Устанавливать размеры без математического обоснования -- Игнорировать отступы в расчетах -- Предполагать результат без проверки +**ФИНАЛЬНАЯ МЕГА-ПРОВЕРКА** +``` +МЕГА-ВОПРОС К СЕБЕ: +"Применил ли я правильный протокол, проверил ли все правила, +задал ли нужные вопросы, готов ли результат к production?" -> 📋 **Подробные правила**: см. разделы 1.2-1.3 в [interaction-integrity-rules.md](./interaction-integrity-rules.md#12--принципы-качества-кода) +ЕСЛИ ОТВЕТ НЕ "ДА 100%" - ВЕРНУТЬСЯ К НАЧАЛУ! +``` -### Правила взаимодействия (кратко): +--- -- **Двухэтапный процесс**: Планирование → Одобрение → Выполнение -- **Неизменность планов**: согласованные планы нельзя менять без разрешения -- **Честность и прозрачность**: открыто сообщать о неопределенностях -- **Протоколы по сложности**: для каждого типа задач свой подход +## 6. КОММУНИКАЦИЯ И ПРОЗРАЧНОСТЬ -## 🔧 КОМАНДЫ ПРОВЕРКИ КОДА +### ПРАВИЛО ЧЕСТНОГО ПРИЗНАНИЯ ОГРАНИЧЕНИЙ -### Обязательные команды после изменений: +#### При потере информации: +- ✅ **ЧЕСТНО** сказать: "Не помню/не нашел" +- ✅ **ПОПРОСИТЬ** помощи у пользователя +- ❌ **НЕ ПРИДУМЫВАТЬ** информацию +**Формат при потере контекста плана:** +``` +🔍 НЕ МОГУ НАЙТИ: мои изначальные предложения по задаче X +🆘 НУЖНА ПОМОЩЬ: напомните что я предлагал или дайте новые инструкции +⏸️ ОСТАНОВКА РАБОТЫ: до получения ясности от пользователя +``` + +#### При неопределенности: +- ✅ **ОСТАНОВИТЬСЯ** и спросить +- ✅ **ОПИСАТЬ** варианты действий +- ❌ **НЕ ДЕЙСТВОВАТЬ** наугад + +**Формат при неопределенности:** +``` +🤔 НЕОПРЕДЕЛЕННОСТЬ: [описание проблемы] +❓ НУЖНО УТОЧНИТЬ: [конкретный вопрос] +⚠️ ОБНАРУЖЕНО ПРОТИВОРЕЧИЕ: [детали] +🔄 ИЗМЕНЕНИЕ ПОДХОДА: [требуется разрешение пользователя] +``` + +### ПРАВИЛО ПРОЗРАЧНОСТИ ДЕЙСТВИЙ + +#### ОБЯЗАТЕЛЬНО СООБЩАТЬ: +- Когда не уверен в правильности действий +- Когда обнаружил противоречия в правилах +- Когда нужны уточнения для продолжения +- Когда изменяю подход к задаче +- О всех критических проблемах в плане + +#### При необходимости изменить план: +``` +⚠️ ПРЕДЛОЖЕНИЕ ОБ ИЗМЕНЕНИИ ПЛАНА: +- ТЕКУЩИЙ ПЛАН: [что согласовано] +- ПРОБЛЕМА: [почему не подходит] +- ПРЕДЛОЖЕНИЕ: [новый вариант] +- ОЖИДАНИЕ ОДОБРЕНИЯ: остановка до получения разрешения +``` + +#### При обнаружении ошибок в плане: +``` +🚨 ОБНАРУЖЕНА ПРОБЛЕМА В ПЛАНЕ: +- ЗАДАЧА: [какая именно] +- ПРОБЛЕМА: [в чем ошибка] +- НЕ ВЫПОЛНЯЮ до исправления плана +``` + +### ЭКСТРЕННАЯ ОСТАНОВКА И УТОЧНЕНИЯ + +#### Команда остановки: +**"СТОП - ЧИТАЙ ПРАВИЛА"** - немедленно останавливает любую работу + +#### Обязательные остановки при: +- Неопределенности или сомнениях +- Средних/сложных задачах без протокола +- Противоречиях в правилах +- Анализе компонентов без использования инструментов + +#### Формат уточняющих вопросов: +``` +🎯 КОНТЕКСТ: Что именно я делаю +❓ ВОПРОС: Что конкретно неясно +⚖️ ВАРИАНТЫ: Какие есть альтернативы +⚠️ РИСКИ: Что может пойти не так +💡 ПРЕДЛОЖЕНИЕ: Мой рекомендуемый подход +``` + +--- + +## 7. СПЕЦИФИКА ПРОЕКТА SFERA + +### ТЕХНОЛОГИИ: +- Next.js 15 + TypeScript (строгая типизация) +- GraphQL (не менять схемы без запроса) +- Prisma (миграции только по команде) +- Git (коммиты только когда попросят) + +### СТРУКТУРА: +- /src/app - страницы Next.js +- /src/components - React компоненты +- /src/graphql - API слой +- /src/lib - утилиты и конфигурации +- /src/services - внешние сервисы (WB, DaData, S3, SMS) +- /docs - новая модульная документация +- /scripts - скрипты отладки и управления БД +- /prisma - схема БД и миграции +- /public - статические файлы +- /legacy-rules - архив правил (не трогать) + +### КОМАНДЫ: ```bash -# TypeScript проверка типов -npm run typecheck - -# Проверка линтером -npm run lint - -# Запуск тестов -npm test - -# Dev сервер для проверки работы -npm run dev +npm run dev # Разработка +npm run build # Сборка production +npm run typecheck # Проверка типов +npm run lint # Проверка кода +npm run lint:fix # Автоисправление ESLint +npm run format # Форматирование Prettier +npm run db:seed # Инициализация БД +npm run db:reset # Полный сброс БД +npx prisma studio # GUI для базы данных ``` -> ⚠️ **ВАЖНО**: Всегда выполнять эти команды перед завершением задачи! +### API ИНТЕГРАЦИИ: +- **Wildberries/Ozon** - маркетплейсы +- **DaData** - валидация ИНН и реквизитов +- **SMS Aero** - отправка SMS для авторизации +- **AWS S3** - хранение файлов и изображений -## 🔄 КОМАНДЫ ОТКАТА +### ПРАВИЛА РАБОТЫ С ДОКУМЕНТАЦИЕЙ -### Откат через комментарии: +#### СТРУКТУРА ДОКУМЕНТАЦИИ СИСТЕМЫ: -**Основная команда:** +**🎯 CORE - Ядро системы** +- **DOMAIN_MODEL.md** - 4 типа организаций, основные сущности +- **BUSINESS_RULES_CORE.md** - Ключевые бизнес-правила: доступ, партнерство, расходники +**🔌 API_LAYER - Уровень API** +- **GRAPHQL_SCHEMA_RULES.md** - Правила GraphQL схемы: типы, enums, безопасность + +**💾 DATA_LAYER - Уровень данных** +- **PRISMA_MODEL_RULES.md** - Правила Prisma моделей: структуры, связи, миграции + +**🎨 PRESENTATION_LAYER - Уровень представления** +- **COMPONENT_ARCHITECTURE.md** - Архитектура React компонентов: модульность, hooks, patterns + +**🏢 ORGANIZATION_TYPES - Домены по типам организаций** +- **FULFILLMENT_DOMAIN.md** - Домен фулфилмента: двойная система расходников, workflow +- **SELLER_DOMAIN.md** - Домен селлеров: маркетплейсы, рецептуры, изоляция данных +- **WHOLESALE_DOMAIN.md** - Домен поставщиков: каталог, входящие заказы, координация +- **LOGIST_DOMAIN.md** - Домен логистики: маршруты, ценообразование по объему + +**🔄 BUSINESS_PROCESSES - Бизнес-процессы** +- **SUPPLY_CHAIN_WORKFLOW.md** - Цепочка поставок: 8 статусов, роли, переходы +- **PARTNERSHIP_SYSTEM.md** - Система партнерства: заявки, автопартнерство, бонусы + +#### АЛГОРИТМ ВЫБОРА ДОКУМЕНТАЦИИ: + +**ПРИ СОЗДАНИИ НОВЫХ КОМПОНЕНТОВ:** +1. **MODULAR_ARCHITECTURE_PATTERN.md** - Архитектурные требования (СНАЧАЛА) +2. **COMPONENT_ARCHITECTURE.md** - Паттерны реализации React компонентов +3. **DOMAIN_MODEL.md** - Понимание доменных сущностей +4. Соответствующий **organization-types/*.md** - Специфика типа организации + +**ПРИ РАБОТЕ С API:** +1. **GRAPHQL_SCHEMA_RULES.md** - Правила схемы +2. **BUSINESS_RULES_CORE.md** - Бизнес-логика +3. **PRISMA_MODEL_RULES.md** - Модели данных + +**ПРИ WORKFLOW ПОСТАВОК:** +1. **SUPPLY_CHAIN_WORKFLOW.md** - Полный процесс +2. Релевантные **organization-types/*.md** - Роли участников +3. **BUSINESS_RULES_CORE.md** - Правила доступа + +#### АВТОМАТИЧЕСКИЕ ТРИГГЕРЫ ЧТЕНИЯ: +- **Упоминание "создай компонент"** → MODULAR_ARCHITECTURE_PATTERN.md + COMPONENT_ARCHITECTURE.md +- **Упоминание "новый компонент"** → MODULAR_ARCHITECTURE_PATTERN.md + COMPONENT_ARCHITECTURE.md +- **Упоминание "архитектура"** → MODULAR_ARCHITECTURE_PATTERN.md +- **Упоминание "фулфилмент"** → FULFILLMENT_DOMAIN.md +- **Упоминание "селлер"** → SELLER_DOMAIN.md +- **Упоминание "поставщик"** → WHOLESALE_DOMAIN.md +- **Упоминание "логистика"** → LOGIST_DOMAIN.md +- **Упоминание "GraphQL"** → GRAPHQL_SCHEMA_RULES.md +- **Упоминание "компонент"** → COMPONENT_ARCHITECTURE.md +- **Упоминание "поставки"** → SUPPLY_CHAIN_WORKFLOW.md + +--- + +## 8. ИНСТРУМЕНТЫ И КОМАНДЫ + +### ОТЧЕТНОСТЬ + +После выполнения всегда показывать: +``` +✅ СДЕЛАНО: +- Создал файл X +- Добавил функцию Y + +❌ НЕ ТРОГАЛ: +- Файл Z +- Логику W + +⚠️ ПРОБЛЕМЫ: +- ESLint warning в строке N +``` + +### КОМАНДЫ ОТКАТА ЧЕРЕЗ КОММЕНТАРИИ + +#### Основная команда: ``` "откати [описание] через комментарии" ``` -**Примеры:** - +**Примеры использования:** - `"откати центрирование поиска через комментарии"` - `"откати изменения кнопки через комментарии"` - `"откати новую логику через комментарии"` -**Дополнительные команды:** +#### Алгоритм выполнения: +**ЭТАП 1: ВОССТАНОВЛЕНИЕ ИСХОДНОГО КОДА** +1. Найти измененный код в текущих файлах +2. Извлечь исходный код из git истории +3. Восстановить исходную функциональность + +**ЭТАП 2: СОЗДАНИЕ СИСТЕМЫ ПЕРЕКЛЮЧЕНИЯ** +4. Оставить **Вариант 1** (исходный) - активным +5. Добавить **Вариант 2** (измененный) в комментариях +6. Добавить четкие описания для каждого варианта + +**Пример структуры кода:** +```jsx +// Вариант 1: Исходный (активный) +
+ {/* исходный код */} +
+ +// Вариант 2: Измененный (для быстрого переключения) +/* +
+ {/* измененный код */} +
+*/ +``` + +#### Дополнительные команды: - `"очисти комментарии"` - удалить закомментированные варианты - `"переключи на вариант 2"` - активировать закомментированный код - `"покажи варианты"` - показать доступные варианты -> 📖 **Подробнее**: см. раздел 6.4 в `legacy-rules/interaction-integrity-rules.md` +#### ПРАВИЛА ПРИМЕНЕНИЯ: +- ✅ **Использовать для UI экспериментов** и небольших логических изменений +- ✅ **Всегда добавлять четкие комментарии** с описанием вариантов +- ✅ **Очищать комментарии перед финальным коммитом** +- ❌ **Не использовать для изменений архитектуры** или критической логики -## 💾 РАБОТА С КОНТЕКСТОМ +### ДОКУМЕНТИРОВАНИЕ ИЗМЕНЕНИЙ -### Файлы для сохранения контекста: +#### При любых изменениях документировать: +- **ЧТО** изменено (конкретные файлы и функции) +- **ПОЧЕМУ** изменено (обоснование решения) +- **КТО** принял решение об изменении (пользователь/автоматически) +- **КОГДА** изменено (временная метка) -- **`current-session.md`** - текущая сессия работы (активные задачи, решения, контекст) -- **`CLAUDE.md`** - системные правила и команды (этот файл) -- **TodoWrite инструмент** - для отслеживания текущих задач в рамках сессии +**Формат документирования:** +``` +📝 ИЗМЕНЕНИЕ ЗАФИКСИРОВАНО: +- ДАТА: [когда] +- ЧТО: [что именно изменено] +- ПРИЧИНА: [обоснование] +- РЕШЕНИЕ: [кто одобрил] +``` -### При потере контекста: +### АНАЛИЗ ПРИМЕРОВ КОДА -1. **Первым делом прочитать**: `current-session.md` -2. **Проверить статус задач**: через TodoWrite -3. **Восстановить контекст**: из истории изменений в current-session.md +#### Трехуровневый анализ примеров: +1. **📋 СОДЕРЖАТЕЛЬНЫЙ** - что делает код (функциональность, логика, данные) +2. **🏗️ АРХИТЕКТУРНЫЙ** - как организован (структура, взаимосвязи, позиционирование) +3. **🎨 СТИЛЕВОЙ** - как выглядит (CSS классы, анимации, цвета) -### Рекомендации для длинных сессий: +#### Алгоритм анализа примера: +1. **Прочитать** весь код компонента-примера +2. **Понять архитектуру** - где элемент размещен относительно других +3. **Понять логику** - почему именно так структурировано +4. **Адаптировать** к текущей задаче - применить принципы, не копировать +5. **Проверить** соответствие правилам проекта -- Обновлять `current-session.md` после каждой важной задачи -- Фиксировать принятые решения и обоснования -- Документировать обнаруженные проблемы и их решения -- Использовать `--resume` флаг для продолжения сессий +#### Стоп-вопросы перед реализацией: +- "Понимаю ли я **архитектуру** этого решения?" +- "Где именно должен располагаться элемент в **общей структуре**?" +- "Какова **семантическая роль** этого элемента?" +- "Как это решение **адаптируется** к моей текущей задаче?" -## 🚨 НАПОМИНАНИЕ +### ИЗВЛЕЧЕННЫЕ УРОКИ И АНТИ-ПАТТЕРНЫ -**Этот файл служит для корректной работы system-reminder'ов. Все детальные правила находятся в `legacy-rules/rules-complete1.md` и `legacy-rules/rules-complete2.md`! Новая архитектура правил в папке `docs/` находится в разработке.** +#### КРИТИЧЕСКИЕ ОШИБКИ В АНАЛИЗЕ UI КОМПОНЕНТОВ: + +**CASE STUDY: Ошибка с плавающей кнопкой из UI Kit** + +**❌ ОШИБКА**: При добавлении кнопки "🌟 Вариант 1: Плавающая кнопка слева": +1. **Поверхностный анализ**: Скопировал только стили кнопки +2. **Игнорирование архитектуры**: Не заметил, что кнопка в **отдельном контейнере** +3. **Неправильное размещение**: Добавил как часть блока контента +4. **Непонимание термина**: "Плавающая" = независимая от контента, между элементами + +#### ОБЯЗАТЕЛЬНЫЙ ЧЕК-ЛИСТ ДЛЯ UI KIT КОМПОНЕНТОВ: +``` +🔍 ПЕРЕД РЕАЛИЗАЦИЕЙ: +□ Прочитал ВЕСЬ код компонента-примера +□ Понял архитектуру размещения в layout +□ Определил семантическую роль элемента +□ Понял взаимосвязи с соседними элементами +□ Адаптировал принципы к текущей задаче +□ Проверил соответствие правилам проекта +``` + +#### АНТИ-ПАТТЕРНЫ: +- **"Быстрое копирование"** - копировать стили без понимания архитектуры +- **"Частичный анализ"** - читать только нужную часть кода +- **"Буквальное применение"** - использовать без адаптации к контексту +- **"Игнорирование контейнеров"** - не обращать внимание на DOM-структуру + +#### ПРАВИЛЬНЫЕ ПАТТЕРНЫ: +- **"Архитектурный анализ первым"** - понять структуру, потом стили +- **"Контекстная адаптация"** - применять принципы, а не код буквально +- **"Семантическое понимание"** - осознавать роль каждого элемента +- **"Итеративная проверка"** - сверяться с примером на каждом шаге + +### БЫСТРЫЕ ПРЕФИКСЫ + +- [STRICT] - строгий режим робота +- [CHECK] - только проверить +- [EXPLAIN] - только объяснить +- [FIX-ONLY] - только исправить конкретную ошибку + +### АКТИВНЫЕ ДОКУМЕНТЫ + +- README.md - техническая документация +- package.json - зависимости и скрипты +- prisma/schema.prisma - модели данных +- src/graphql/typedefs.ts - GraphQL схема +- .env - переменные окружения (не коммитить!) +- docker-compose.yml - настройки Docker +- src/lib/prisma.ts - клиент базы данных + +### РАБОТА С КОНТЕКСТОМ + +#### Файлы контекста: +- **current-session.md** - текущие задачи и решения +- **CLAUDE.md** - эти правила (загружаются автоматически) +- **TodoWrite** - инструмент для отслеживания задач + +#### При потере контекста: +1. Прочитать current-session.md +2. Проверить TodoWrite +3. Спросить у пользователя о текущей задаче + +--- + +# Важные напоминания для Claude Code +Делай только то, что просят; ни больше, ни меньше. +НИКОГДА не создавай файлы, если они не абсолютно необходимы для достижения цели. +ВСЕГДА отдавай предпочтение редактированию существующего файла, а не созданию нового. +НИКОГДА не создавай проактивно файлы документации (*.md) или README файлы. Создавай файлы документации только по явной просьбе пользователя. \ No newline at end of file diff --git a/README.md b/README.md index f75c634..6b1139a 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,186 @@ -# Sfera V - Управление бизнесом +# 🌐 Sfera - B2B Marketplace Platform -Платформа для управления различными типами бизнеса: фулфилмент, селлеры, логистика, оптовики. +**Комплексная платформа для управления бизнес-процессами в сфере электронной коммерции** -## Новые возможности +[![Next.js](https://img.shields.io/badge/Next.js-15.4.1-black?logo=next.js)](https://nextjs.org/) +[![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue?logo=typescript)](https://www.typescriptlang.org/) +[![Prisma](https://img.shields.io/badge/Prisma-6.12.0-2D3748?logo=prisma)](https://www.prisma.io/) +[![GraphQL](https://img.shields.io/badge/GraphQL-16.11.0-E10098?logo=graphql)](https://graphql.org/) -### 🏪 Склад Wildberries для селлеров +## 🏗️ Архитектура системы -Новый раздел для селлеров, позволяющий: +Sfera - это многомодульная B2B платформа, объединяющая четыре типа участников: -- **Просмотр остатков** товаров на всех складах WB в реальном времени -- **Статистика по складам** - общее количество товаров, остатки, товары в пути -- **Фильтрация и поиск** товаров по названию, артикулу, бренду -- **Детальная информация** по каждому складу отдельно -- **Красивые карточки товаров** с изображениями и статусами остатков +- **👔 Seller** - Продавцы на маркетплейсах +- **📦 Fulfillment** - Фулфилмент-центры +- **🚛 Logist** - Логистические компании +- **🏪 Wholesale** - Поставщики товаров + +## 🚀 Быстрый старт + +### Предварительные требования +- Node.js 20+ +- PostgreSQL 14+ +- npm или yarn + +### Установка + +```bash +# Клонирование проекта +git clone +cd sfera + +# Установка зависимостей +npm install + +# Настройка базы данных +cp .env.example .env +# Отредактируйте DATABASE_URL в .env + +# Инициализация БД +npx prisma db push +npm run db:seed + +# Запуск в режиме разработки +npm run dev +``` + +Приложение будет доступно по адресу: http://localhost:3000 + +### 👤 Администратор по умолчанию + +При первом запуске автоматически создается администратор: +- **Логин**: `admin` +- **Пароль**: `admin123` +- **Email**: `admin@sferav.com` + +⚠️ **ОБЯЗАТЕЛЬНО смените пароль после первого входа для безопасности!** + +### 🔄 Автоматическая инициализация БД + +**Система умно инициализируется:** +- ✅ Проверяет существующие данные +- ✅ Не создает дубликаты при повторном запуске +- ✅ Автоматическая инициализация при первом запуске приложения +- ✅ Создание 20 базовых категорий товаров +- ✅ Настройка администратора системы + +## 🔧 Команды разработчика + +### 🏃‍♂️ Основные команды +```bash +# Разработка с turbopack +npm run dev + +# Сборка для production +npm run build + +# Запуск production +npm start +``` + +### 🗄️ База данных +```bash +# Инициализация БД (создание админа и категорий) +npm run db:seed + +# Полный сброс БД и пересоздание данных +npm run db:reset + +# Применение миграций и генерация клиента +npx prisma db push + +# Prisma Studio +npx prisma studio +``` + +### 🧹 Качество кода +```bash +# Проверка ESLint +npm run lint + +# Исправление ESLint ошибок +npm run lint:fix + +# Форматирование Prettier +npm run format + +# Проверка форматирования +npm run format:check +``` + +## 🏛️ Архитектура проекта + +### 📁 Структура каталогов +``` +src/ +├── app/ # Next.js App Router +│ ├── api/ # API endpoints +│ ├── dashboard/ # Дашборды по типам организаций +│ ├── supplies/ # Управление поставками +│ ├── wb-warehouse/ # Склад WB (только для SELLER) +│ ├── employees/ # Система управления персоналом +│ ├── market/ # B2B маркетплейс +│ └── ... +├── components/ # React компоненты +│ ├── dashboard/ # Компоненты дашбордов +│ ├── supplies/ # Компоненты поставок +│ ├── wb-warehouse/ # Компоненты интерфейса склада WB +│ ├── auth/ # Система авторизации +│ └── ui/ # UI Kit на Radix UI +├── graphql/ # GraphQL слой +│ ├── typedefs.ts # GraphQL схема +│ ├── resolvers.ts # Резолверы +│ ├── security/ # Система безопасности +│ └── ... +├── lib/ # Утилиты и конфигурации +│ └── seed-init.ts # Автоматическая инициализация +├── hooks/ # React хуки +├── services/ # Внешние сервисы +│ └── wildberries-service.ts # Интеграция с API WB +└── types/ # TypeScript типы +``` + +### 🔧 Технологический стек + +#### **Frontend** +- **Next.js 15.4.1** - React фреймворк с App Router +- **React 19.1.0** - UI библиотека +- **TypeScript 5** - Типизация +- **Tailwind CSS 4** - Стилизация +- **Radix UI** - Компоненты интерфейса + +#### **Backend & API** +- **GraphQL** - API слой с Apollo Server +- **Prisma 6.12.0** - ORM для работы с БД +- **PostgreSQL** - Основная база данных +- **JWT** - Авторизация и безопасность + +#### **Интеграции** +- **Wildberries API** - Интеграция с маркетплейсом +- **Ozon API** - Интеграция с маркетплейсом +- **DaData API** - Валидация ИНН организаций и работа с реквизитами +- **SMS Aero** - Отправка SMS-сообщений для авторизации +- **AWS S3** - Хранение файлов + +#### **Инфраструктура** +- **Docker** - Контейнеризация +- **Husky** - Git hooks +- **ESLint + Prettier** - Линтинг и форматирование + +## 🏪 Новые возможности + +### 🛒 **Склад Wildberries для селлеров** + +**Доступ:** Только для пользователей с типом организации **SELLER** + +Новый раздел позволяет: + +- **📊 Просмотр остатков** товаров на всех складах WB в реальном времени +- **📈 Статистика по складам** - общее количество товаров, остатки, товары в пути +- **🔍 Фильтрация и поиск** товаров по названию, артикулу, бренду +- **📋 Детальная информация** по каждому складу отдельно +- **🎨 Красивые карточки товаров** с изображениями и статусами остатков #### Как использовать: 1. Настройте API ключ Wildberries в разделе "Настройки" → "API" @@ -20,73 +188,149 @@ 3. Система автоматически загрузит актуальные остатки с вашего аккаунта WB #### Технические особенности: -- Интеграция с официальным API Wildberries -- Поддержка всех типов складов WB -- Кэширование данных для быстрой работы -- Адаптивный дизайн в стиле платформы +- ✅ Интеграция с официальным API Wildberries +- ✅ Поддержка всех типов складов WB +- ✅ Кэширование данных для быстрой работы +- ✅ Адаптивный дизайн в стиле платформы -## База данных и инициализация +## 🔐 Система безопасности -### 🛠 Команды для работы с БД +Проект включает комплексную систему безопасности: -- `npm run db:seed` - Инициализация БД (создание админа и категорий) -- `npm run db:reset` - Полный сброс БД и пересоздание данных -- `npm run postinstall` - Генерация Prisma Client +- **🛡️ Аудит действий** - Логирование всех операций +- **🔍 Обнаружение угроз** - Автоматическое выявление подозрительной активности +- **🚨 Мониторинг доступа** - Контроль доступа к данным партнеров +- **📊 Security Dashboard** - Панель управления безопасностью -### 👤 Администратор по умолчанию +## 📊 Основные модули -При первом запуске автоматически создается админ: -- **Логин**: `admin` -- **Пароль**: `admin123` -- **Email**: `admin@sferav.com` +### 🏪 **Управление поставками** +- Многоуровневая система заказов +- Интеграция поставщик → логистика → фулфилмент +- Отслеживание статусов поставок +- Управление рецептурами товаров -⚠️ **Обязательно смените пароль после первого входа!** +### 👥 **Система партнерства** +- Реферальная программа +- Автоматическое создание бизнес-партнерств +- Управление контрагентами +- B2B мессенджер -### 📂 Категории товаров +### 📈 **Аналитика и статистика** +- Статистика продаж Wildberries/Ozon +- Анализ рекламных кампаний +- Складская аналитика +- Отчеты по сотрудникам -Автоматически создается 20 базовых категорий: -- Одежда и обувь, Косметика и парфюмерия, Дом и сад -- Детские товары, Спорт и отдых, Электроника -- И другие популярные категории... +### 🏭 **Управление складом** +- Система остатков и резервов +- Управление возвратами ПВЗ +- Складская логистика +- Инвентаризация -### 🔄 Автоматическая инициализация +## 🔌 API и интеграции -База данных инициализируется автоматически при: -- Первом запуске приложения -- Запуске команды `npm run db:seed` -- Сбросе БД через `npm run db:reset` +### GraphQL API +Основной API построен на GraphQL со следующими возможностями: +- **Аутентификация** через SMS-коды +- **CRUD операции** для всех сущностей +- **Система прав доступа** по типу организации +- **Real-time подписки** для чатов -Система умно проверяет существующие данные и не создает дубликаты. +### Внешние API +- **Wildberries API** - статистика, товары, кампании, остатки склада +- **Ozon API** - интеграция с маркетплейсом +- **DaData API** - валидация ИНН организаций, получение реквизитов компаний +- **SMS Aero** - отправка SMS-кодов для авторизации пользователей -## Структура проекта - -- `src/app/wb-warehouse/` - Страница склада WB -- `prisma/seed.js` - Скрипт инициализации БД -- `src/lib/seed-init.ts` - Автоматическая инициализация -- `src/components/wb-warehouse/` - Компоненты интерфейса склада -- `src/services/wildberries-service.ts` - Интеграция с API WB - -## Технологии - -- Next.js 15 -- React 18 -- TypeScript -- GraphQL -- Prisma -- TailwindCSS -- Shadcn/ui - -## Установка и запуск +## 🧪 Тестирование и отладка +### Скрипты отладки ```bash -npm install -npm run dev +# Анализ данных фулфилмента +node scripts/analyze-fulfillment-supplies.cjs + +# Проверка типов поставок +node scripts/check-supply-order-types.cjs + +# Очистка тестовых данных +node scripts/clear-fulfillment-data.cjs ``` -## API интеграции +### Health Check +```bash +# Проверка состояния API +curl http://localhost:3000/api/health +``` -- Wildberries API для получения остатков и информации о складах -- DaData для работы с организациями -- SMS Aero для отправки SMS +## 📚 Документация -Доступ к разделу "Склад ВБ" имеют только пользователи с типом организации "SELLER". +Детальная документация проекта находится в: +- **`docs/`** - Новая модульная архитектура документации +- **`legacy-rules/`** - Бизнес-правила и процессы +- **`CLAUDE.md`** - Системные правила разработки +- **`prisma/seed.js`** - Скрипт инициализации БД + +## 🐳 Docker + +```bash +# Сборка образа +docker build -t sfera . + +# Запуск с docker-compose +docker-compose up -d +``` + +## 🤝 Участие в разработке + +### Стиль кода +- **ESLint** - обязательное соблюдение правил +- **Prettier** - автоматическое форматирование +- **TypeScript** - строгая типизация +- **Conventional Commits** - стандарт коммитов + +### Git Flow +1. Создание feature-ветки +2. Разработка с соблюдением линтинга +3. Тестирование изменений +4. Pull Request с ревью + +## 🔧 Переменные окружения + +```bash +# База данных +DATABASE_URL="postgresql://user:pass@localhost:5432/sfera" + +# JWT +JWT_SECRET="your-secret-key" + +# API ключи маркетплейсов +WB_API_TOKEN="wildberries-token" +OZON_CLIENT_ID="ozon-client-id" +OZON_API_KEY="ozon-api-key" + +# DaData API +DADATA_API_KEY="dadata-api-key" +DADATA_SECRET="dadata-secret" + +# SMS Aero +SMS_AERO_EMAIL="your-email" +SMS_AERO_API_KEY="sms-aero-api-key" + +# S3 для файлов +AWS_ACCESS_KEY_ID="access-key" +AWS_SECRET_ACCESS_KEY="secret-key" +``` + +## 📞 Поддержка + +При возникновении проблем: +1. Проверьте статус систем через `/api/health` +2. Просмотрите логи в `dev.log` +3. Обратитесь к документации в `docs/` + +--- + +**Лицензия:** Частная разработка +**Статус:** Активная разработка +**Версия:** 0.2.0 \ No newline at end of file diff --git a/docs/business-processes/SUPPLY_CHAIN_WORKFLOW_V2.md b/docs/business-processes/SUPPLY_CHAIN_WORKFLOW_V2.md new file mode 100644 index 0000000..b266224 --- /dev/null +++ b/docs/business-processes/SUPPLY_CHAIN_WORKFLOW_V2.md @@ -0,0 +1,348 @@ +# 🚚 WORKFLOW ЦЕПОЧКИ ПОСТАВОК SFERA v2.0 + +> **⚠️ ВАЖНО:** Этот документ описывает НОВУЮ архитектуру системы поставок с разделением на отдельные типы. Для старой системы см. SUPPLY_CHAIN_WORKFLOW.md + +## 🎯 ОБЗОР НОВОЙ СИСТЕМЫ + +Система поставок SFERA v2.0 включает **5 типов поставок**, разделенных на две категории: + +### 📦 **ПОСТАВКИ НА ФУЛФИЛМЕНТ** +- `GoodsSupplyOrder` - товары селлера → склад ФФ +- `FulfillmentConsumableSupplyOrder` - расходники ФФ → склад ФФ +- `SellerConsumableSupplyOrder` - расходники селлера → склад ФФ + +### 🛒 **ПОСТАВКИ НА МАРКЕТПЛЕЙСЫ** +- `OzonSupplyOrder` - готовые продукты → Ozon +- `WildberriesSupplyOrder` - готовые продукты → Wildberries + +--- + +## 🔄 WORKFLOW ПО ТИПАМ ПОСТАВОК + +### 1️⃣ **WORKFLOW: Поставки расходников ФФ** + +```mermaid +graph TD + A[ФФ создает заказ расходников] --> B[PENDING] + B --> C{Поставщик одобряет?} + C -->|Да| D[SUPPLIER_APPROVED] + C -->|Нет| X[CANCELLED] + D --> E{Логистика назначена?} + E -->|Да| F[LOGISTICS_CONFIRMED] + E -->|Нет| D + F --> G[Поставщик отгружает] + G --> H[SHIPPED] + H --> I[IN_TRANSIT] + I --> J[ФФ принимает на склад] + J --> K[DELIVERED] + + style A fill:#e1f5fe + style K fill:#c8e6c9 + style X fill:#ffcdd2 +``` + +**Участники:** +- 🏭 **Фулфилмент** - создатель, получатель +- 🏪 **Поставщик (WHOLESALE)** - одобрение, отгрузка +- 🚛 **Логистика (LOGIST)** - доставка + +**Особенности:** +- ✅ ФФ видит все детали + устанавливает цены продажи селлерам +- ✅ Поставщик видит товары/количества, НЕ видит цены продажи ФФ +- ✅ Показывается сразу после создания + +### 2️⃣ **WORKFLOW: Поставки товаров селлера** + +```mermaid +graph TD + A[Селлер создает заказ товаров] --> B[PENDING] + B --> C{Поставщик одобряет?} + C -->|Да| D[SUPPLIER_APPROVED] + C -->|Нет| X[CANCELLED] + D --> E{Логистика назначена?} + E -->|Да| F[LOGISTICS_CONFIRMED] + E -->|Нет| D + F --> G[Поставщик отгружает] + G --> H[SHIPPED] + H --> I[IN_TRANSIT] + I --> J[ФФ принимает + обрабатывает] + J --> K[DELIVERED] + + style A fill:#fff3e0 + style K fill:#c8e6c9 + style X fill:#ffcdd2 +``` + +**Участники:** +- 🛒 **Селлер** - создатель, владелец товара +- 🏪 **Поставщик (WHOLESALE)** - поставка +- 🚛 **Логистика (LOGIST)** - доставка +- 🏭 **Фулфилмент** - получатель, обработка + +**Особенности:** +- ✅ Селлер видит свои товары + рецептуры + закупочные цены +- ✅ ФФ видит товары + рецептуры + услуги, НЕ видит закупочные цены селлера +- ✅ Поставщик видит товары + количества, НЕ видит рецептуры +- ✅ Расходники селлера идут **в состав продукта**, не отслеживаются отдельно + +### 3️⃣ **WORKFLOW: Поставки расходников селлера** + +```mermaid +graph TD + A[Селлер заказывает свои расходники] --> B[PENDING] + B --> C{Поставщик одобряет?} + C -->|Да| D[SUPPLIER_APPROVED] + C -->|Нет| X[CANCELLED] + D --> E{Логистика назначена?} + E -->|Да| F[LOGISTICS_CONFIRMED] + E -->|Нет| D + F --> G[Поставщик отгружает] + G --> H[SHIPPED] + H --> I[IN_TRANSIT] + I --> J[ФФ принимает НА ХРАНЕНИЕ] + J --> K[DELIVERED] + + style A fill:#f3e5f5 + style K fill:#c8e6c9 + style X fill:#ffcdd2 +``` + +**Участники:** +- 🛒 **Селлер** - создатель, владелец расходников +- 🏪 **Поставщик (WHOLESALE)** - поставка +- 🚛 **Логистика (LOGIST)** - доставка +- 🏭 **Фулфилмент** - хранитель (НЕ владелец) + +**Особенности:** +- ✅ Селлер видит свои расходники + закупочные цены +- ✅ ФФ видит факт хранения + количества, НЕ видит закупочные цены селлера +- ✅ Срок хранения + права доступа настраиваются +- ✅ Используются селлером в рецептурах своих товаров + +--- + +## 📊 СТАТУСЫ И ПЕРЕХОДЫ + +### **SupplyOrderStatus (поставки НА фулфилмент)** + +| Статус | Описание | Ответственный | Действия | +|--------|----------|---------------|----------| +| `PENDING` | Ожидает одобрения поставщика | Поставщик | Одобрить/Отклонить | +| `SUPPLIER_APPROVED` | Одобрено поставщиком | Логистика | Назначить маршрут | +| `LOGISTICS_CONFIRMED` | Логистика подтверждена | Поставщик | Отгрузить товар | +| `SHIPPED` | Отгружено | Система | Автоматический переход | +| `IN_TRANSIT` | В пути | Логистика | Отслеживание доставки | +| `DELIVERED` | Доставлено | ФФ | Принять на склад | +| `CANCELLED` | Отменено | Любой участник | Указать причину | + +### **MarketplaceSupplyStatus (поставки НА маркетплейсы)** + +| Статус | Описание | Ответственный | Действия | +|--------|----------|---------------|----------| +| `PLANNED` | Запланирована | ФФ | Подготовить товары | +| `PREPARED` | Подготовлена | ФФ | Отгрузить | +| `SHIPPED_TO_MARKETPLACE` | Отгружена | Маркетплейс | Принять товар | +| `ACCEPTED_BY_MARKETPLACE` | Принята | Система | Обновить остатки | +| `CANCELLED` | Отменена | ФФ/Маркетплейс | Указать причину | + +--- + +## 🎭 РОЛИ И ПРАВА ДОСТУПА + +### 🏭 **ФУЛФИЛМЕНТ** + +**Может создавать:** +- ✅ Поставки расходников ФФ +- ✅ Поставки на маркетплейсы + +**Может видеть:** +- ✅ Свои поставки расходников: все детали + цены продажи +- ✅ Товарные поставки селлеров: товары + рецептуры, НЕ закупочные цены +- ✅ Расходники селлеров на хранении: количества, НЕ закупочные цены + +### 🛒 **СЕЛЛЕР** + +**Может создавать:** +- ✅ Товарные поставки +- ✅ Поставки расходников селлера + +**Может видеть:** +- ✅ Свои товарные поставки: все детали + рецептуры + закупочные цены +- ✅ Свои расходники: все детали + закупочные цены +- ❌ Чужие поставки +- ❌ Поставки расходников ФФ + +### 🏪 **ПОСТАВЩИК (WHOLESALE)** + +**Может видеть:** +- ✅ Заказы к себе: товары + количества +- ❌ Рецептуры товаров +- ❌ Цены продажи ФФ селлерам +- ❌ Услуги ФФ + +### 🚛 **ЛОГИСТИКА (LOGIST)** + +**Может видеть:** +- ✅ Маршруты + объемы + вес +- ❌ Коммерческие данные (цены, услуги) +- ❌ Рецептуры товаров + +--- + +## 🌐 ИНТЕРФЕЙСЫ СИСТЕМЫ + +### 📦 **Кабинет фулфилмента** + +**URL:** `/fulfillment-supplies/` + +**Вкладки:** +- `ff-consumables` - поставки расходников ФФ (создание + просмотр) +- `seller-consumables` - расходники селлеров на хранении +- `goods?status=new` - новые товарные поставки +- `goods?status=receiving` - товары в приемке +- `goods?status=accepted` - принятые товары + +### 🛒 **Кабинет селлера** + +**URL:** `/seller-supplies/` + +**Вкладки:** +- `my-goods` - мои товарные поставки +- `my-consumables` - мои расходники +- `create-goods` - создать поставку товаров +- `create-consumables` - заказать расходники + +### 🛍️ **Кабинет маркетплейсов** + +**URL:** `/marketplace-supplies/` + +**Вкладки:** +- `ozon` - поставки на Ozon +- `wildberries` - поставки на Wildberries +- `create-ozon` - создать поставку на Ozon +- `create-wildberries` - создать поставку на WB + +--- + +## ⚡ ОСОБЕННОСТИ РЕАЛИЗАЦИИ + +### 🔄 **Дополнение данных по этапам** + +Каждая поставка - это **одна запись**, которая дополняется участниками: + +```typescript +// Создание (селлер/ФФ) +supply = { + id: "...", + status: "PENDING", + sellerId: "...", // кто создал + requestedDate: "...", // когда нужно + items: [...] // что заказано +} + +// Одобрение (поставщик) +supply = { + ...supply, + status: "SUPPLIER_APPROVED", + supplierId: "...", // кто одобрил + approvedAt: "...", // когда одобрил + packagesCount: 5, // уточненные параметры + estimatedVolume: 2.5 +} + +// Назначение логистики (ФФ) +supply = { + ...supply, + status: "LOGISTICS_CONFIRMED", + logisticsPartnerId: "...", // кто повезет + routeId: "...", // маршрут + logisticsCost: 1500 // стоимость +} +``` + +### 🔐 **Фильтрация по безопасности** + +Каждый resolver применяет фильтрацию по роли: + +```typescript +// Пример: поставки расходников ФФ +const supplies = await prisma.fulfillmentConsumableSupplyOrder.findMany({ + where: { + // Только свои поставки для ФФ + fulfillmentCenterId: user.organizationId + } +}) + +// Фильтрация полей по роли +return supplies.map(supply => + SupplyDataFilter.filterByRole(supply, user.organizationType) +) +``` + +### 📈 **Масштабируемость** + +Новые типы поставок добавляются независимо: + +```typescript +// Будущее: Поставки на Яндекс.Маркет +interface YandexMarketSupplyOrder { + // специфичные поля для Яндекс.Маркета +} + +// Отдельные операции +createYandexMarketSupply() +myYandexMarketSupplies() +``` + +--- + +## ✅ ПРЕИМУЩЕСТВА НОВОЙ АРХИТЕКТУРЫ + +### 🎯 **Четкое разделение ответственности** +- Каждый тип поставки имеет свою логику +- Независимая разработка и тестирование +- Простота добавления новых типов + +### 🔒 **Надежная безопасность** +- Раздельные правила доступа для каждого типа +- Невозможно случайно показать чужие данные +- Гранулярные права по ролям + +### 📈 **Масштабируемость** +- Легко добавлять новые маркетплейсы +- Независимые схемы для разных процессов +- Оптимизация каждого типа отдельно + +### 🛡️ **Безопасная миграция** +- Поэтапное внедрение без остановки системы +- Система откатов на каждом этапе +- Сохранение работоспособности старой системы + +--- + +## 🚀 ПЛАН ВНЕДРЕНИЯ + +### **Phase 1:** FulfillmentConsumableSupplyOrder ⏳ +- Новая модель данных +- GraphQL операции +- Интерфейс создания и просмотра +- Тестирование + +### **Phase 2:** SellerConsumableSupplyOrder +- Аналогично Phase 1 +- Интеграция с системой хранения + +### **Phase 3:** GoodsSupplyOrder +- Самый сложный тип с рецептурами +- Миграция существующих товарных поставок + +### **Phase 4:** Поставки на маркетплейсы +- Отдельная система для Ozon/WB +- API интеграции с маркетплейсами + +### **Phase 5:** Очистка и оптимизация +- Миграция старых данных +- Удаление устаревшего кода (с одобрения) +- Финальная оптимизация + +**Следующий шаг:** Начало реализации Phase 1 - FulfillmentConsumableSupplyOrder \ No newline at end of file diff --git a/docs/business-processes/SUPPLY_SYSTEM_ARCHITECTURE.md b/docs/business-processes/SUPPLY_SYSTEM_ARCHITECTURE.md new file mode 100644 index 0000000..d624de3 --- /dev/null +++ b/docs/business-processes/SUPPLY_SYSTEM_ARCHITECTURE.md @@ -0,0 +1,487 @@ +# 🚚 АРХИТЕКТУРА СИСТЕМЫ ПОСТАВОК SFERA + +## 🎯 ОБЗОР СИСТЕМЫ + +Система поставок SFERA включает **5 типов поставок**, разделенных на две категории: + +### 📦 **ПОСТАВКИ НА ФУЛФИЛМЕНТ (3 типа)** +1. **Товарные поставки** - товары селлера → склад ФФ +2. **Поставки расходников ФФ** - расходники ФФ → склад ФФ +3. **Поставки расходников селлеров** - расходники селлера → склад ФФ (на хранение) + +### 🛒 **ПОСТАВКИ НА МАРКЕТПЛЕЙСЫ (2+ типов)** +4. **Поставки на Ozon** - готовые продукты со склада ФФ → Ozon +5. **Поставки на Wildberries** - готовые продукты со склада ФФ → Wildberries + +--- + +## 📊 АРХИТЕКТУРА ДАННЫХ + +### ✅ **ПРИНЦИП: ОТДЕЛЬНАЯ ТАБЛИЦА ДЛЯ КАЖДОГО ТИПА ПОСТАВКИ** + +#### **Преимущества подхода:** +- 🎯 Четкое разделение ответственности +- 🔒 Упрощение системы безопасности +- 📈 Независимые схемы для разных процессов +- 🔧 Простота миграций и изменений +- 📝 Специфичные поля для каждого типа + +### 🏗️ **СТРУКТУРА ТАБЛИЦ ПОСТАВОК НА ФУЛФИЛМЕНТ** + +#### **1. GoodsSupplyOrder - Товарные поставки** +```typescript +interface GoodsSupplyOrder { + // === БАЗОВЫЕ ПОЛЯ === + id: string + status: SupplyOrderStatus + createdAt: DateTime + updatedAt: DateTime + + // === ДАННЫЕ СЕЛЛЕРА (создатель) === + sellerId: string // кто заказывает + fulfillmentCenterId: string // куда доставить + requestedDeliveryDate: DateTime // когда нужно + + // === ДАННЫЕ ПОСТАВЩИКА === + supplierId: string // кто поставляет + supplierApprovedAt?: DateTime // когда одобрил + packagesCount?: number // количество грузомест + estimatedVolume?: number // объем груза + supplierContractId?: string // номер договора + + // === ДАННЫЕ ЛОГИСТИКИ === + logisticsPartnerId?: string // кто везет + estimatedDeliveryDate?: DateTime // план доставки + routeId?: string // маршрут + logisticsCost?: number // стоимость доставки + + // === ДАННЫЕ ОТГРУЗКИ === + shippedAt?: DateTime // факт отгрузки + + // === ДАННЫЕ ПРИЕМКИ === + receivedAt?: DateTime // факт приемки + receivedById?: string // кто принял (сотрудник ФФ) + actualQuantity?: number // принято количество + defectQuantity?: number // брак + + // === УНИКАЛЬНЫЕ ПОЛЯ ДЛЯ ТОВАРОВ === + hasRecipes: boolean // есть ли рецептуры + totalServicesValue?: number // стоимость услуг ФФ + + // === СВЯЗИ === + items: GoodsSupplyItem[] +} + +interface GoodsSupplyItem { + id: string + supplyOrderId: string // связь с поставкой + productId: string // какой товар + requestedQuantity: number // запросили + approvedQuantity?: number // поставщик одобрил + shippedQuantity?: number // отгрузили + receivedQuantity?: number // приняли + defectQuantity?: number // брак + unitPrice: number // цена за единицу + totalPrice: number // общая стоимость + + // === РЕЦЕПТУРА (JSON) === + recipe?: { + services: string[] // ID услуг ФФ + fulfillmentConsumables: string[] // ID расходников ФФ + sellerConsumables: string[] // ID расходников селлера + marketplaceCardId?: string // карточка товара + } +} +``` + +#### **2. FulfillmentConsumableSupplyOrder - Поставки расходников ФФ** +```typescript +interface FulfillmentConsumableSupplyOrder { + // === БАЗОВЫЕ ПОЛЯ === + id: string + status: SupplyOrderStatus + createdAt: DateTime + updatedAt: DateTime + + // === ДАННЫЕ ФФ (создатель) === + fulfillmentCenterId: string // кто заказывает + requestedDeliveryDate: DateTime // когда нужно + + // === ДАННЫЕ ПОСТАВЩИКА === + supplierId: string // кто поставляет + supplierApprovedAt?: DateTime // когда одобрил + packagesCount?: number // количество грузомест + estimatedVolume?: number // объем груза + supplierContractId?: string // номер договора + + // === ДАННЫЕ ЛОГИСТИКИ === + logisticsPartnerId?: string // кто везет + estimatedDeliveryDate?: DateTime // план доставки + routeId?: string // маршрут + logisticsCost?: number // стоимость доставки + + // === ДАННЫЕ ОТГРУЗКИ === + shippedAt?: DateTime // факт отгрузки + + // === ДАННЫЕ ПРИЕМКИ === + receivedAt?: DateTime // факт приемки + receivedById?: string // кто принял (сотрудник ФФ) + actualQuantity?: number // принято количество + defectQuantity?: number // брак + + // === УНИКАЛЬНЫЕ ПОЛЯ ДЛЯ РАСХОДНИКОВ ФФ === + resalePricePerUnit?: number // цена продажи селлерам + minStockLevel?: number // складские лимиты + + // === СВЯЗИ === + items: FulfillmentConsumableSupplyItem[] +} + +interface FulfillmentConsumableSupplyItem { + id: string + supplyOrderId: string // связь с поставкой + productId: string // какой расходник + requestedQuantity: number // запросили + approvedQuantity?: number // поставщик одобрил + shippedQuantity?: number // отгрузили + receivedQuantity?: number // приняли + defectQuantity?: number // брак + unitPrice: number // цена за единицу от поставщика + totalPrice: number // общая стоимость +} +``` + +#### **3. SellerConsumableSupplyOrder - Поставки расходников селлеров** +```typescript +interface SellerConsumableSupplyOrder { + // === БАЗОВЫЕ ПОЛЯ === + id: string + status: SupplyOrderStatus + createdAt: DateTime + updatedAt: DateTime + + // === ДАННЫЕ СЕЛЛЕРА (создатель) === + sellerId: string // кто заказывает + fulfillmentCenterId: string // где будет храниться + requestedDeliveryDate: DateTime // когда нужно + + // === ДАННЫЕ ПОСТАВЩИКА === + supplierId: string // кто поставляет + supplierApprovedAt?: DateTime // когда одобрил + packagesCount?: number // количество грузомест + estimatedVolume?: number // объем груза + supplierContractId?: string // номер договора + + // === ДАННЫЕ ЛОГИСТИКИ === + logisticsPartnerId?: string // кто везет + estimatedDeliveryDate?: DateTime // план доставки + routeId?: string // маршрут + logisticsCost?: number // стоимость доставки + + // === ДАННЫЕ ОТГРУЗКИ === + shippedAt?: DateTime // факт отгрузки + + // === ДАННЫЕ ПРИЕМКИ === + receivedAt?: DateTime // факт приемки ФФ + receivedById?: string // кто принял (сотрудник ФФ) + actualQuantity?: number // принято количество + defectQuantity?: number // брак + + // === УНИКАЛЬНЫЕ ПОЛЯ ДЛЯ РАСХОДНИКОВ СЕЛЛЕРОВ === + storageTermMonths?: number // срок хранения + accessRights: 'SELLER_ONLY' | 'SHARED_WITH_FF' // кто может использовать + storageCostPerMonth?: number // стоимость хранения + + // === СВЯЗИ === + items: SellerConsumableSupplyItem[] +} + +interface SellerConsumableSupplyItem { + id: string + supplyOrderId: string // связь с поставкой + productId: string // какой расходник селлера + requestedQuantity: number // запросили + approvedQuantity?: number // поставщик одобрил + shippedQuantity?: number // отгрузили + receivedQuantity?: number // приняли + defectQuantity?: number // брак + unitPrice: number // цена за единицу + totalPrice: number // общая стоимость +} +``` + +### 🛒 **СТРУКТУРА ТАБЛИЦ ПОСТАВОК НА МАРКЕТПЛЕЙСЫ** + +#### **4. OzonSupplyOrder - Поставки на Ozon** +```typescript +interface OzonSupplyOrder { + // === БАЗОВЫЕ ПОЛЯ === + id: string + status: MarketplaceSupplyStatus + createdAt: DateTime + updatedAt: DateTime + + // === СПЕЦИФИЧНЫЕ ПОЛЯ OZON === + ozonWarehouseId: string // склад Ozon + ozonSupplyId?: string // ID поставки в системе Ozon + // ... дополнительные поля для Ozon API + + // === СВЯЗИ === + items: OzonSupplyItem[] +} +``` + +#### **5. WildberriesSupplyOrder - Поставки на Wildberries** +```typescript +interface WildberriesSupplyOrder { + // === БАЗОВЫЕ ПОЛЯ === + id: string + status: MarketplaceSupplyStatus + createdAt: DateTime + updatedAt: DateTime + + // === СПЕЦИФИЧНЫЕ ПОЛЯ WB === + wbWarehouseId: string // склад WB + wbSupplyId?: string // ID поставки в системе WB + // ... дополнительные поля для WB API + + // === СВЯЗИ === + items: WildberriesSupplyItem[] +} +``` + +--- + +## 🔐 СИСТЕМА БЕЗОПАСНОСТИ + +### 📋 **ПРАВИЛА ДОСТУПА К ПОСТАВКАМ РАСХОДНИКОВ ФФ** + +**Кто видит и что:** +- ✅ **ФФ (создатель):** все детали + цены + остатки +- ✅ **Поставщик:** товары, количества, НЕ цены продажи ФФ +- ✅ **Логистика:** маршруты, объемы, НЕ коммерческие данные +- ❌ **Селлеры:** вообще не видят + +### 📋 **ПРАВИЛА ДОСТУПА К ПОСТАВКАМ РАСХОДНИКОВ СЕЛЛЕРОВ** + +**Кто видит и что:** +- ✅ **Селлер (создатель):** все свои детали + свои цены +- ✅ **ФФ (хранитель):** факт хранения + количества, НЕ закупочные цены селлера +- ✅ **Поставщик:** товары, количества для селлера +- ✅ **Логистика:** маршруты, объемы +- ❌ **Другие селлеры:** не видят чужие расходники + +### 📋 **ПРАВИЛА ДОСТУПА К ТОВАРНЫМ ПОСТАВКАМ** + +**Кто видит и что:** +- ✅ **Селлер (создатель):** все детали своих товаров + рецептуры +- ✅ **ФФ (получатель):** товары + рецептуры + услуги, НЕ закупочные цены селлера +- ✅ **Поставщик:** товары + количества, НЕ рецептуры и НЕ цены ФФ +- ✅ **Логистика:** маршруты + объемы, НЕ коммерческие данные +- ❌ **Другие селлеры:** не видят чужие поставки + +--- + +## 🌐 URL-СТРУКТУРА ИНТЕРФЕЙСОВ + +### 📦 **ПОСТАВКИ НА ФУЛФИЛМЕНТ** +``` +/fulfillment-supplies/goods?status=new # Товар → Новые +/fulfillment-supplies/goods?status=receiving # Товар → Приёмка +/fulfillment-supplies/goods?status=accepted # Товар → Принято +/fulfillment-supplies/ff-consumables # Расходники фулфилмента +/fulfillment-supplies/seller-consumables # Расходники селлеров +/fulfillment-supplies/create-consumables # Создание поставки расходников ФФ +``` + +### 🛒 **ПОСТАВКИ НА МАРКЕТПЛЕЙСЫ** +``` +/marketplace-supplies/ozon # Поставки на Ozon +/marketplace-supplies/wildberries # Поставки на Wildberries +/marketplace-supplies/create-ozon # Создание поставки на Ozon +/marketplace-supplies/create-wildberries # Создание поставки на WB +``` + +--- + +## ⚡ GRAPHQL API АРХИТЕКТУРА + +### 🎯 **ПРИНЦИП: ОТДЕЛЬНЫЕ ОПЕРАЦИИ ДЛЯ КАЖДОГО ТИПА** + +#### **Поставки расходников ФФ:** +```graphql +# Queries +myFulfillmentConsumableSupplies: [FulfillmentConsumableSupplyOrder!]! +fulfillmentConsumableSupply(id: ID!): FulfillmentConsumableSupplyOrder + +# Mutations +createFulfillmentConsumableSupply(input: CreateFulfillmentConsumableSupplyInput!): CreateSupplyResult! +updateFulfillmentConsumableSupply(id: ID!, input: UpdateFulfillmentConsumableSupplyInput!): UpdateSupplyResult! +``` + +#### **Поставки расходников селлеров:** +```graphql +# Queries +mySellerConsumableSupplies: [SellerConsumableSupplyOrder!]! +sellerConsumableSupply(id: ID!): SellerConsumableSupplyOrder + +# Mutations +createSellerConsumableSupply(input: CreateSellerConsumableSupplyInput!): CreateSupplyResult! +updateSellerConsumableSupply(id: ID!, input: UpdateSellerConsumableSupplyInput!): UpdateSupplyResult! +``` + +#### **Товарные поставки:** +```graphql +# Queries +myGoodsSupplies: [GoodsSupplyOrder!]! +goodsSupply(id: ID!): GoodsSupplyOrder + +# Mutations +createGoodsSupply(input: CreateGoodsSupplyInput!): CreateSupplyResult! +updateGoodsSupply(id: ID!, input: UpdateGoodsSupplyInput!): UpdateSupplyResult! +``` + +#### **Поставки на маркетплейсы (отдельная система):** +```graphql +# Queries +myOzonSupplies: [OzonSupplyOrder!]! +myWildberriesSupplies: [WildberriesSupplyOrder!]! + +# Mutations +createOzonSupply(input: CreateOzonSupplyInput!): CreateMarketplaceSupplyResult! +createWildberriesSupply(input: CreateWildberriesSupplyInput!): CreateMarketplaceSupplyResult! +``` + +--- + +## 🔄 ПЛАН ПОЭТАПНОЙ МИГРАЦИИ + +### ✅ **ПРИНЦИПЫ БЕЗОПАСНОЙ МИГРАЦИИ** +- 🛡️ Сохранение работоспособности существующей системы +- 🔄 Система откатов на каждом этапе +- 🧪 Тестирование каждого типа поставки отдельно +- 📝 Модульная архитектура +- ❌ **Удаление старого кода ТОЛЬКО после одобрения пользователя** + +### **ЭТАП 1: FulfillmentConsumableSupplyOrder** + +#### **1.1 Создание новой модели данных** +```typescript +// Новая таблица параллельно со старой +model FulfillmentConsumableSupplyOrder { + // ... вся структура +} + +// Старая таблица остается для совместимости +model SupplyOrder { + // ... существующая структура +} +``` + +#### **1.2 GraphQL API** +```graphql +# Новые операции +createFulfillmentConsumableSupply() +myFulfillmentConsumableSupplies() + +# Старые операции остаются работать +createSupplyOrder() # для других типов +mySupplyOrders() # для других типов +``` + +#### **1.3 Интерфейс** +- Форма `/fulfillment-supplies/create-consumables` → новая мутация +- Вкладка `/fulfillment-supplies/ff-consumables` → новый запрос + +#### **1.4 Тестирование** +- ✅ Создание поставки расходников ФФ +- ✅ Отображение в интерфейсе +- ✅ Безопасность доступа +- ✅ Работа старой системы + +### **ЭТАП 2: SellerConsumableSupplyOrder** +- Аналогично этапу 1 +- Тестирование + интеграция + +### **ЭТАП 3: GoodsSupplyOrder** +- Аналогично этапу 1 +- Тестирование + интеграция + +### **ЭТАП 4: Очистка (ТОЛЬКО после одобрения)** +- Миграция данных из старых таблиц +- Удаление старого кода +- Обновление документации + +--- + +## 🎯 СТАТУСЫ ПОСТАВОК + +### **SupplyOrderStatus (для поставок НА фулфилмент)** +```typescript +enum SupplyOrderStatus { + PENDING // Создана, ждет одобрения поставщика + SUPPLIER_APPROVED // Одобрена поставщиком + LOGISTICS_CONFIRMED // Логистика подтверждена + SHIPPED // Отгружена поставщиком + IN_TRANSIT // В пути + DELIVERED // Доставлена на склад ФФ + CANCELLED // Отменена +} +``` + +### **MarketplaceSupplyStatus (для поставок НА маркетплейсы)** +```typescript +enum MarketplaceSupplyStatus { + PLANNED // Запланирована + PREPARED // Подготовлена к отгрузке + SHIPPED_TO_MARKETPLACE // Отгружена на маркетплейс + ACCEPTED_BY_MARKETPLACE // Принята маркетплейсом + CANCELLED // Отменена +} +``` + +--- + +## 📊 ИНТЕГРАЦИИ + +### **Поставки НА фулфилмент:** +- 🏪 **DaData API** - валидация ИНН поставщиков +- 📱 **SMS Aero** - уведомления участникам +- ☁️ **AWS S3** - документы поставок +- 🚚 **Логистические партнеры** - трекинг доставки + +### **Поставки НА маркетплейсы:** +- 🛒 **Ozon API** - создание поставок +- 🛍️ **Wildberries API** - создание поставок +- 📦 **Трекинг системы** - статусы доставки +- 💰 **Биллинг системы** - расчет комиссий + +--- + +## 🔍 МОНИТОРИНГ И АУДИТ + +### **Коммерческие данные:** +- 📊 Логирование доступа к ценам +- 🔐 Аудит прав доступа по ролям +- 📈 Метрики безопасности +- ⚠️ Алерты на подозрительную активность + +### **Операционные метрики:** +- ⏱️ Время выполнения поставок +- 📦 Процент успешных доставок +- 🔄 SLA по статусам +- 📊 Аналитика по типам поставок + +--- + +## ✅ ЗАКЛЮЧЕНИЕ + +Новая архитектура системы поставок обеспечивает: + +- 🎯 **Четкое разделение** типов поставок +- 🔒 **Надежную безопасность** коммерческих данных +- 📈 **Масштабируемость** для новых маркетплейсов +- 🔧 **Простоту развития** каждого типа независимо +- 🛡️ **Безопасную миграцию** без потери данных + +**Следующий шаг:** Поэтапная реализация начиная с `FulfillmentConsumableSupplyOrder` \ No newline at end of file diff --git a/docs/development/DATABASE_SCHEMA_V2.md b/docs/development/DATABASE_SCHEMA_V2.md new file mode 100644 index 0000000..1c30fa5 --- /dev/null +++ b/docs/development/DATABASE_SCHEMA_V2.md @@ -0,0 +1,600 @@ +# 🗄️ СХЕМА БАЗЫ ДАННЫХ SFERA v2.0 - СИСТЕМА ПОСТАВОК + +> **⚠️ ВАЖНО:** Этот документ описывает НОВЫЕ таблицы для системы поставок v2.0. Существующие таблицы остаются без изменений для обратной совместимости. + +## 📦 НОВЫЕ ТАБЛИЦЫ ПОСТАВОК НА ФУЛФИЛМЕНТ + +### 1️⃣ **FulfillmentConsumableSupplyOrder - Поставки расходников ФФ** + +```prisma +model FulfillmentConsumableSupplyOrder { + // === БАЗОВЫЕ ПОЛЯ === + id String @id @default(cuid()) + status SupplyOrderStatusV2 @default(PENDING) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + // === ДАННЫЕ ФФ (создатель) === + fulfillmentCenterId String // кто заказывает (FK: Organization) + requestedDeliveryDate DateTime // когда нужно + resalePricePerUnit Decimal? @db.Decimal(10, 2) // цена продажи селлерам + minStockLevel Int? // минимальный остаток + notes String? // заметки ФФ + + // === ДАННЫЕ ПОСТАВЩИКА === + supplierId String? // кто поставляет (FK: Organization) + supplierApprovedAt DateTime? // когда одобрил + packagesCount Int? // количество грузомест + estimatedVolume Decimal? @db.Decimal(8, 3) // объем груза в м³ + supplierContractId String? // номер договора + supplierNotes String? // заметки поставщика + + // === ДАННЫЕ ЛОГИСТИКИ === + logisticsPartnerId String? // кто везет (FK: Organization) + estimatedDeliveryDate DateTime? // план доставки + routeId String? // маршрут (FK: LogisticsRoute) + logisticsCost Decimal? @db.Decimal(10, 2) // стоимость доставки + logisticsNotes String? // заметки логистики + + // === ДАННЫЕ ОТГРУЗКИ === + shippedAt DateTime? // факт отгрузки + trackingNumber String? // номер отслеживания + + // === ДАННЫЕ ПРИЕМКИ === + receivedAt DateTime? // факт приемки + receivedById String? // кто принял (FK: User) + actualQuantity Int? // принято количество + defectQuantity Int? // брак + receiptNotes String? // заметки приемки + + // === СВЯЗИ === + fulfillmentCenter Organization @relation("FFSupplyOrdersFulfillment", fields: [fulfillmentCenterId], references: [id]) + supplier Organization? @relation("FFSupplyOrdersSupplier", fields: [supplierId], references: [id]) + logisticsPartner Organization? @relation("FFSupplyOrdersLogistics", fields: [logisticsPartnerId], references: [id]) + receivedBy User? @relation("FFSupplyOrdersReceiver", fields: [receivedById], references: [id]) + items FulfillmentConsumableSupplyItem[] + + @@map("fulfillment_consumable_supply_orders") +} + +model FulfillmentConsumableSupplyItem { + id String @id @default(cuid()) + supplyOrderId String // связь с поставкой + productId String // какой расходник (FK: Product) + + // === КОЛИЧЕСТВА === + requestedQuantity Int // запросили + approvedQuantity Int? // поставщик одобрил + shippedQuantity Int? // отгрузили + receivedQuantity Int? // приняли + defectQuantity Int? @default(0) // брак + + // === ЦЕНЫ === + unitPrice Decimal @db.Decimal(10, 2) // цена за единицу от поставщика + totalPrice Decimal @db.Decimal(12, 2) // общая стоимость + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + // === СВЯЗИ === + supplyOrder FulfillmentConsumableSupplyOrder @relation(fields: [supplyOrderId], references: [id], onDelete: Cascade) + product Product @relation("FFSupplyItems", fields: [productId], references: [id]) + + @@unique([supplyOrderId, productId]) + @@map("fulfillment_consumable_supply_items") +} +``` + +### 2️⃣ **SellerConsumableSupplyOrder - Поставки расходников селлеров** + +```prisma +model SellerConsumableSupplyOrder { + // === БАЗОВЫЕ ПОЛЯ === + id String @id @default(cuid()) + status SupplyOrderStatusV2 @default(PENDING) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + // === ДАННЫЕ СЕЛЛЕРА (создатель) === + sellerId String // кто заказывает (FK: Organization) + fulfillmentCenterId String // где будет храниться (FK: Organization) + requestedDeliveryDate DateTime // когда нужно + notes String? // заметки селлера + + // === ДАННЫЕ ПОСТАВЩИКА === + supplierId String? // кто поставляет (FK: Organization) + supplierApprovedAt DateTime? // когда одобрил + packagesCount Int? // количество грузомест + estimatedVolume Decimal? @db.Decimal(8, 3) // объем груза в м³ + supplierContractId String? // номер договора + supplierNotes String? // заметки поставщика + + // === ДАННЫЕ ЛОГИСТИКИ === + logisticsPartnerId String? // кто везет (FK: Organization) + estimatedDeliveryDate DateTime? // план доставки + routeId String? // маршрут (FK: LogisticsRoute) + logisticsCost Decimal? @db.Decimal(10, 2) // стоимость доставки + logisticsNotes String? // заметки логистики + + // === ДАННЫЕ ОТГРУЗКИ === + shippedAt DateTime? // факт отгрузки + trackingNumber String? // номер отслеживания + + // === ДАННЫЕ ПРИЕМКИ === + receivedAt DateTime? // факт приемки ФФ + receivedById String? // кто принял (FK: User) + actualQuantity Int? // принято количество + defectQuantity Int? // брак + receiptNotes String? // заметки приемки + + // === УНИКАЛЬНЫЕ ПОЛЯ ДЛЯ РАСХОДНИКОВ СЕЛЛЕРОВ === + storageTermMonths Int? // срок хранения в месяцах + accessRights SellerConsumableAccessRights @default(SELLER_ONLY) + storageCostPerMonth Decimal? @db.Decimal(8, 2) // стоимость хранения за месяц + + // === СВЯЗИ === + seller Organization @relation("SellerSupplyOrdersSeller", fields: [sellerId], references: [id]) + fulfillmentCenter Organization @relation("SellerSupplyOrdersFulfillment", fields: [fulfillmentCenterId], references: [id]) + supplier Organization? @relation("SellerSupplyOrdersSupplier", fields: [supplierId], references: [id]) + logisticsPartner Organization? @relation("SellerSupplyOrdersLogistics", fields: [logisticsPartnerId], references: [id]) + receivedBy User? @relation("SellerSupplyOrdersReceiver", fields: [receivedById], references: [id]) + items SellerConsumableSupplyItem[] + + @@map("seller_consumable_supply_orders") +} + +model SellerConsumableSupplyItem { + id String @id @default(cuid()) + supplyOrderId String // связь с поставкой + productId String // какой расходник селлера (FK: Product) + + // === КОЛИЧЕСТВА === + requestedQuantity Int // запросили + approvedQuantity Int? // поставщик одобрил + shippedQuantity Int? // отгрузили + receivedQuantity Int? // приняли + defectQuantity Int? @default(0) // брак + + // === ЦЕНЫ (видит только селлер) === + unitPrice Decimal @db.Decimal(10, 2) // цена за единицу от поставщика + totalPrice Decimal @db.Decimal(12, 2) // общая стоимость + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + // === СВЯЗИ === + supplyOrder SellerConsumableSupplyOrder @relation(fields: [supplyOrderId], references: [id], onDelete: Cascade) + product Product @relation("SellerSupplyItems", fields: [productId], references: [id]) + + @@unique([supplyOrderId, productId]) + @@map("seller_consumable_supply_items") +} +``` + +### 3️⃣ **GoodsSupplyOrder - Товарные поставки** + +```prisma +model GoodsSupplyOrder { + // === БАЗОВЫЕ ПОЛЯ === + id String @id @default(cuid()) + status SupplyOrderStatusV2 @default(PENDING) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + // === ДАННЫЕ СЕЛЛЕРА (создатель) === + sellerId String // кто заказывает (FK: Organization) + fulfillmentCenterId String // куда доставить (FK: Organization) + requestedDeliveryDate DateTime // когда нужно + notes String? // заметки селлера + + // === ДАННЫЕ ПОСТАВЩИКА === + supplierId String? // кто поставляет (FK: Organization) + supplierApprovedAt DateTime? // когда одобрил + packagesCount Int? // количество грузомест + estimatedVolume Decimal? @db.Decimal(8, 3) // объем груза в м³ + supplierContractId String? // номер договора + supplierNotes String? // заметки поставщика + + // === ДАННЫЕ ЛОГИСТИКИ === + logisticsPartnerId String? // кто везет (FK: Organization) + estimatedDeliveryDate DateTime? // план доставки + routeId String? // маршрут (FK: LogisticsRoute) + logisticsCost Decimal? @db.Decimal(10, 2) // стоимость доставки + logisticsNotes String? // заметки логистики + + // === ДАННЫЕ ОТГРУЗКИ === + shippedAt DateTime? // факт отгрузки + trackingNumber String? // номер отслеживания + + // === ДАННЫЕ ПРИЕМКИ === + receivedAt DateTime? // факт приемки + receivedById String? // кто принял (FK: User) + actualQuantity Int? // принято количество + defectQuantity Int? // брак + receiptNotes String? // заметки приемки + + // === УНИКАЛЬНЫЕ ПОЛЯ ДЛЯ ТОВАРОВ === + hasRecipes Boolean @default(false) // есть ли рецептуры + totalServicesValue Decimal? @db.Decimal(12, 2) // общая стоимость услуг ФФ + + // === СВЯЗИ === + seller Organization @relation("GoodsSupplyOrdersSeller", fields: [sellerId], references: [id]) + fulfillmentCenter Organization @relation("GoodsSupplyOrdersFulfillment", fields: [fulfillmentCenterId], references: [id]) + supplier Organization? @relation("GoodsSupplyOrdersSupplier", fields: [supplierId], references: [id]) + logisticsPartner Organization? @relation("GoodsSupplyOrdersLogistics", fields: [logisticsPartnerId], references: [id]) + receivedBy User? @relation("GoodsSupplyOrdersReceiver", fields: [receivedById], references: [id]) + items GoodsSupplyItem[] + + @@map("goods_supply_orders") +} + +model GoodsSupplyItem { + id String @id @default(cuid()) + supplyOrderId String // связь с поставкой + productId String // какой товар (FK: Product) + + // === КОЛИЧЕСТВА === + requestedQuantity Int // запросили + approvedQuantity Int? // поставщик одобрил + shippedQuantity Int? // отгрузили + receivedQuantity Int? // приняли + defectQuantity Int? @default(0) // брак + + // === ЦЕНЫ (видит только селлер) === + unitPrice Decimal @db.Decimal(10, 2) // цена за единицу от поставщика + totalPrice Decimal @db.Decimal(12, 2) // общая стоимость + + // === РЕЦЕПТУРА (JSON) === + recipe Json? // полная рецептура в JSON + /* + recipe structure: + { + services: string[] // ID услуг ФФ + fulfillmentConsumables: string[] // ID расходников ФФ + sellerConsumables: string[] // ID расходников селлера + marketplaceCardId?: string // карточка товара + } + */ + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + // === СВЯЗИ === + supplyOrder GoodsSupplyOrder @relation(fields: [supplyOrderId], references: [id], onDelete: Cascade) + product Product @relation("GoodsSupplyItems", fields: [productId], references: [id]) + + @@unique([supplyOrderId, productId]) + @@map("goods_supply_items") +} +``` + +--- + +## 🛒 ТАБЛИЦЫ ПОСТАВОК НА МАРКЕТПЛЕЙСЫ + +### 4️⃣ **OzonSupplyOrder - Поставки на Ozon** + +```prisma +model OzonSupplyOrder { + // === БАЗОВЫЕ ПОЛЯ === + id String @id @default(cuid()) + status MarketplaceSupplyStatus @default(PLANNED) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + // === ДАННЫЕ ФФ (создатель) === + fulfillmentCenterId String // кто отгружает (FK: Organization) + plannedShipmentDate DateTime // план отгрузки + notes String? // заметки ФФ + + // === СПЕЦИФИЧНЫЕ ПОЛЯ OZON === + ozonWarehouseId String // склад Ozon + ozonSupplyId String? // ID поставки в системе Ozon + ozonPostingNumber String? // номер отправления Ozon + + // === ДАННЫЕ ОТГРУЗКИ === + preparedAt DateTime? // готово к отгрузке + shippedAt DateTime? // отгружено + trackingNumber String? // номер отслеживания + + // === ДАННЫЕ ПРИЕМКИ МАРКЕТПЛЕЙСОМ === + acceptedAt DateTime? // принято Ozon + acceptedQuantity Int? // принято количество + rejectedQuantity Int? // отклонено + rejectionReason String? // причина отклонения + + // === СВЯЗИ === + fulfillmentCenter Organization @relation("OzonSupplyOrders", fields: [fulfillmentCenterId], references: [id]) + items OzonSupplyItem[] + + @@map("ozon_supply_orders") +} + +model OzonSupplyItem { + id String @id @default(cuid()) + supplyOrderId String // связь с поставкой + productId String // какой готовый продукт (FK: Product) + + // === КОЛИЧЕСТВА === + plannedQuantity Int // планируется отгрузить + preparedQuantity Int? // подготовлено + shippedQuantity Int? // отгружено + acceptedQuantity Int? // принято Ozon + rejectedQuantity Int? @default(0) // отклонено + + // === OZON СПЕЦИФИЧНЫЕ ПОЛЯ === + ozonProductId String? // ID товара в Ozon + ozonSku String? // SKU в Ozon + ozonBarcode String? // штрихкод + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + // === СВЯЗИ === + supplyOrder OzonSupplyOrder @relation(fields: [supplyOrderId], references: [id], onDelete: Cascade) + product Product @relation("OzonSupplyItems", fields: [productId], references: [id]) + + @@unique([supplyOrderId, productId]) + @@map("ozon_supply_items") +} +``` + +### 5️⃣ **WildberriesSupplyOrder - Поставки на Wildberries** + +```prisma +model WildberriesSupplyOrder { + // === БАЗОВЫЕ ПОЛЯ === + id String @id @default(cuid()) + status MarketplaceSupplyStatus @default(PLANNED) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + // === ДАННЫЕ ФФ (создатель) === + fulfillmentCenterId String // кто отгружает (FK: Organization) + plannedShipmentDate DateTime // план отгрузки + notes String? // заметки ФФ + + // === СПЕЦИФИЧНЫЕ ПОЛЯ WILDBERRIES === + wbWarehouseId String // склад WB + wbSupplyId String? // ID поставки в системе WB + wbStickerId String? // ID стикера WB + + // === ДАННЫЕ ОТГРУЗКИ === + preparedAt DateTime? // готово к отгрузке + shippedAt DateTime? // отгружено + trackingNumber String? // номер отслеживания + + // === ДАННЫЕ ПРИЕМКИ МАРКЕТПЛЕЙСОМ === + acceptedAt DateTime? // принято WB + acceptedQuantity Int? // принято количество + rejectedQuantity Int? // отклонено + rejectionReason String? // причина отклонения + + // === СВЯЗИ === + fulfillmentCenter Organization @relation("WildberriesSupplyOrders", fields: [fulfillmentCenterId], references: [id]) + items WildberriesSupplyItem[] + + @@map("wildberries_supply_orders") +} + +model WildberriesSupplyItem { + id String @id @default(cuid()) + supplyOrderId String // связь с поставкой + productId String // какой готовый продукт (FK: Product) + + // === КОЛИЧЕСТВА === + plannedQuantity Int // планируется отгрузить + preparedQuantity Int? // подготовлено + shippedQuantity Int? // отгружено + acceptedQuantity Int? // принято WB + rejectedQuantity Int? @default(0) // отклонено + + // === WB СПЕЦИФИЧНЫЕ ПОЛЯ === + wbNmId String? // Номенклатура WB + wbSku String? // SKU в WB + wbBarcode String? // штрихкод + wbSize String? // размер для WB + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + // === СВЯЗИ === + supplyOrder WildberriesSupplyOrder @relation(fields: [supplyOrderId], references: [id], onDelete: Cascade) + product Product @relation("WBSupplyItems", fields: [productId], references: [id]) + + @@unique([supplyOrderId, productId]) + @@map("wildberries_supply_items") +} +``` + +--- + +## 📊 НОВЫЕ ENUMS + +### **SupplyOrderStatusV2 - Статусы поставок НА фулфилмент** + +```prisma +enum SupplyOrderStatusV2 { + PENDING // Ожидает одобрения поставщика + SUPPLIER_APPROVED // Одобрено поставщиком + LOGISTICS_CONFIRMED // Логистика подтверждена + SHIPPED // Отгружено поставщиком + IN_TRANSIT // В пути + DELIVERED // Доставлено и принято + CANCELLED // Отменено +} +``` + +### **MarketplaceSupplyStatus - Статусы поставок НА маркетплейсы** + +```prisma +enum MarketplaceSupplyStatus { + PLANNED // Запланирована + PREPARED // Подготовлена к отгрузке + SHIPPED_TO_MARKETPLACE // Отгружена на маркетплейс + ACCEPTED_BY_MARKETPLACE // Принята маркетплейсом + CANCELLED // Отменена +} +``` + +### **SellerConsumableAccessRights - Права доступа к расходникам селлеров** + +```prisma +enum SellerConsumableAccessRights { + SELLER_ONLY // Только селлер может использовать + SHARED_WITH_FF // ФФ тоже может использовать (с разрешения) +} +``` + +--- + +## 🔄 ОБНОВЛЕНИЯ СУЩЕСТВУЮЩИХ ТАБЛИЦ + +### **Product - Добавление новых связей** + +```prisma +model Product { + // ... существующие поля остаются без изменений + + // === НОВЫЕ СВЯЗИ С ПОСТАВКАМИ V2 === + fulfillmentSupplyItems FulfillmentConsumableSupplyItem[] @relation("FFSupplyItems") + sellerSupplyItems SellerConsumableSupplyItem[] @relation("SellerSupplyItems") + goodsSupplyItems GoodsSupplyItem[] @relation("GoodsSupplyItems") + ozonSupplyItems OzonSupplyItem[] @relation("OzonSupplyItems") + wildberriesSupplyItems WildberriesSupplyItem[] @relation("WBSupplyItems") + + // ... остальные поля остаются без изменений +} +``` + +### **Organization - Добавление новых связей** + +```prisma +model Organization { + // ... существующие поля остаются без изменений + + // === НОВЫЕ СВЯЗИ С ПОСТАВКАМИ V2 === + // Поставки расходников ФФ + fulfillmentSupplyOrdersAsFulfillment FulfillmentConsumableSupplyOrder[] @relation("FFSupplyOrdersFulfillment") + fulfillmentSupplyOrdersAsSupplier FulfillmentConsumableSupplyOrder[] @relation("FFSupplyOrdersSupplier") + fulfillmentSupplyOrdersAsLogistics FulfillmentConsumableSupplyOrder[] @relation("FFSupplyOrdersLogistics") + + // Поставки расходников селлеров + sellerSupplyOrdersAsSeller SellerConsumableSupplyOrder[] @relation("SellerSupplyOrdersSeller") + sellerSupplyOrdersAsFulfillment SellerConsumableSupplyOrder[] @relation("SellerSupplyOrdersFulfillment") + sellerSupplyOrdersAsSupplier SellerConsumableSupplyOrder[] @relation("SellerSupplyOrdersSupplier") + sellerSupplyOrdersAsLogistics SellerConsumableSupplyOrder[] @relation("SellerSupplyOrdersLogistics") + + // Товарные поставки + goodsSupplyOrdersAsSeller GoodsSupplyOrder[] @relation("GoodsSupplyOrdersSeller") + goodsSupplyOrdersAsFulfillment GoodsSupplyOrder[] @relation("GoodsSupplyOrdersFulfillment") + goodsSupplyOrdersAsSupplier GoodsSupplyOrder[] @relation("GoodsSupplyOrdersSupplier") + goodsSupplyOrdersAsLogistics GoodsSupplyOrder[] @relation("GoodsSupplyOrdersLogistics") + + // Поставки на маркетплейсы + ozonSupplyOrders OzonSupplyOrder[] @relation("OzonSupplyOrders") + wildberriesSupplyOrders WildberriesSupplyOrder[] @relation("WildberriesSupplyOrders") + + // ... остальные поля остаются без изменений +} +``` + +### **User - Добавление новых связей** + +```prisma +model User { + // ... существующие поля остаются без изменений + + // === НОВЫЕ СВЯЗИ С ПРИЕМКОЙ ПОСТАВОК V2 === + fulfillmentSupplyOrdersReceived FulfillmentConsumableSupplyOrder[] @relation("FFSupplyOrdersReceiver") + sellerSupplyOrdersReceived SellerConsumableSupplyOrder[] @relation("SellerSupplyOrdersReceiver") + goodsSupplyOrdersReceived GoodsSupplyOrder[] @relation("GoodsSupplyOrdersReceiver") + + // ... остальные поля остаются без изменений +} +``` + +--- + +## 🔍 ИНДЕКСЫ ДЛЯ ПРОИЗВОДИТЕЛЬНОСТИ + +```prisma +// Индексы для быстрого поиска поставок по статусу и организации +@@index([fulfillmentCenterId, status]) +@@index([sellerId, status]) +@@index([supplierId, status]) +@@index([logisticsPartnerId, status]) + +// Индексы для поиска по датам +@@index([createdAt]) +@@index([requestedDeliveryDate]) +@@index([estimatedDeliveryDate]) + +// Индексы для отслеживания +@@index([trackingNumber]) +@@index([supplierContractId]) + +// Маркетплейс-специфичные индексы +@@index([ozonSupplyId]) +@@index([ozonProductId]) +@@index([wbSupplyId]) +@@index([wbNmId]) +``` + +--- + +## 📋 ПЛАН МИГРАЦИИ + +### **Phase 1: Создание новых таблиц** +```sql +-- Создание таблиц параллельно с существующими +CREATE TABLE fulfillment_consumable_supply_orders (...); +CREATE TABLE fulfillment_consumable_supply_items (...); +-- и т.д. +``` + +### **Phase 2: Новые enum значения** +```sql +-- Добавление новых enum без затрагивания старых +CREATE TYPE "SupplyOrderStatusV2" AS ENUM (...); +CREATE TYPE "MarketplaceSupplyStatus" AS ENUM (...); +``` + +### **Phase 3: Обновление связей (без удаления старых)** +```sql +-- Добавление новых foreign key связей +ALTER TABLE "Product" ADD COLUMN ...; +ALTER TABLE "Organization" ADD COLUMN ...; +``` + +### **Phase 4: Данные миграции (только с одобрения)** +```sql +-- Миграция данных из старых таблиц в новые +-- ТОЛЬКО после полного тестирования новой системы +``` + +--- + +## ⚠️ ВАЖНЫЕ ПРИНЦИПЫ + +### ✅ **ОБРАТНАЯ СОВМЕСТИМОСТЬ** +- Все существующие таблицы остаются без изменений +- Новые таблицы создаются параллельно +- Старая система продолжает работать + +### 🔒 **БЕЗОПАСНОСТЬ ДАННЫХ** +- Каждый тип поставки изолирован в отдельной таблице +- Связи защищены foreign key constraints +- Каскадное удаление только для зависимых записей + +### 📈 **МАСШТАБИРУЕМОСТЬ** +- Легко добавлять новые типы поставок +- Оптимизированные индексы для каждого use case +- Независимые схемы для разных процессов + +### 🛡️ **ЦЕЛОСТНОСТЬ ДАННЫХ** +- Строгие ограничения на связи между таблицами +- Валидация через enum значения +- Уникальные индексы предотвращают дублирование + +**Следующий шаг:** Создание Prisma миграций для FulfillmentConsumableSupplyOrder \ No newline at end of file diff --git a/docs/development/SUPPLY_SYSTEM_IMPLEMENTATION_PLAN.md b/docs/development/SUPPLY_SYSTEM_IMPLEMENTATION_PLAN.md new file mode 100644 index 0000000..79a4106 --- /dev/null +++ b/docs/development/SUPPLY_SYSTEM_IMPLEMENTATION_PLAN.md @@ -0,0 +1,817 @@ +# 🛡️ ДЕТАЛЬНЫЙ БЕЗОПАСНЫЙ ПЛАН РЕАЛИЗАЦИИ СИСТЕМЫ ПОСТАВОК v2.0 + +> **🎯 ЦЕЛЬ:** Поэтапная миграция на новую архитектуру системы поставок без нарушения работы существующей системы + +## 🔒 ПРИНЦИПЫ БЕЗОПАСНОЙ РЕАЛИЗАЦИИ + +### ✅ **ЗОЛОТЫЕ ПРАВИЛА:** +1. **НИКОГДА НЕ ЛОМАТЬ СУЩЕСТВУЮЩИЙ ФУНКЦИОНАЛ** - старая система работает на 100% +2. **КАЖДЫЙ ЭТАП ИМЕЕТ ТОЧКУ ОТКАТА** - можно вернуться к предыдущему состоянию +3. **ТЕСТИРОВАНИЕ ПЕРЕД ПРОДАКШЕНОМ** - каждое изменение проверяется отдельно +4. **МОДУЛЬНОСТЬ** - изменения изолированы друг от друга +5. **ОДОБРЕНИЕ ПОЛЬЗОВАТЕЛЯ** - удаление старого кода ТОЛЬКО с разрешения +6. **МОНИТОРИНГ** - отслеживание работы на каждом этапе + +### 🛠️ **ИНСТРУМЕНТЫ БЕЗОПАСНОСТИ:** +- **Feature Flags** - включение/отключение новой функциональности +- **Database Migrations** - обратимые изменения схемы БД +- **Parallel Testing** - новая система тестируется параллельно со старой +- **Gradual Rollout** - постепенное включение для пользователей +- **Automated Rollback** - автоматический откат при критических ошибках + +--- + +## 📋 PHASE 1: FULFILLMENT CONSUMABLE SUPPLY ORDERS + +> **Сроки:** 2-3 недели +> **Риск:** НИЗКИЙ (новая функциональность параллельно со старой) + +### 🎯 **ЦЕЛЬ PHASE 1:** +Создать полностью рабочую систему для поставок расходников фулфилмента, которая работает параллельно со старой системой. + +--- + +## 📊 STEP 1.1: ПОДГОТОВКА ИНФРАСТРУКТУРЫ + +### **Задачи:** +1. ✅ Создание feature flag системы +2. ✅ Настройка параллельного тестирования +3. ✅ Подготовка инструментов мониторинга + +### **1.1.1 Feature Flags** +```typescript +// /src/lib/featureFlags.ts +export const FEATURE_FLAGS = { + FULFILLMENT_CONSUMABLE_SUPPLY_V2: process.env.FULFILLMENT_CONSUMABLE_V2 === 'true', + ENABLE_PARALLEL_TESTING: process.env.PARALLEL_TESTING === 'true', +} as const + +export function isFeatureEnabled(flag: keyof typeof FEATURE_FLAGS): boolean { + return FEATURE_FLAGS[flag] || false +} +``` + +### **1.1.2 Мониторинг** +```typescript +// /src/lib/monitoring/supplySystemMonitor.ts +export class SupplySystemMonitor { + static logMigrationEvent(event: string, data: any) { + console.log(`[SUPPLY_V2_MIGRATION] ${event}:`, data) + // Отправка в систему мониторинга + } + + static logError(error: Error, context: string) { + console.error(`[SUPPLY_V2_ERROR] ${context}:`, error) + // Алерты в систему мониторинга + } +} +``` + +### **1.1.3 Тестовая база данных** +```bash +# Создание отдельной БД для тестирования миграций +npm run db:create:test-migration +npm run db:migrate:test +``` + +### **⚠️ ТОЧКА ПРОВЕРКИ 1.1:** +- [ ] Feature flags работают +- [ ] Мониторинг настроен +- [ ] Тестовая БД создана +- [ ] **ОТКАТ:** Отключить feature flags + +**🔒 ROLLBACK PLAN 1.1:** +```bash +# Отключение feature flags +export FULFILLMENT_CONSUMABLE_V2=false +# Остановка мониторинга новых метрик +``` + +--- + +## 🗄️ STEP 1.2: СОЗДАНИЕ СХЕМЫ БАЗЫ ДАННЫХ + +### **Задачи:** +1. ✅ Создание новых Prisma моделей +2. ✅ Генерация и проверка миграций +3. ✅ Применение миграций в test окружении + +### **1.2.1 Prisma Schema Update** +```prisma +// Добавление в /prisma/schema.prisma +model FulfillmentConsumableSupplyOrder { + // ... полная схема из DATABASE_SCHEMA_V2.md +} + +model FulfillmentConsumableSupplyItem { + // ... полная схема из DATABASE_SCHEMA_V2.md +} + +enum SupplyOrderStatusV2 { + PENDING + SUPPLIER_APPROVED + LOGISTICS_CONFIRMED + SHIPPED + IN_TRANSIT + DELIVERED + CANCELLED +} +``` + +### **1.2.2 Создание миграции** +```bash +# Генерация миграции +npx prisma migrate dev --name "add_fulfillment_consumable_supply_v2" + +# Проверка SQL миграции ПЕРЕД применением +cat prisma/migrations/xxx_add_fulfillment_consumable_supply_v2/migration.sql +``` + +### **1.2.3 Тестирование миграции** +```bash +# Применение в тестовой БД +npx prisma migrate deploy --schema=./prisma/schema.test.prisma + +# Проверка создания таблиц +npx prisma studio --schema=./prisma/schema.test.prisma +``` + +### **⚠️ ТОЧКА ПРОВЕРКИ 1.2:** +- [ ] Миграция создана успешно +- [ ] SQL миграции проверен вручную +- [ ] Таблицы созданы в тестовой БД +- [ ] Все связи (FK) работают корректно +- [ ] **ОТКАТ:** Revert migration + +**🔒 ROLLBACK PLAN 1.2:** +```bash +# Откат миграции +npx prisma migrate reset --force --skip-seed +# ИЛИ ручной DROP TABLE если нужно +``` + +--- + +## ⚡ STEP 1.3: GRAPHQL API + +### **Задачи:** +1. ✅ Создание GraphQL типов +2. ✅ Создание resolvers с feature flag +3. ✅ Тестирование API отдельно + +### **1.3.1 GraphQL Types** +```typescript +// /src/graphql/typedefs.ts - ДОБАВИТЬ К СУЩЕСТВУЮЩИМ +const fulfillmentConsumableTypes = ` + type FulfillmentConsumableSupplyOrder { + id: ID! + status: SupplyOrderStatusV2! + fulfillmentCenterId: ID! + supplierId: ID + requestedDeliveryDate: DateTime! + resalePricePerUnit: Float + # ... все остальные поля + items: [FulfillmentConsumableSupplyItem!]! + } + + type FulfillmentConsumableSupplyItem { + id: ID! + productId: ID! + requestedQuantity: Int! + unitPrice: Float! + totalPrice: Float! + product: Product! + } + + enum SupplyOrderStatusV2 { + PENDING + SUPPLIER_APPROVED + LOGISTICS_CONFIRMED + SHIPPED + IN_TRANSIT + DELIVERED + CANCELLED + } + + input CreateFulfillmentConsumableSupplyInput { + supplierId: ID! + requestedDeliveryDate: DateTime! + items: [FulfillmentConsumableSupplyItemInput!]! + notes: String + } + + input FulfillmentConsumableSupplyItemInput { + productId: ID! + requestedQuantity: Int! + } + + type CreateFulfillmentConsumableSupplyResult { + success: Boolean! + message: String! + supplyOrder: FulfillmentConsumableSupplyOrder + } + + extend type Query { + # Новые запросы с feature flag + myFulfillmentConsumableSupplies: [FulfillmentConsumableSupplyOrder!]! + fulfillmentConsumableSupply(id: ID!): FulfillmentConsumableSupplyOrder + } + + extend type Mutation { + # Новые мутации с feature flag + createFulfillmentConsumableSupply( + input: CreateFulfillmentConsumableSupplyInput! + ): CreateFulfillmentConsumableSupplyResult! + } +` +``` + +### **1.3.2 Resolvers с Feature Flags** +```typescript +// /src/graphql/resolvers.ts - ДОБАВИТЬ К СУЩЕСТВУЮЩИМ +const fulfillmentConsumableResolvers = { + Query: { + myFulfillmentConsumableSupplies: async (_: unknown, __: unknown, context: Context) => { + // 🚨 FEATURE FLAG ПРОВЕРКА + if (!isFeatureEnabled('FULFILLMENT_CONSUMABLE_SUPPLY_V2')) { + throw new GraphQLError('Feature not enabled') + } + + SupplySystemMonitor.logMigrationEvent('API_CALL', { + operation: 'myFulfillmentConsumableSupplies', + userId: context.user?.id + }) + + try { + // Полная реализация с системой безопасности + // ... код реализации + } catch (error) { + SupplySystemMonitor.logError(error, 'myFulfillmentConsumableSupplies') + throw error + } + }, + + fulfillmentConsumableSupply: async (_: unknown, args: { id: string }, context: Context) => { + if (!isFeatureEnabled('FULFILLMENT_CONSUMABLE_SUPPLY_V2')) { + throw new GraphQLError('Feature not enabled') + } + // ... реализация + } + }, + + Mutation: { + createFulfillmentConsumableSupply: async (_: unknown, args: any, context: Context) => { + if (!isFeatureEnabled('FULFILLMENT_CONSUMABLE_SUPPLY_V2')) { + return { success: false, message: 'Feature not enabled' } + } + + SupplySystemMonitor.logMigrationEvent('CREATE_SUPPLY', { + supplierId: args.input.supplierId, + itemsCount: args.input.items.length, + userId: context.user?.id + }) + + try { + // Полная реализация создания поставки + // ... код реализации + + return { success: true, message: 'Supply created', supplyOrder: result } + } catch (error) { + SupplySystemMonitor.logError(error, 'createFulfillmentConsumableSupply') + return { success: false, message: error.message } + } + } + } +} +``` + +### **1.3.3 API Testing** +```typescript +// /tests/api/fulfillmentConsumableSupply.test.ts +describe('FulfillmentConsumableSupply API', () => { + beforeAll(() => { + // Включаем feature flag для тестов + process.env.FULFILLMENT_CONSUMABLE_V2 = 'true' + }) + + test('should create supply order', async () => { + const mutation = ` + mutation CreateFulfillmentConsumableSupply($input: CreateFulfillmentConsumableSupplyInput!) { + createFulfillmentConsumableSupply(input: $input) { + success + message + supplyOrder { + id + status + } + } + } + ` + // ... тестирование + }) + + test('should not work when feature disabled', async () => { + process.env.FULFILLMENT_CONSUMABLE_V2 = 'false' + // Проверка что API недоступно + }) +}) +``` + +### **⚠️ ТОЧКА ПРОВЕРКИ 1.3:** +- [ ] GraphQL типы корректны +- [ ] Resolvers работают с feature flag +- [ ] API тесты проходят +- [ ] Feature flag блокирует доступ когда выключен +- [ ] **ОТКАТ:** Отключить feature flag + +**🔒 ROLLBACK PLAN 1.3:** +```bash +# Отключение API через feature flag +export FULFILLMENT_CONSUMABLE_V2=false +# API станет недоступно мгновенно +``` + +--- + +## 🎨 STEP 1.4: FRONTEND ИНТЕРФЕЙС + +### **Задачи:** +1. ✅ Создание формы создания поставки +2. ✅ Создание страницы просмотра поставок +3. ✅ Интеграция с существующим интерфейсом + +### **1.4.1 Feature Flag в Frontend** +```typescript +// /src/lib/featureFlags.client.ts +export function useFeatureFlag(flag: string) { + return process.env.NEXT_PUBLIC_FULFILLMENT_CONSUMABLE_V2 === 'true' +} +``` + +### **1.4.2 Форма создания (с feature flag)** +```typescript +// /src/components/fulfillment-supplies/create-consumables-v2.tsx +export function CreateConsumablesV2Page() { + const featureEnabled = useFeatureFlag('FULFILLMENT_CONSUMABLE_V2') + + if (!featureEnabled) { + // Показываем старую версию или заглушку + return + } + + // Новая реализация + return ( +
+

Создать поставку расходников ФФ (v2.0)

+ {/* Полная форма */} +
+ ) +} +``` + +### **1.4.3 Список поставок (с feature flag)** +```typescript +// /src/components/fulfillment-supplies/ff-consumables-v2.tsx +export function FFConsumablesV2Tab() { + const featureEnabled = useFeatureFlag('FULFILLMENT_CONSUMABLE_V2') + + if (!featureEnabled) { + return + } + + const { data, loading, error } = useQuery(GET_MY_FULFILLMENT_CONSUMABLE_SUPPLIES) + + // Новая реализация отображения +} +``` + +### **1.4.4 Интеграция в роутинг** +```typescript +// /src/app/fulfillment-supplies/ff-consumables/page.tsx +export default function FFConsumablesPage() { + const featureEnabled = useFeatureFlag('FULFILLMENT_CONSUMABLE_V2') + + return featureEnabled ? : +} +``` + +### **⚠️ ТОЧКА ПРОВЕРКИ 1.4:** +- [ ] Форма создания работает +- [ ] Список поставок отображается +- [ ] Feature flag переключает версии +- [ ] Нет поломок в старом интерфейсе +- [ ] **ОТКАТ:** Отключить frontend feature flag + +**🔒 ROLLBACK PLAN 1.4:** +```bash +# Отключение новых компонентов +export NEXT_PUBLIC_FULFILLMENT_CONSUMABLE_V2=false +# Пользователи увидят старый интерфейс +``` + +--- + +## 🔗 STEP 1.5: ИНТЕГРАЦИЯ И END-TO-END ТЕСТИРОВАНИЕ + +### **Задачи:** +1. ✅ Полное тестирование workflow поставки +2. ✅ Тестирование безопасности доступа +3. ✅ Нагрузочное тестирование +4. ✅ Проверка совместимости со старой системой + +### **1.5.1 End-to-End тесты** +```typescript +// /tests/e2e/fulfillmentConsumableSupply.e2e.ts +describe('Fulfillment Consumable Supply E2E', () => { + test('Complete supply workflow', async () => { + // 1. ФФ создает поставку + // 2. Поставщик одобряет + // 3. Логистика назначается + // 4. Отгрузка + // 5. Приемка + // Проверяем каждый этап + }) + + test('Security access control', async () => { + // Проверяем что селлеры не видят поставки ФФ + // Проверяем фильтрацию данных по ролям + }) +}) +``` + +### **1.5.2 Параллельное тестирование** +```typescript +// /tests/parallel/oldVsNewSystem.test.ts +describe('Old vs New System Compatibility', () => { + test('Both systems work independently', async () => { + // Создаем поставку в старой системе + // Создаем поставку в новой системе + // Проверяем что они не мешают друг другу + }) +}) +``` + +### **1.5.3 Performance Testing** +```bash +# Нагрузочное тестирование API +npm run test:load -- --scenario=fulfillment-consumable-v2 +``` + +### **⚠️ ТОЧКА ПРОВЕРКИ 1.5:** +- [ ] E2E тесты проходят +- [ ] Система безопасности работает +- [ ] Производительность приемлемая +- [ ] Старая система не затронута +- [ ] **ОТКАТ:** Полное отключение feature flags + +**🔒 ROLLBACK PLAN 1.5:** +```bash +# Полное отключение новой системы +export FULFILLMENT_CONSUMABLE_V2=false +export NEXT_PUBLIC_FULFILLMENT_CONSUMABLE_V2=false +npm run restart +``` + +--- + +## 🚀 STEP 1.6: PRODUCTION DEPLOYMENT + +### **Задачи:** +1. ✅ Постепенное включение для пользователей +2. ✅ Мониторинг в реальном времени +3. ✅ Готовность к быстрому откату + +### **1.6.1 Gradual Rollout** +```typescript +// Включение для 10% пользователей +export function shouldUseV2ForUser(userId: string): boolean { + if (!isFeatureEnabled('FULFILLMENT_CONSUMABLE_V2')) return false + + // Хэш от userId, включаем для 10% + const hash = hashCode(userId) + return (hash % 10) === 0 +} +``` + +### **1.6.2 Real-time мониторинг** +```typescript +// Метрики в реальном времени +export const supplyV2Metrics = { + createSuccess: 0, + createError: 0, + querySuccess: 0, + queryError: 0, + rollbackTriggered: false +} + +// Автоматический откат при критических ошибках +if (supplyV2Metrics.createError > 10) { + // Экстренное отключение + process.env.FULFILLMENT_CONSUMABLE_V2 = 'false' + supplyV2Metrics.rollbackTriggered = true +} +``` + +### **1.6.3 Production Checklist** +```markdown +## PRODUCTION DEPLOYMENT CHECKLIST +- [ ] Database migration applied successfully +- [ ] Feature flags configured +- [ ] Monitoring dashboards active +- [ ] Rollback procedures tested +- [ ] Support team notified +- [ ] Documentation updated +- [ ] Gradual rollout configured (10% users) +``` + +### **⚠️ ТОЧКА ПРОВЕРКИ 1.6:** +- [ ] Deployment прошел успешно +- [ ] 10% пользователей используют новую систему +- [ ] Метрики показывают стабильность +- [ ] Нет критических ошибок +- [ ] **ОТКАТ:** Экстренное отключение + +**🔒 ROLLBACK PLAN 1.6:** +```bash +# ЭКСТРЕННЫЙ ОТКАТ +kubectl set env deployment/app FULFILLMENT_CONSUMABLE_V2=false +# ИЛИ через admin panel +curl -X POST /admin/feature-flags/disable/FULFILLMENT_CONSUMABLE_V2 +``` + +--- + +## 📊 STEP 1.7: МОНИТОРИНГ И СТАБИЛИЗАЦИЯ + +### **Задачи:** +1. ✅ Анализ метрик производительности +2. ✅ Сбор фидбека пользователей +3. ✅ Исправление найденных проблем +4. ✅ Подготовка к Phase 2 + +### **1.7.1 Мониторинг (1-2 недели)** +```typescript +// Ключевые метрики для отслеживания +const metricsToWatch = { + // Функциональные + supplyCreationRate: 'число создаваемых поставок/день', + successRate: 'процент успешных операций', + averageProcessingTime: 'среднее время обработки', + + // Технические + databasePerformance: 'время ответа БД', + apiLatency: 'задержка API', + errorRate: 'процент ошибок', + + // Бизнесовые + userAdoption: 'процент пользователей использующих v2', + userSatisfaction: 'оценка пользователей', + supportTickets: 'количество тикетов поддержки' +} +``` + +### **1.7.2 Feedback Collection** +```typescript +// Встроенная система сбора фидбека +export function FeedbackWidget() { + return ( +
+

Как вам новая система поставок?

+ + +
+ ) +} +``` + +### **1.7.3 Анализ и улучшения** +```markdown +## КРИТЕРИИ УСПЕХА PHASE 1: +✅ Успешность операций > 98% +✅ Время отклика < 2 сек +✅ Пользовательская оценка > 4/5 +✅ Количество багов < 5 критических +✅ Готовность инфраструктуры к Phase 2 +``` + +### **⚠️ ТОЧКА ПРОВЕРКИ 1.7:** +- [ ] Метрики в пределах нормы +- [ ] Пользователи довольны +- [ ] Критические баги исправлены +- [ ] Система готова к расширению +- [ ] **РЕШЕНИЕ:** Переход к Phase 2 или доработка + +--- + +## 🎯 КРИТЕРИИ ГОТОВНОСТИ К PHASE 2 + +### ✅ **ОБЯЗАТЕЛЬНЫЕ УСЛОВИЯ:** +1. **Функциональность:** Все features Phase 1 работают стабильно +2. **Производительность:** Нет деградации по сравнению со старой системой +3. **Безопасность:** Нет утечек данных, доступ контролируется корректно +4. **Пользователи:** Положительные отзывы от 80%+ пользователей +5. **Мониторинг:** Налаженная система отслеживания метрик + +### 📈 **ЧИСЛЕННЫЕ ПОКАЗАТЕЛИ:** +- Успешность создания поставок: **> 98%** +- Время загрузки списка поставок: **< 2 сек** +- Время создания поставки: **< 5 сек** +- Uptime системы: **> 99.5%** +- Количество критических багов: **= 0** + +--- + +## 📋 PHASE 2: SELLER CONSUMABLE SUPPLY ORDERS + +> **Сроки:** 2-3 недели после стабилизации Phase 1 +> **Риск:** СРЕДНИЙ (более сложная логика с хранением) + +### **Ключевые особенности Phase 2:** +1. **Мульти-организационность:** Селлер создает, ФФ хранит +2. **Права доступа:** Сложная система доступа к данным +3. **Сроки хранения:** Временные ограничения на хранение +4. **Интеграция с Phase 1:** Должна работать вместе с поставками ФФ + +### **Plan Phase 2:** +- Step 2.1: Database Schema для селлерских расходников +- Step 2.2: GraphQL API с правами доступа +- Step 2.3: Frontend для селлеров и ФФ +- Step 2.4: Интеграция с системой хранения +- Step 2.5: Тестирование совместимости с Phase 1 + +--- + +## 📋 PHASE 3: GOODS SUPPLY ORDERS + +> **Сроки:** 3-4 недели после стабилизации Phase 2 +> **Риск:** ВЫСОКИЙ (самая сложная система с рецептурами) + +### **Ключевые особенности Phase 3:** +1. **Рецептуры:** Сложная JSON структура с услугами +2. **Мульти-компонентность:** Товары + услуги + расходники +3. **Marketplace интеграция:** Связь с карточками товаров +4. **Миграция данных:** Перенос существующих товарных поставок + +--- + +## 📋 PHASE 4: ОЧИСТКА И ОПТИМИЗАЦИЯ + +> **Сроки:** 1-2 недели +> **Риск:** НИЗКИЙ (только после полного одобрения) + +### **Задачи Phase 4:** +1. **Миграция старых данных** (ТОЛЬКО с одобрения) +2. **Удаление устаревшего кода** (ТОЛЬКО с одобрения) +3. **Оптимизация производительности** +4. **Финальное тестирование** + +--- + +## 🚨 EMERGENCY PROCEDURES + +### **🔴 CRITICAL ROLLBACK (при критических ошибках)** +```bash +#!/bin/bash +# emergency-rollback.sh + +echo "🚨 EMERGENCY ROLLBACK INITIATED" + +# Отключение всех feature flags +export FULFILLMENT_CONSUMABLE_V2=false +export SELLER_CONSUMABLE_V2=false +export GOODS_SUPPLY_V2=false +export NEXT_PUBLIC_FULFILLMENT_CONSUMABLE_V2=false + +# Перезапуск сервисов +kubectl rollout restart deployment/app +kubectl rollout restart deployment/api + +# Уведомления +curl -X POST "$SLACK_WEBHOOK" -d '{"text": "🚨 Supply System V2 Emergency Rollback Executed"}' + +echo "✅ Rollback completed. System reverted to V1." +``` + +### **🟡 PARTIAL ROLLBACK (при локальных проблемах)** +```bash +# Откат конкретной фазы +rollback_phase_1() { + export FULFILLMENT_CONSUMABLE_V2=false + kubectl set env deployment/app FULFILLMENT_CONSUMABLE_V2=false +} + +rollback_phase_2() { + export SELLER_CONSUMABLE_V2=false + kubectl set env deployment/app SELLER_CONSUMABLE_V2=false +} +``` + +### **📊 MONITORING ALERTS** +```typescript +// Автоматические алерты +const ALERT_THRESHOLDS = { + ERROR_RATE: 5, // > 5% ошибок = алерт + RESPONSE_TIME: 5000, // > 5 сек = алерт + FAILED_CREATES: 10, // > 10 неудачных создания = алерт + DATABASE_ERRORS: 3 // > 3 ошибки БД = критический алерт +} + +// Автоматическое отключение при превышении порогов +if (currentMetrics.errorRate > ALERT_THRESHOLDS.ERROR_RATE) { + triggerEmergencyRollback('HIGH_ERROR_RATE') +} +``` + +--- + +## 📈 SUCCESS METRICS + +### **📊 KPI для каждой фазы:** + +#### **Phase 1 Success Criteria:** +- ✅ 100% функций поставок расходников ФФ работают +- ✅ 0 критических багов в production +- ✅ < 2 сек время отклика API +- ✅ > 90% положительных отзывов пользователей +- ✅ 0 инцидентов безопасности + +#### **Phase 2 Success Criteria:** +- ✅ Поставки расходников селлеров работают корректно +- ✅ Права доступа работают (селлер видит свое, ФФ видит ограниченно) +- ✅ Интеграция с Phase 1 без конфликтов + +#### **Phase 3 Success Criteria:** +- ✅ Товарные поставки с рецептурами работают +- ✅ Миграция существующих данных (если одобрена) +- ✅ Интеграция с маркетплейсами + +#### **Overall Success Criteria:** +- ✅ **Производительность:** Не хуже старой системы +- ✅ **Стабильность:** 99.9% uptime +- ✅ **Безопасность:** 0 утечек данных +- ✅ **Пользователи:** > 85% satisfaction rate +- ✅ **Бизнес:** Увеличение эффективности процессов поставок + +--- + +## ⏰ TIMELINE + +```mermaid +gantt + title Supply System V2 Implementation Timeline + dateFormat YYYY-MM-DD + section Phase 1 + Infrastructure Setup :2024-08-25, 3d + Database Schema :2024-08-28, 2d + GraphQL API :2024-08-30, 4d + Frontend Interface :2024-09-03, 4d + Integration Testing :2024-09-07, 3d + Production Deployment :2024-09-10, 2d + Monitoring & Stabilization :2024-09-12, 7d + + section Phase 2 + Seller Consumables Schema :2024-09-19, 3d + Complex Access Control :2024-09-22, 5d + Multi-org Frontend :2024-09-27, 4d + Integration Testing :2024-10-01, 4d + + section Phase 3 + Goods Supply Schema :2024-10-05, 4d + Recipe System :2024-10-09, 6d + Data Migration Prep :2024-10-15, 3d + Testing & Validation :2024-10-18, 5d + + section Phase 4 + Data Migration :2024-10-23, 3d + Code Cleanup :2024-10-26, 2d + Final Testing :2024-10-28, 2d +``` + +### **🎯 MILESTONE DATES:** +- **Phase 1 Complete:** September 19, 2024 +- **Phase 2 Complete:** October 4, 2024 +- **Phase 3 Complete:** October 23, 2024 +- **Full Migration Complete:** October 30, 2024 + +--- + +## ✅ ЗАКЛЮЧЕНИЕ + +Данный план обеспечивает: + +### 🛡️ **МАКСИМАЛЬНУЮ БЕЗОПАСНОСТЬ:** +- Поэтапное внедрение без риска для работающей системы +- Множественные точки отката на каждом этапе +- Тщательное тестирование перед каждым шагом + +### 📈 **КОНТРОЛИРУЕМЫЙ РОСТ:** +- Постепенное увеличение сложности (ФФ → Селлер → Товары) +- Feature flags для контроля доступа +- Мониторинг метрик на каждом этапе + +### 🔧 **ГИБКОСТЬ РЕАЛИЗАЦИИ:** +- Возможность приостановить на любом этапе +- Адаптация планов по результатам предыдущих фаз +- Учет пожеланий пользователей + +**Система готова к безопасной поэтапной реализации!** \ No newline at end of file diff --git a/development-diary.md b/legacy-rules/development-diary.md similarity index 100% rename from development-diary.md rename to legacy-rules/development-diary.md diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 4e81562..b449e14 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -23,6 +23,9 @@ model User { sentMessages Message[] @relation("SentMessages") smsCodes SmsCode[] organization Organization? @relation(fields: [organizationId], references: [id]) + + // === НОВЫЕ СВЯЗИ С ПРИЕМКОЙ ПОСТАВОК V2 === + fulfillmentSupplyOrdersReceived FulfillmentConsumableSupplyOrder[] @relation("FFSupplyOrdersReceiver") @@map("users") } @@ -120,6 +123,12 @@ model Organization { users User[] wbWarehouseCaches WBWarehouseCache[] @relation("WBWarehouseCaches") wildberriesSupplies WildberriesSupply[] + + // === НОВЫЕ СВЯЗИ С ПОСТАВКАМИ V2 === + // Поставки расходников ФФ + fulfillmentSupplyOrdersAsFulfillment FulfillmentConsumableSupplyOrder[] @relation("FFSupplyOrdersFulfillment") + fulfillmentSupplyOrdersAsSupplier FulfillmentConsumableSupplyOrder[] @relation("FFSupplyOrdersSupplier") + fulfillmentSupplyOrdersAsLogistics FulfillmentConsumableSupplyOrder[] @relation("FFSupplyOrdersLogistics") @@index([referralCode]) @@index([referredById]) @@ -283,6 +292,9 @@ model Product { category Category? @relation(fields: [categoryId], references: [id]) organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) supplyOrderItems SupplyOrderItem[] + + // === НОВЫЕ СВЯЗИ С ПОСТАВКАМИ V2 === + fulfillmentSupplyItems FulfillmentConsumableSupplyItem[] @relation("FFSupplyItems") @@unique([organizationId, article]) @@map("products") @@ -732,3 +744,96 @@ enum SecurityAlertSeverity { HIGH CRITICAL } + +// =============================================== +// НОВАЯ СИСТЕМА ПОСТАВОК V2.0 +// =============================================== + +// Новый enum для статусов поставок v2 +enum SupplyOrderStatusV2 { + PENDING // Ожидает одобрения поставщика + SUPPLIER_APPROVED // Одобрено поставщиком + LOGISTICS_CONFIRMED // Логистика подтверждена + SHIPPED // Отгружено поставщиком + IN_TRANSIT // В пути + DELIVERED // Доставлено и принято + CANCELLED // Отменено +} + +// Модель для поставок расходников фулфилмента +model FulfillmentConsumableSupplyOrder { + // === БАЗОВЫЕ ПОЛЯ === + id String @id @default(cuid()) + status SupplyOrderStatusV2 @default(PENDING) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + // === ДАННЫЕ ФФ (создатель) === + fulfillmentCenterId String // кто заказывает (FK: Organization) + requestedDeliveryDate DateTime // когда нужно + resalePricePerUnit Decimal? @db.Decimal(10, 2) // цена продажи селлерам + minStockLevel Int? // минимальный остаток + notes String? // заметки ФФ + + // === ДАННЫЕ ПОСТАВЩИКА === + supplierId String? // кто поставляет (FK: Organization) + supplierApprovedAt DateTime? // когда одобрил + packagesCount Int? // количество грузомест + estimatedVolume Decimal? @db.Decimal(8, 3) // объем груза в м³ + supplierContractId String? // номер договора + supplierNotes String? // заметки поставщика + + // === ДАННЫЕ ЛОГИСТИКИ === + logisticsPartnerId String? // кто везет (FK: Organization) + estimatedDeliveryDate DateTime? // план доставки + routeId String? // маршрут (FK: LogisticsRoute) + logisticsCost Decimal? @db.Decimal(10, 2) // стоимость доставки + logisticsNotes String? // заметки логистики + + // === ДАННЫЕ ОТГРУЗКИ === + shippedAt DateTime? // факт отгрузки + trackingNumber String? // номер отслеживания + + // === ДАННЫЕ ПРИЕМКИ === + receivedAt DateTime? // факт приемки + receivedById String? // кто принял (FK: User) + actualQuantity Int? // принято количество + defectQuantity Int? // брак + receiptNotes String? // заметки приемки + + // === СВЯЗИ === + fulfillmentCenter Organization @relation("FFSupplyOrdersFulfillment", fields: [fulfillmentCenterId], references: [id]) + supplier Organization? @relation("FFSupplyOrdersSupplier", fields: [supplierId], references: [id]) + logisticsPartner Organization? @relation("FFSupplyOrdersLogistics", fields: [logisticsPartnerId], references: [id]) + receivedBy User? @relation("FFSupplyOrdersReceiver", fields: [receivedById], references: [id]) + items FulfillmentConsumableSupplyItem[] + + @@map("fulfillment_consumable_supply_orders") +} + +model FulfillmentConsumableSupplyItem { + id String @id @default(cuid()) + supplyOrderId String // связь с поставкой + productId String // какой расходник (FK: Product) + + // === КОЛИЧЕСТВА === + requestedQuantity Int // запросили + approvedQuantity Int? // поставщик одобрил + shippedQuantity Int? // отгрузили + receivedQuantity Int? // приняли + defectQuantity Int? @default(0) // брак + + // === ЦЕНЫ === + unitPrice Decimal @db.Decimal(10, 2) // цена за единицу от поставщика + totalPrice Decimal @db.Decimal(12, 2) // общая стоимость + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + // === СВЯЗИ === + supplyOrder FulfillmentConsumableSupplyOrder @relation(fields: [supplyOrderId], references: [id], onDelete: Cascade) + product Product @relation("FFSupplyItems", fields: [productId], references: [id]) + + @@unique([supplyOrderId, productId]) + @@map("fulfillment_consumable_supply_items") +} diff --git a/src/app/create-fulfillment-consumables-v2/page.tsx b/src/app/create-fulfillment-consumables-v2/page.tsx new file mode 100644 index 0000000..2529aa1 --- /dev/null +++ b/src/app/create-fulfillment-consumables-v2/page.tsx @@ -0,0 +1,5 @@ +import CreateFulfillmentConsumablesSupplyV2Page from '@/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2' + +export default function Page() { + return +} \ No newline at end of file diff --git a/src/app/fulfillment-supplies/consumables/page.tsx b/src/app/fulfillment-supplies/consumables/page.tsx new file mode 100644 index 0000000..917d836 --- /dev/null +++ b/src/app/fulfillment-supplies/consumables/page.tsx @@ -0,0 +1,9 @@ +import { FulfillmentConsumablesOrdersTab } from '@/components/fulfillment-supplies/fulfillment-supplies/fulfillment-consumables-orders-tab' + +export default function ConsumablesPage() { + return ( +
+ +
+ ) +} \ No newline at end of file diff --git a/src/app/fulfillment-supplies/detailed-supplies/page.tsx b/src/app/fulfillment-supplies/detailed-supplies/page.tsx new file mode 100644 index 0000000..3205375 --- /dev/null +++ b/src/app/fulfillment-supplies/detailed-supplies/page.tsx @@ -0,0 +1,9 @@ +import { FulfillmentDetailedSuppliesTab } from '@/components/fulfillment-supplies/fulfillment-supplies/fulfillment-detailed-supplies-tab' + +export default function DetailedSuppliesPage() { + return ( +
+ +
+ ) +} \ No newline at end of file diff --git a/src/app/fulfillment-supplies/goods/new/page.tsx b/src/app/fulfillment-supplies/goods/new/page.tsx new file mode 100644 index 0000000..d6154da --- /dev/null +++ b/src/app/fulfillment-supplies/goods/new/page.tsx @@ -0,0 +1,10 @@ +import { FulfillmentGoodsOrdersTab } from '@/components/fulfillment-supplies/fulfillment-supplies/fulfillment-goods-orders-tab' + +export default function GoodsNewPage() { + return ( +
+

Новые товары

+ +
+ ) +} \ No newline at end of file diff --git a/src/app/fulfillment-supplies/goods/received/page.tsx b/src/app/fulfillment-supplies/goods/received/page.tsx new file mode 100644 index 0000000..138543c --- /dev/null +++ b/src/app/fulfillment-supplies/goods/received/page.tsx @@ -0,0 +1,8 @@ +export default function GoodsReceivedPage() { + return ( +
+

Принятые товары

+
Здесь отображаются принятые на склад товары
+
+ ) +} \ No newline at end of file diff --git a/src/app/fulfillment-supplies/goods/receiving/page.tsx b/src/app/fulfillment-supplies/goods/receiving/page.tsx new file mode 100644 index 0000000..6fece66 --- /dev/null +++ b/src/app/fulfillment-supplies/goods/receiving/page.tsx @@ -0,0 +1,8 @@ +export default function GoodsReceivingPage() { + return ( +
+

Товары в приёмке

+
Здесь отображаются товары в процессе приёмки на склад фулфилмента
+
+ ) +} \ No newline at end of file diff --git a/src/app/fulfillment-supplies/layout.tsx b/src/app/fulfillment-supplies/layout.tsx new file mode 100644 index 0000000..fbdfb84 --- /dev/null +++ b/src/app/fulfillment-supplies/layout.tsx @@ -0,0 +1,16 @@ +import { AuthGuard } from '@/components/auth-guard' +import { FulfillmentSuppliesLayout } from '@/components/fulfillment-supplies/fulfillment-supplies-layout' + +export default function FulfillmentSuppliesLayoutPage({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + {children} + + + ) +} \ No newline at end of file diff --git a/src/app/fulfillment-supplies/page.tsx b/src/app/fulfillment-supplies/page.tsx index 81fa800..aaf6117 100644 --- a/src/app/fulfillment-supplies/page.tsx +++ b/src/app/fulfillment-supplies/page.tsx @@ -1,10 +1,6 @@ -import { AuthGuard } from '@/components/auth-guard' -import { FulfillmentSuppliesDashboard } from '@/components/fulfillment-supplies/fulfillment-supplies-dashboard' +import { redirect } from 'next/navigation' export default function FulfillmentSuppliesPage() { - return ( - - - - ) + // Редирект на дефолтный таб - товары новые + redirect('/fulfillment-supplies/goods/new') } diff --git a/src/app/fulfillment-supplies/returns/page.tsx b/src/app/fulfillment-supplies/returns/page.tsx new file mode 100644 index 0000000..2bd1ea6 --- /dev/null +++ b/src/app/fulfillment-supplies/returns/page.tsx @@ -0,0 +1,9 @@ +import { PvzReturnsTab } from '@/components/fulfillment-supplies/fulfillment-supplies/pvz-returns-tab' + +export default function ReturnsPage() { + return ( +
+ +
+ ) +} \ No newline at end of file diff --git a/src/app/supplies/create-fulfillment-consumables-v2/page.tsx b/src/app/supplies/create-fulfillment-consumables-v2/page.tsx new file mode 100644 index 0000000..2529aa1 --- /dev/null +++ b/src/app/supplies/create-fulfillment-consumables-v2/page.tsx @@ -0,0 +1,5 @@ +import CreateFulfillmentConsumablesSupplyV2Page from '@/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2' + +export default function Page() { + return +} \ No newline at end of file diff --git a/src/components/dashboard/sidebar.tsx b/src/components/dashboard/sidebar.tsx index bd2e485..58b62a8 100644 --- a/src/components/dashboard/sidebar.tsx +++ b/src/components/dashboard/sidebar.tsx @@ -218,7 +218,7 @@ export function Sidebar({ isRootInstance = false }: { isRootInstance?: boolean } // Для каждого типа кабинета свой роут switch (user?.organization?.type) { case 'FULFILLMENT': - router.push('/fulfillment-supplies') + router.push('/fulfillment-supplies/goods/new') break case 'SELLER': router.push('/supplies') diff --git a/src/components/fulfillment-supplies/create-fulfillment-consumables-supply-page.tsx b/src/components/fulfillment-supplies/create-fulfillment-consumables-supply-page.tsx index 579396a..4697220 100644 --- a/src/components/fulfillment-supplies/create-fulfillment-consumables-supply-page.tsx +++ b/src/components/fulfillment-supplies/create-fulfillment-consumables-supply-page.tsx @@ -52,6 +52,8 @@ interface FulfillmentConsumableProduct { } stock?: number unit?: string + quantity?: number + ordered?: number } interface SelectedFulfillmentConsumable { diff --git a/src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2.tsx b/src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2.tsx new file mode 100644 index 0000000..01b47a2 --- /dev/null +++ b/src/components/fulfillment-supplies/create-fulfillment-consumables-supply-v2.tsx @@ -0,0 +1,821 @@ +'use client' + +import { useQuery, useMutation } from '@apollo/client' +import { ArrowLeft, Building2, Search, Package, Plus, Minus, ShoppingCart, Wrench } from 'lucide-react' +import Image from 'next/image' +import { useRouter } from 'next/navigation' +import React, { useState, useMemo } from 'react' +import { toast } from 'sonner' + +import { Sidebar } from '@/components/dashboard/sidebar' +import { OrganizationAvatar } from '@/components/market/organization-avatar' +import { Badge } from '@/components/ui/badge' +import { Button } from '@/components/ui/button' +import { Card } from '@/components/ui/card' +import { Input } from '@/components/ui/input' +import { CREATE_FULFILLMENT_CONSUMABLE_SUPPLY, GET_MY_FULFILLMENT_CONSUMABLE_SUPPLIES } from '@/graphql/queries/fulfillment-consumables-v2' +import { + GET_MY_COUNTERPARTIES, + GET_ORGANIZATION_PRODUCTS, +} from '@/graphql/queries' +import { useAuth } from '@/hooks/useAuth' +import { useSidebar } from '@/hooks/useSidebar' + +interface FulfillmentConsumableSupplier { + id: string + inn: string + name?: string + fullName?: string + type: 'FULFILLMENT' | 'SELLER' | 'LOGIST' | 'WHOLESALE' + address?: string + phones?: Array<{ value: string }> + emails?: Array<{ value: string }> + users?: Array<{ id: string; avatar?: string; managerName?: string }> + createdAt: string +} + +interface FulfillmentConsumableProduct { + id: string + name: string + description?: string + price: number + type?: 'PRODUCT' | 'CONSUMABLE' + category?: { name: string } + images: string[] + mainImage?: string + organization: { + id: string + name: string + } + stock?: number + unit?: string + quantity?: number + ordered?: number +} + +interface SelectedFulfillmentConsumable { + id: string + name: string + price: number + selectedQuantity: number + unit?: string + category?: string + supplierId: string + supplierName: string +} + +export default function CreateFulfillmentConsumablesSupplyV2Page() { + const router = useRouter() + const { getSidebarMargin } = useSidebar() + const { user } = useAuth() + const [selectedSupplier, setSelectedSupplier] = useState(null) + const [selectedLogistics, setSelectedLogistics] = useState(null) + const [selectedConsumables, setSelectedConsumables] = useState([]) + const [searchQuery, setSearchQuery] = useState('') + const [productSearchQuery, setProductSearchQuery] = useState('') + const [deliveryDate, setDeliveryDate] = useState('') + const [notes, setNotes] = useState('') + const [isCreatingSupply, setIsCreatingSupply] = useState(false) + + // Загружаем контрагентов-поставщиков расходников + const { data: counterpartiesData, loading: counterpartiesLoading } = useQuery(GET_MY_COUNTERPARTIES) + + // Убираем избыточное логирование для предотвращения визуального "бесконечного цикла" + + // Стабилизируем переменные для useQuery + const queryVariables = useMemo(() => { + return { + organizationId: selectedSupplier?.id || '', // Всегда возвращаем объект, но с пустым ID если нет поставщика + search: productSearchQuery || null, + category: null, + type: 'CONSUMABLE' as const, // Фильтруем только расходники согласно rules2.md + } + }, [selectedSupplier?.id, productSearchQuery]) + + // Загружаем товары для выбранного поставщика с фильтрацией по типу CONSUMABLE + const { + data: productsData, + loading: productsLoading, + error: _productsError, + } = useQuery(GET_ORGANIZATION_PRODUCTS, { + skip: !selectedSupplier?.id, // Используем стабильное условие вместо !queryVariables + variables: queryVariables, + onCompleted: (data) => { + // Логируем только количество загруженных товаров + console.warn(`📦 Загружено товаров: ${data?.organizationProducts?.length || 0}`) + }, + onError: (error) => { + console.error('❌ GET_ORGANIZATION_PRODUCTS ERROR:', error) + }, + }) + + // Мутация для создания заказа поставки расходников v2 + const [createSupply] = useMutation(CREATE_FULFILLMENT_CONSUMABLE_SUPPLY) + + // Фильтруем только поставщиков расходников (поставщиков) + const consumableSuppliers = (counterpartiesData?.myCounterparties || []).filter( + (org: FulfillmentConsumableSupplier) => org.type === 'WHOLESALE', + ) + + // Фильтруем только логистические компании + const logisticsPartners = (counterpartiesData?.myCounterparties || []).filter( + (org: FulfillmentConsumableSupplier) => org.type === 'LOGIST', + ) + + // Фильтруем поставщиков по поисковому запросу + const filteredSuppliers = consumableSuppliers.filter( + (supplier: FulfillmentConsumableSupplier) => + supplier.name?.toLowerCase().includes(searchQuery.toLowerCase()) || + supplier.fullName?.toLowerCase().includes(searchQuery.toLowerCase()) || + supplier.inn?.toLowerCase().includes(searchQuery.toLowerCase()), + ) + + // Фильтруем товары по выбранному поставщику + // 📦 Получаем товары поставщика (уже отфильтрованы в GraphQL запросе по типу CONSUMABLE) + const supplierProducts = productsData?.organizationProducts || [] + + // Отладочное логирование только при смене поставщика + React.useEffect(() => { + if (selectedSupplier) { + console.warn('🔄 ПОСТАВЩИК ВЫБРАН:', { + id: selectedSupplier.id, + name: selectedSupplier.name || selectedSupplier.fullName, + type: selectedSupplier.type, + }) + } + }, [selectedSupplier]) // Включаем весь объект поставщика для корректной работы + + // Логируем результат загрузки товаров только при получении данных + React.useEffect(() => { + if (productsData && !productsLoading) { + console.warn('📦 ТОВАРЫ ЗАГРУЖЕНЫ:', { + organizationProductsCount: productsData?.organizationProducts?.length || 0, + supplierProductsCount: supplierProducts.length, + }) + } + }, [productsData, productsLoading, supplierProducts.length]) // Включаем все зависимости для корректной работы + + const formatCurrency = (amount: number) => { + return new Intl.NumberFormat('ru-RU', { + style: 'currency', + currency: 'RUB', + minimumFractionDigits: 0, + }).format(amount) + } + + const updateConsumableQuantity = (productId: string, quantity: number) => { + const product = supplierProducts.find((p: FulfillmentConsumableProduct) => p.id === productId) + if (!product || !selectedSupplier) return + + // 🔒 ВАЛИДАЦИЯ ОСТАТКОВ согласно правилам (раздел 6.2) + if (quantity > 0) { + const availableStock = (product.stock || product.quantity || 0) - (product.ordered || 0) + + if (quantity > availableStock) { + toast.error(`❌ Недостаточно остатков!\nДоступно: ${availableStock} шт.\nЗапрашивается: ${quantity} шт.`) + return + } + } + + setSelectedConsumables((prev) => { + const existing = prev.find((p) => p.id === productId) + + if (quantity === 0) { + // Удаляем расходник если количество 0 + return prev.filter((p) => p.id !== productId) + } + + if (existing) { + // Обновляем количество существующего расходника + return prev.map((p) => (p.id === productId ? { ...p, selectedQuantity: quantity } : p)) + } else { + // Добавляем новый расходник + return [ + ...prev, + { + id: product.id, + name: product.name, + price: product.price, + selectedQuantity: quantity, + unit: product.unit || 'шт', + category: product.category?.name || 'Расходники', + supplierId: selectedSupplier.id, + supplierName: selectedSupplier.name || selectedSupplier.fullName || 'Поставщик', + }, + ] + } + }) + } + + const getSelectedQuantity = (productId: string): number => { + const selected = selectedConsumables.find((p) => p.id === productId) + return selected ? selected.selectedQuantity : 0 + } + + const getTotalAmount = () => { + return selectedConsumables.reduce((sum, consumable) => sum + consumable.price * consumable.selectedQuantity, 0) + } + + const getTotalItems = () => { + return selectedConsumables.reduce((sum, consumable) => sum + consumable.selectedQuantity, 0) + } + + const handleCreateSupply = async () => { + if (!selectedSupplier || selectedConsumables.length === 0 || !deliveryDate) { + toast.error('Заполните все обязательные поля: поставщик, расходники и дата доставки') + return + } + + setIsCreatingSupply(true) + + try { + // Новый формат для системы v2 + const input = { + supplierId: selectedSupplier.id, + requestedDeliveryDate: deliveryDate, + items: selectedConsumables.map((consumable) => ({ + productId: consumable.id, + requestedQuantity: consumable.selectedQuantity, + })), + notes: notes || undefined, + } + + console.warn('🚀 СОЗДАНИЕ ПОСТАВКИ v2 - INPUT:', input) + + const result = await createSupply({ + variables: { input }, + refetchQueries: [ + { query: GET_MY_FULFILLMENT_CONSUMABLE_SUPPLIES }, // Обновляем новый v2 запрос + ], + }) + + console.warn('🎯 РЕЗУЛЬТАТ СОЗДАНИЯ ПОСТАВКИ v2:', result) + + if (result.data?.createFulfillmentConsumableSupply?.success) { + toast.success('Поставка расходников создана успешно!') + // Очищаем форму + setSelectedSupplier(null) + setSelectedLogistics(null) + setSelectedConsumables([]) + setDeliveryDate('') + setProductSearchQuery('') + setSearchQuery('') + setNotes('') + + // Перенаправляем на страницу детальных поставок + router.push('/fulfillment-supplies/detailed-supplies') + } else { + toast.error(result.data?.createFulfillmentConsumableSupply?.message || 'Ошибка при создании поставки') + } + } catch (error) { + console.error('Error creating fulfillment consumables supply v2:', error) + toast.error('Ошибка при создании поставки расходников') + } finally { + setIsCreatingSupply(false) + } + } + + return ( +
+ +
+
+ {/* Заголовок */} +
+
+

Создание поставки расходников фулфилмента

+

+ Выберите поставщика и добавьте расходники в заказ для вашего фулфилмент-центра +

+
+ +
+ + {/* Основной контент с двумя блоками */} +
+ {/* Левая колонка - Поставщики и Расходники */} +
+ {/* Блок "Поставщики" */} + +
+
+

+ + Поставщики расходников +

+
+ + setSearchQuery(e.target.value)} + className="bg-white/20 backdrop-blur border-white/30 text-white placeholder-white/50 pl-10 h-8 text-sm rounded-full shadow-inner focus:ring-2 focus:ring-purple-400/50 focus:border-purple-400/50 transition-all duration-300" + /> +
+ {selectedSupplier && ( + + )} +
+
+ +
+ {counterpartiesLoading ? ( +
+
+

Загружаем поставщиков...

+
+ ) : filteredSuppliers.length === 0 ? ( +
+
+ +
+

+ {searchQuery ? 'Поставщики не найдены' : 'Добавьте поставщиков'} +

+
+ ) : ( +
+ {filteredSuppliers.slice(0, 7).map((supplier: FulfillmentConsumableSupplier, index: number) => ( + setSelectedSupplier(supplier)} + > +
+
+ ({ + id: user.id, + avatar: user.avatar, + })), + }} + size="sm" + /> + {selectedSupplier?.id === supplier.id && ( +
+ +
+ )} +
+
+

+ {(supplier.name || supplier.fullName || 'Поставщик').slice(0, 10)} +

+
+ + 4.5 +
+
+
+
+
+
+ + {/* Hover эффект */} +
+
+ ))} + {filteredSuppliers.length > 7 && ( +
+
+{filteredSuppliers.length - 7}
+
ещё
+
+ )} +
+ )} +
+
+ + {/* Блок "Расходники" */} + +
+
+

+ + Расходники для фулфилмента + {selectedSupplier && ( + + - {selectedSupplier.name || selectedSupplier.fullName} + + )} +

+
+ {selectedSupplier && ( +
+ + setProductSearchQuery(e.target.value)} + className="bg-white/10 border-white/20 text-white placeholder-white/40 pl-7 h-8 text-sm" + /> +
+ )} +
+ +
+ {!selectedSupplier ? ( +
+ +

Выберите поставщика для просмотра расходников

+
+ ) : productsLoading ? ( +
+
+

Загрузка...

+
+ ) : supplierProducts.length === 0 ? ( +
+ +

Нет доступных расходников

+
+ ) : ( +
+ {supplierProducts.map((product: FulfillmentConsumableProduct, index: number) => { + const selectedQuantity = getSelectedQuantity(product.id) + return ( + 0 + ? 'ring-2 ring-green-400/50 bg-gradient-to-br from-green-500/20 via-green-400/10 to-green-500/20' + : 'hover:from-white/20 hover:via-white/10 hover:to-white/20 hover:border-white/40' + }`} + style={{ + animationDelay: `${index * 50}ms`, + minHeight: '200px', + width: '100%', + }} + > +
+ {/* Изображение товара */} +
+ {/* 🚫 ОВЕРЛЕЙ НЕДОСТУПНОСТИ */} + {(() => { + const totalStock = product.stock || (product as any).quantity || 0 + const orderedStock = (product as any).ordered || 0 + const availableStock = totalStock - orderedStock + + if (availableStock <= 0) { + return ( +
+
+
НЕТ В НАЛИЧИИ
+
+
+ ) + } + return null + })()} + {product.images && product.images.length > 0 && product.images[0] ? ( + {product.name} + ) : product.mainImage ? ( + {product.name} + ) : ( +
+ +
+ )} + {selectedQuantity > 0 && ( +
+ + {selectedQuantity > 999 ? '999+' : selectedQuantity} + +
+ )} +
+ + {/* Информация о товаре */} +
+

+ {product.name} +

+
+ {product.category && ( + + {product.category.name.slice(0, 10)} + + )} + {/* 🚨 ИНДИКАТОР НИЗКИХ ОСТАТКОВ согласно правилам (раздел 6.3) */} + {(() => { + const totalStock = product.stock || product.quantity || 0 + const orderedStock = product.ordered || 0 + const availableStock = totalStock - orderedStock + + if (availableStock <= 0) { + return ( + + Нет в наличии + + ) + } else if (availableStock <= 10) { + return ( + + Мало остатков + + ) + } + return null + })()} +
+
+ + {formatCurrency(product.price)} + + {/* 📊 АКТУАЛЬНЫЙ ОСТАТОК согласно правилам (раздел 6.4.2) */} +
+ {(() => { + const totalStock = product.stock || product.quantity || 0 + const orderedStock = product.ordered || 0 + const availableStock = totalStock - orderedStock + + return ( +
+ + Доступно: {availableStock} + + {orderedStock > 0 && ( + Заказано: {orderedStock} + )} +
+ ) + })()} +
+
+
+ + {/* Управление количеством */} +
+ {(() => { + const totalStock = product.stock || (product as any).quantity || 0 + const orderedStock = (product as any).ordered || 0 + const availableStock = totalStock - orderedStock + + return ( +
+ + { + let inputValue = e.target.value + + // Удаляем все нецифровые символы + inputValue = inputValue.replace(/[^0-9]/g, '') + + // Удаляем ведущие нули + inputValue = inputValue.replace(/^0+/, '') + + // Если строка пустая после удаления нулей, устанавливаем 0 + const numericValue = inputValue === '' ? 0 : parseInt(inputValue) + + // Ограничиваем значение максимумом доступного остатка + const clampedValue = Math.min(numericValue, availableStock, 99999) + + updateConsumableQuantity(product.id, clampedValue) + }} + onBlur={(e) => { + // При потере фокуса, если поле пустое, устанавливаем 0 + if (e.target.value === '') { + updateConsumableQuantity(product.id, 0) + } + }} + className="w-16 h-7 text-center text-sm bg-white/10 border-white/20 text-white rounded px-1 focus:ring-2 focus:ring-purple-400/50 focus:border-purple-400/50" + placeholder="0" + /> + +
+ ) + })()} + + {selectedQuantity > 0 && ( +
+ + {formatCurrency(product.price * selectedQuantity)} + +
+ )} +
+
+ + {/* Hover эффект */} +
+
+ ) + })} +
+ )} +
+
+
+ + {/* Правая колонка - Корзина */} +
+ +

+ + Корзина ({getTotalItems()} шт) +

+ + {selectedConsumables.length === 0 ? ( +
+
+ +
+

Корзина пуста

+

Добавьте расходники для создания поставки

+
+ ) : ( +
+ {selectedConsumables.map((consumable) => ( +
+
+

{consumable.name}

+

+ {formatCurrency(consumable.price)} × {consumable.selectedQuantity} +

+
+
+ + {formatCurrency(consumable.price * consumable.selectedQuantity)} + + +
+
+ ))} +
+ )} + +
+
+ + setDeliveryDate(e.target.value)} + className="bg-white/10 border-white/20 text-white h-8 text-sm" + min={new Date().toISOString().split('T')[0]} + required + /> +
+ + {/* Выбор логистики */} +
+ +
+ +
+ + + +
+
+
+ + {/* Заметки */} +
+ +