feat(fulfillment-supplies): миграция формы создания поставок расходников на v2 систему

- Обновлена форма создания поставок расходников фулфилмента для использования 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 <noreply@anthropic.com>
This commit is contained in:
Veronika Smirnova
2025-08-25 07:52:46 +03:00
parent d05f0a6a93
commit 0e3ffc179c
34 changed files with 5795 additions and 565 deletions

View File

@ -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

View File

@ -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`

View File

@ -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

View File

@ -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 <CreateConsumablesV1 />
}
// Новая реализация
return (
<div>
<h1>Создать поставку расходников ФФ (v2.0)</h1>
{/* Полная форма */}
</div>
)
}
```
### **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 <FFConsumablesV1Tab />
}
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 ? <FFConsumablesV2Tab /> : <FFConsumablesV1Tab />
}
```
### **⚠️ ТОЧКА ПРОВЕРКИ 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 (
<div className="feedback-widget">
<p>Как вам новая система поставок?</p>
<button onClick={() => sendFeedback('positive')}>👍</button>
<button onClick={() => sendFeedback('negative')}>👎</button>
</div>
)
}
```
### **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 для контроля доступа
- Мониторинг метрик на каждом этапе
### 🔧 **ГИБКОСТЬ РЕАЛИЗАЦИИ:**
- Возможность приостановить на любом этапе
- Адаптация планов по результатам предыдущих фаз
- Учет пожеланий пользователей
**Система готова к безопасной поэтапной реализации!**