feat(supplier-orders): добавить параметры поставки в таблицу заявок

- Добавлены колонки Объём и Грузовые места между Цена товаров и Статус
- Реализованы инпуты для ввода volume и packagesCount в статусе PENDING для роли WHOLESALE
- Добавлена мутация UPDATE_SUPPLY_PARAMETERS с проверками безопасности
- Скрыта строка Поставщик для роли WHOLESALE (поставщик знает свои данные)
- Исправлено выравнивание таблицы при скрытии уровня поставщика
- Реорганизованы документы: legacy-rules/, docs/, docs-and-reports/

ВНИМАНИЕ: Компонент multilevel-supplies-table.tsx (1697 строк) нарушает правило модульной архитектуры (>800 строк требует рефакторинга)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Veronika Smirnova
2025-08-23 18:47:23 +03:00
parent 35cbbac504
commit 12fd8ddf61
27 changed files with 1250 additions and 208 deletions

View File

@ -625,7 +625,7 @@ query GetMyCounterparties($type: OrganizationType) {
### МУТАЦИИ ПОСТАВЩИКОВ:
```graphql
# Одобрение заказа поставщиком с опциональными полями упаковки
# Одобрение заказа поставщиком с дополнительными параметрами поставки
mutation SupplierApproveOrder(
$orderId: ID!
$packagesCount: Int
@ -635,23 +635,25 @@ mutation SupplierApproveOrder(
) {
supplierApproveOrder(
id: $orderId
packagesCount: $packagesCount # Опционально: для логистических расчетов
volume: $volume # Опционально: для планирования логистики
readyDate: $readyDate # Опционально: дата готовности к отгрузке
notes: $notes # Опционально: комментарии
packagesCount: $packagesCount # Параметр поставки: количество грузовых мест
volume: $volume # Параметр поставки: объем груза
readyDate: $readyDate # Параметр поставки: дата готовности к отгрузке
notes: $notes # Параметр поставки: дополнительная информация
) {
success
message
order {
id
status # PENDING → SUPPLIER_APPROVED
deliveryDate # Основной параметр поставки
totalAmount # Ключевой параметр поставки - общая стоимость
totalItems # Параметр поставки - количество товаров
organization {
id
name
}
totalAmount
packagesCount # null если не указано
volume # null если не указано
packagesCount # Параметр поставки (опционально)
volume # Параметр поставки (опционально)
readyDate # null если не указано
notes # null если не указано
}

View File

@ -0,0 +1,342 @@
# ПРАВИЛА ВНЕДРЕНИЯ МОДУЛЯ "БИРЖА" В СИСТЕМУ SFERA
## 🎯 ОБЗОР ЗАДАЧИ
**Цель:** Создание нового раздела "Биржа" во всех кабинетах с переносом функционала из раздела "Маркет"
**Принцип:** Модульная архитектура с безопасным рефакторингом и сохранением совместимости
## 📋 ДЕТАЛЬНЫЕ ПРАВИЛА РЕАЛИЗАЦИИ
### 1. СТРУКТУРНАЯ АРХИТЕКТУРА
#### 1.1 Создание модульной структуры:
```
src/components/exchange/ # Новый модуль Биржи
├── exchange-dashboard.tsx # Главная страница Биржи
├── tabs/
│ ├── investments-tab.tsx # Перенос из market/investments
│ └── market-tab.tsx # Перенос из market/market
├── hooks/
│ ├── useExchangeData.ts # Хуки для данных биржи
│ └── useInvestmentOperations.ts # Логика инвестиций
├── components/
│ ├── ExchangeHeader.tsx # Заголовок раздела
│ └── ExchangeStats.tsx # Статистика биржи
└── types/
└── exchange.types.ts # TypeScript интерфейы
```
#### 1.2 Обновление навигации:
```typescript
// src/components/dashboard/sidebar.tsx
const navigationItems = [
// ... существующие пункты
{
name: 'Биржа',
href: '/exchange',
icon: TrendingUp, // или Zap, или BarChart3
position: 'before-settings', // Над разделом настройки
},
// ... остальные пункты
]
```
### 2. БЕЗОПАСНЫЙ ПЕРЕНОС КОМПОНЕНТОВ
#### 2.1 Сохранение оригиналов:
```bash
# Создаем backup перед переносом
cp -r src/components/market/investments-tab.tsx src/components/market/investments-tab.tsx.backup
cp -r src/components/market/market-tab.tsx src/components/market/market-tab.tsx.backup
```
#### 2.2 Поэтапный перенос:
```typescript
// Этап 1: Создание алиасов (временно)
export { InvestmentsTab as ExchangeInvestmentsTab } from '../market/investments-tab'
export { MarketTab as ExchangeMarketTab } from '../market/market-tab'
// Этап 2: Копирование с адаптацией
// Этап 3: Обновление импортов
// Этап 4: Удаление дубликатов
```
### 3. МАРШРУТИЗАЦИЯ И URL
#### 3.1 Новая структура маршрутов:
```
/exchange # Главная страница Биржи
├── /exchange/investments # Инвестиции (было /market/investments)
└── /exchange/market # Маркет (было /market/market)
/market # Остается, но без investments и market
├── /market/logistics # Остается
├── /market/analytics # Остается
└── /market/other-tabs # Остальные вкладки остаются
```
#### 3.2 Редиректы для совместимости:
```typescript
// src/middleware.ts или pages/_app.tsx
const redirectRules = [
{
from: '/market/investments',
to: '/exchange/investments',
permanent: true,
},
{
from: '/market/market',
to: '/exchange/market',
permanent: true,
},
]
```
### 4. УНИВЕРСАЛЬНОСТЬ ДЛЯ ВСЕХ КАБИНЕТОВ
#### 4.1 Роль-агностический подход:
```typescript
// src/components/exchange/exchange-dashboard.tsx
interface ExchangeDashboardProps {
userRole: 'SELLER' | 'WHOLESALE' | 'FULFILLMENT' | 'LOGIST'
// Компонент адаптируется под роль, но функционал одинаковый
}
const ExchangeDashboard: FC<ExchangeDashboardProps> = ({ userRole }) => {
// Одинаковая логика для всех ролей
return (
<div className="exchange-dashboard">
<ExchangeHeader userRole={userRole} />
<ExchangeTabs userRole={userRole} />
</div>
)
}
```
#### 4.2 Единая конфигурация:
```typescript
// src/config/exchange.config.ts
export const EXCHANGE_CONFIG = {
tabs: [
{
id: 'investments',
name: 'Инвестиции',
path: '/exchange/investments',
availableFor: ['SELLER', 'WHOLESALE', 'FULFILLMENT', 'LOGIST'], // Все роли
},
{
id: 'market',
name: 'Маркет',
path: '/exchange/market',
availableFor: ['SELLER', 'WHOLESALE', 'FULFILLMENT', 'LOGIST'], // Все роли
},
],
}
```
### 5. ИКОНКА И UI ЭЛЕМЕНТЫ
#### 5.1 Выбор иконки:
```typescript
import { TrendingUp } from 'lucide-react' // Основной вариант - рост, тренды
// Альтернативы:
// import { Zap } from 'lucide-react' // Энергия, быстрота
// import { BarChart3 } from 'lucide-react' // Аналитика, графики
// import { Activity } from 'lucide-react' // Активность, пульс
```
#### 5.2 Стилизация в соответствии с дизайн-системой:
```typescript
// Используем существующую Glass Morphism систему
<div className="glass-card">
<div className="flex items-center gap-3">
<TrendingUp className="h-5 w-5 text-primary" />
<span className="font-semibold">Биржа</span>
</div>
</div>
```
### 6. БЕЗОПАСНОСТЬ И СОВМЕСТИМОСТЬ
#### 6.1 Проверка существующих зависимостей:
```bash
# Поиск всех ссылок на старые компоненты
grep -r "market/investments" src/
grep -r "market/market" src/
grep -r "/market/investments" src/
grep -r "/market/market" src/
```
#### 6.2 Градуальная миграция:
```typescript
// Временный мост-компонент
const MarketInvestmentsBridge: FC = () => {
// Редирект на новый URL
useEffect(() => {
router.replace('/exchange/investments')
}, [])
return <div>Перенаправление на новую страницу...</div>
}
```
### 7. ТЕСТИРОВАНИЕ И ПРОВЕРКА
#### 7.1 Чек-лист проверки:
- [ ] Все ссылки работают корректно
- [ ] Редиректы функционируют
- [ ] Навигация обновлена во всех кабинетах
- [ ] Компоненты отображаются идентично
- [ ] Данные загружаются без ошибок
- [ ] TypeScript типы корректны
#### 7.2 Тестирование по ролям:
```typescript
const testScenarios = [
{ role: 'SELLER', path: '/exchange' },
{ role: 'WHOLESALE', path: '/exchange' },
{ role: 'FULFILLMENT', path: '/exchange' },
{ role: 'LOGIST', path: '/exchange' },
]
```
### 8. ГРАФQL И ДАННЫЕ
#### 8.1 Анализ существующих компонентов:
```typescript
// ✅ ОБНАРУЖЕНО: Компоненты НЕ ИСПОЛЬЗУЮТ GraphQL
// market-investments.tsx - статичные UI карточки
// market-business.tsx - статичные UI карточки
// Никаких useQuery/useMutation не найдено
```
#### 8.2 Стратегия переноса данных:
```typescript
// Компоненты используют только UI элементы:
// - Статичный контент
// - Иконки Lucide React
// - Glass Morphism карточки
// - Никаких внешних API вызовов
```
#### 8.3 Заключение по GraphQL:
-**GraphQL запросы НЕ НУЖНЫ** - компоненты статичные
-**Простой перенос** - только UI компоненты
-**Без изменений в resolvers** - данные не загружаются
### 9. ПЛАН ПОЭТАПНОЙ РЕАЛИЗАЦИИ
#### Фаза 1: Подготовка
1. Создание структуры папок
2. Анализ зависимостей
3. Создание TypeScript интерфейсов
#### Фаза 2: Создание компонентов
1. Копирование и адаптация существующих компонентов
2. Создание exchange-dashboard.tsx
3. Настройка навигации
#### Фаза 3: Интеграция
1. Обновление маршрутов
2. Настройка редиректов
3. Обновление импортов
#### Фаза 4: Тестирование и очистка
1. Тестирование всех сценариев
2. Удаление дубликатов
3. Финальная проверка
---
## ✅ ПОДТВЕРЖДЁННЫЕ РЕШЕНИЯ:
1. **Иконка:**`TrendingUp` подходит для раздела "Биржа"
2. **Позиционирование:** ✅ Размещать прямо перед Settings в сайдбаре
3. **GraphQL:** ✅ Изменения НЕ НУЖНЫ - компоненты статичные
4. **Уведомления:**НЕ показывать уведомления о переносе
## 🎯 ФИНАЛЬНАЯ АРХИТЕКТУРА:
### Структура переноса:
```
ИЗ: /market (4 вкладки)
├── investments ➜ /exchange/investments
├── business ➜ /exchange/market
├── products ➜ остается в /market
└── requests ➜ остается в /market
В: /exchange (2 вкладки)
├── /exchange/investments (было: market-investments.tsx)
└── /exchange/market (было: market-business.tsx)
```
### Обновление nav структуры:
```typescript
// sidebar.tsx порядок пунктов:
[Home, Supplies, Services, Exchange, Settings, ...]
// ↑ новый пункт
```
## 📊 СТАТУС РЕАЛИЗАЦИИ
**Текущий статус**: ✅ **ПОЛНОСТЬЮ РЕАЛИЗОВАНО**
**Дата завершения**: 22 августа 2025
### Выполненные фазы:
**Фаза 1**: Создана структура папок `src/components/exchange/`
**Фаза 2**: Скопированы и адаптированы компоненты из market
**Фаза 3**: Обновлена навигация и созданы маршруты
**Фаза 4**: Протестировано и очищено
### Результат реализации:
- ✅ Раздел "Биржа" доступен во всех кабинетах с иконкой TrendingUp
- ✅ Перенесены вкладки "Инвестиции" и "Бизнес" из Маркета в Биржу
- ✅ Маркет теперь содержит только "Товары" и "Заявки"
- ✅ Сохранена полная функциональность без потери данных
- ✅ Безопасная реализация с резервными копиями
- ✅ Протестировано - dev сервер запускается корректно
- ✅ Типы TypeScript корректны
### Финальная структура:
```
src/components/exchange/
├── exchange-dashboard.tsx ✅ Главная страница Биржи
├── tabs/
│ ├── investments-tab.tsx ✅ Перенос из market-investments
│ └── business-tab.tsx ✅ Перенос из market-business
├── types/
│ └── exchange.types.ts ✅ TypeScript интерфейсы
└── index.ts ✅ Экспорты модуля
src/app/exchange/page.tsx ✅ Next.js маршрут
```
**ЗАДАЧА ВЫПОЛНЕНА УСПЕШНО!** 🎉

View File

@ -36,7 +36,7 @@ interface SupplyOrder {
sellerConsumables: Supply[] // Расходники селлера
}
// Упаковочная информация (опциональная)
// Параметры поставки (опциональные)
packagesCount?: number // Количество грузовых мест
volume?: number // Объем груза в м³
readyDate?: Date // Дата готовности к отгрузке
@ -567,7 +567,7 @@ export class CommercialDataAudit {
"quantity": 10
// ❌ НЕ видит recipe
}],
"packagesCount": 2, // ✅ Видит упаковочную информацию
"packagesCount": 2, // ✅ Видит параметры поставки
"volume": 0.5,
// ❌ НЕ видит totalAmount, услуги ФФ, логистику
}

View File

@ -0,0 +1,302 @@
# ПРАВИЛА ПАРАМЕТРОВ ПОСТАВКИ
## 📋 **ОПРЕДЕЛЕНИЕ ПАРАМЕТРОВ ПОСТАВКИ**
**Параметры поставки** - это все характеристики и данные, которые описывают конкретную поставку товаров в системе SFERA.
## 🎯 **КЛАССИФИКАЦИЯ ПАРАМЕТРОВ ПОСТАВКИ**
### **1. ОБЯЗАТЕЛЬНЫЕ ПАРАМЕТРЫ:**
- **`id`** - Уникальный идентификатор поставки
- **`deliveryDate`** - Дата поставки (когда товары должны быть доставлены)
- **`totalAmount`** - Общая стоимость поставки
- **`totalItems`** - Количество товаров в поставке
- **`organizationId`** - Идентификатор заказчика
- **`partnerId`** - Идентификатор поставщика
- **`status`** - Статус поставки (PENDING, APPROVED, SHIPPED и т.д.)
### **2. ЦЕНОВЫЕ ПАРАМЕТРЫ:**
- **`totalAmount`** - Общая сумма поставки
- **`goodsPrice`** - Стоимость товаров (расчетное поле)
- **`servicesPrice`** - Стоимость услуг фулфилмента (расчетное поле)
- **`logisticsPrice`** - Стоимость логистических услуг (расчетное поле)
- **`sellerConsumablesPrice`** - Стоимость расходников селлера (расчетное поле)
- **`ffConsumablesPrice`** - Стоимость расходников фулфилмента (расчетное поле)
### **3. ТОВАРНЫЕ ПАРАМЕТРЫ:**
- **`items[]`** - Массив товаров с количествами и ценами
- **`productId`** - ID товара в позиции
- **`quantity`** - Количество каждого товара
- **`price`** - Цена за единицу товара
- **`totalPrice`** - Общая стоимость позиции
- **`services[]`** - Массив услуг для товара
- **`fulfillmentConsumables[]`** - Расходники фулфилмента для товара
- **`sellerConsumables[]`** - Расходники селлера для товара
- **`marketplaceCardId`** - ID карточки маркетплейса
### **4. ЛОГИСТИЧЕСКИЕ ПАРАМЕТРЫ:**
- **`packagesCount`** - Количество грузовых мест
- **`volume`** - Объём груза в м³
- **`routes[]`** - Маршруты доставки
- **`fromLocation`** - Точка забора груза
- **`toLocation`** - Точка доставки
- **`fromAddress`** - Полный адрес забора
- **`toAddress`** - Полный адрес доставки
- **`distance`** - Расстояние маршрута в км
- **`estimatedTime`** - Время доставки в часах
- **`logisticsPartnerId`** - ID логистического партнера
### **5. УПРАВЛЕНЧЕСКИЕ ПАРАМЕТРЫ:**
- **`responsibleEmployee`** - Ответственный сотрудник
- **`notes`** - Комментарии и дополнительная информация
- **`createdAt`** - Дата создания поставки
- **`updatedAt`** - Дата последнего обновления
- **`fulfillmentCenterId`** - ID фулфилмент центра
- **`consumableType`** - Тип расходников
<!-- ОТКАТ: Расширенная классификация параметров поставки
### **6. ВРЕМЕННЫЕ ПАРАМЕТРЫ:**
- **`deliveryDate`** - Плановая дата доставки
- **`readyDate`** - Дата готовности к отгрузке
- **`actualDeliveryDate`** - Фактическая дата доставки
- **`createdAt`** - Время создания заявки
- **`approvedAt`** - Время одобрения поставщиком
- **`shippedAt`** - Время отгрузки
### **7. КОНТАКТНЫЕ ПАРАМЕТРЫ:**
- **`supplierContact`** - Контактное лицо поставщика
- **`buyerContact`** - Контактное лицо покупателя
- **`logisticsContact`** - Контактное лицо логистики
- **`fulfillmentContact`** - Контактное лицо фулфилмента
### **8. АДРЕСНЫЕ ПАРАМЕТРЫ:**
- **`pickupAddress`** - Адрес забора груза
- **`deliveryAddress`** - Адрес доставки
- **`warehouseLocation`** - Местоположение склада
- **`marketLocation`** - Адрес торговой точки
### **9. КАЧЕСТВЕННЫЕ ПАРАМЕТРЫ:**
- **`qualityRequirements`** - Требования к качеству товаров
- **`temperatureConditions`** - Температурные условия хранения/транспортировки
- **`handlingInstructions`** - Инструкции по обращению с грузом
- **`packagingType`** - Тип упаковки товаров
- **`fragileGoods`** - Отметка о хрупких товарах
### **10. ФИНАНСОВЫЕ ПАРАМЕТРЫ:**
- **`paymentTerms`** - Условия оплаты
- **`paymentMethod`** - Способ оплаты
- **`currency`** - Валюта расчетов
- **`taxRate`** - Налоговая ставка
- **`invoiceNumber`** - Номер счета
- **`paymentStatus`** - Статус оплаты
### **11. ТЕХНИЧЕСКИЕ ПАРАМЕТРЫ:**
- **`supplyOrderId`** - Уникальный идентификатор поставки
- **`supplierOrderNumber`** - Внутренний номер заказа поставщика
- **`trackingNumber`** - Трек-номер для отслеживания
- **`barcodes`** - Штрих-коды товаров
- **`rfidTags`** - RFID метки для автоматизации
### **12. АНАЛИТИЧЕСКИЕ ПАРАМЕТРЫ:**
- **`profitMargin`** - Маржинальность поставки
- **`supplierRating`** - Рейтинг поставщика
- **`deliveryReliability`** - Надежность доставки
- **`orderPriority`** - Приоритет заказа
- **`seasonalFactor`** - Сезонный коэффициент
-->
## 🔄 **ЖИЗНЕННЫЙ ЦИКЛ ПАРАМЕТРОВ ПОСТАВКИ**
### **Этап 1: Создание поставки (SELLER)**
```typescript
// Селлер указывает базовые параметры поставки
{
deliveryDate: "2025-08-25",
totalItems: 100,
items: [...products],
partnerId: "supplier-123",
organizationId: "seller-456"
}
```
### **Этап 2: Одобрение поставщиком (WHOLESALE)**
```typescript
// Поставщик может дополнить параметры поставки
{
status: "SUPPLIER_APPROVED",
packagesCount: 3, // Новый параметр
volume: 1.2, // Новый параметр
readyDate: "2025-08-24", // Новый параметр
notes: "Хрупкий груз" // Новый параметр
}
```
### **Этап 3: Логистическое планирование (LOGIST)**
```typescript
// Логистика использует параметры для расчетов
{
logisticsPrice: calculateByVolume(volume, distance),
routes: [
{
from: "Садовод",
to: "ФФ Центр",
packagesCount: 3,
volume: 1.2
}
]
}
```
## 🛡️ **ПРАВИЛА БЕЗОПАСНОСТИ ПАРАМЕТРОВ**
### **Видимость по ролям:**
| Параметр поставки | SELLER | WHOLESALE | FULFILLMENT | LOGIST |
|------------------|--------|-----------|-------------|---------|
| deliveryDate | ✅ | ✅ | ✅ | ✅ |
| totalAmount | ✅ | ❌ | ❌ | ❌ |
| productPrice | ✅ | ✅ | ❌ | ❌ |
| packagesCount | ✅ | ✅ | ✅ | ✅ |
| volume | ✅ | ✅ | ✅ | ✅ |
| recipe | ✅ | ❌ | ✅ | ❌ |
| logisticsPrice | ✅ | ❌ | ✅ | ✅ |
### **Права на изменение:**
- **SELLER**: Может изменять до одобрения поставщиком
- **WHOLESALE**: Может добавлять логистические параметры при одобрении
- **FULFILLMENT**: Не может изменять, только просматривать
- **LOGIST**: Может добавлять маршруты и логистические расчеты
## 🎛️ **UI КОМПОНЕНТЫ ДЛЯ ПАРАМЕТРОВ ПОСТАВКИ**
### **Форма ввода параметров поставщиком:**
```jsx
<div className="supply-parameters-form">
<h3>Параметры поставки</h3>
{/* Основные параметры */}
<Input name="deliveryDate" label="Дата поставки" type="date" required />
<Input name="totalAmount" label="Общая стоимость" type="number" readOnly />
{/* Логистические параметры (опционально) */}
<Input name="packagesCount" label="Количество грузовых мест" type="number" />
<Input name="volume" label="Объём груза (м³)" type="number" step="0.01" />
{/* Дополнительные параметры */}
<Input name="readyDate" label="Дата готовности" type="date" />
<Textarea name="notes" label="Комментарии к поставке" />
</div>
```
### **Отображение в таблице заявок:**
```jsx
<Table>
<TableHeader>
<TableRow>
<TableHead>Дата поставки</TableHead>
<TableHead>Количество товаров</TableHead>
<TableHead>Стоимость товаров</TableHead>
{userRole !== 'WHOLESALE' && <TableHead>Общая стоимость</TableHead>}
<TableHead>Параметры логистики</TableHead>
<TableHead>Статус</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{supplies.map(supply => (
<TableRow key={supply.id}>
<TableCell>{supply.deliveryDate}</TableCell>
<TableCell>{supply.totalItems}</TableCell>
<TableCell>{formatCurrency(supply.productPrice)}</TableCell>
{userRole !== 'WHOLESALE' && (
<TableCell>{formatCurrency(supply.totalAmount)}</TableCell>
)}
<TableCell>
{supply.packagesCount && `${supply.packagesCount} мест`}
{supply.volume && ` • ${supply.volume} м³`}
</TableCell>
<TableCell><StatusBadge status={supply.status} /></TableCell>
</TableRow>
))}
</TableBody>
</Table>
```
## 📊 **ВАЛИДАЦИЯ ПАРАМЕТРОВ ПОСТАВКИ**
### **Серверная валидация:**
```typescript
function validateSupplyParameters(params: SupplyParameters) {
// Обязательные параметры
if (!params.deliveryDate) throw new Error('Дата поставки обязательна')
if (!params.totalItems || params.totalItems <= 0) {
throw new Error('Количество товаров должно быть больше 0')
}
// Логистические параметры
if (params.packagesCount && params.packagesCount <= 0) {
throw new Error('Количество грузовых мест должно быть положительным')
}
if (params.volume && params.volume <= 0) {
throw new Error('Объём груза должен быть положительным')
}
// Даты
if (params.readyDate && params.deliveryDate) {
if (new Date(params.readyDate) > new Date(params.deliveryDate)) {
throw new Error('Дата готовности не может быть позже даты поставки')
}
}
}
```
## 🔍 **ПОИСК И ФИЛЬТРАЦИЯ ПО ПАРАМЕТРАМ**
### **GraphQL запрос с фильтрами:**
```graphql
query GetSuppliesByParameters(
$deliveryDateFrom: DateTime
$deliveryDateTo: DateTime
$minAmount: Float
$maxAmount: Float
$status: SupplyStatus
$hasLogisticsParams: Boolean
) {
mySupplyOrders(
filters: {
deliveryDate: { gte: $deliveryDateFrom, lte: $deliveryDateTo }
totalAmount: { gte: $minAmount, lte: $maxAmount }
status: $status
packagesCount: { isNull: $hasLogisticsParams }
}
) {
id
deliveryDate
totalAmount
totalItems
packagesCount
volume
status
}
}
```
## 📈 **АНАЛИТИКА ПО ПАРАМЕТРАМ ПОСТАВКИ**
### **Ключевые метрики:**
- Средний объём поставки (в м³)
- Среднее количество грузовых мест
- Распределение по стоимости поставок
- Время цикла от создания до доставки
- Процент поставок с дополнительными параметрами
---
*Все параметры поставки служат для оптимизации логистических процессов и обеспечения прозрачности цепочки поставок в экосистеме SFERA.*

View File

@ -191,9 +191,9 @@ model SupplyOrder {
totalAmount Decimal @db.Decimal(12, 2) // Общая сумма
totalItems Int // Количество позиций
// Логистические данные
packagesCount Int? // Грузовые места
volume Float? // Объём в м³
// Параметры поставки (дополнительные)
packagesCount Int? // Количество грузовых мест - параметр поставки
volume Float? // Объём груза в м³ - параметр поставки
// Управление
responsibleEmployee String? // ID ответственного

View File

@ -0,0 +1,333 @@
# 🏗️ Паттерн модульной архитектуры для React компонентов
> ⚠️ **ОФИЦИАЛЬНЫЙ СТАНДАРТ АРХИТЕКТУРЫ SFERA**
> Этот документ описывает **ОБЯЗАТЕЛЬНУЮ** архитектуру для всех новых компонентов >500 строк и рефакторинга существующих >800 строк.
## 🎯 СТАТУС АРХИТЕКТУРНОГО СТАНДАРТА
### 🟢 ПРИНЯТЫЕ СТАНДАРТЫ (готовы к использованию):
-**create-suppliers-supply-page.tsx** → Модульная архитектура (ФАЗА 1-4 завершены)
-**direct-supply-creation.tsx** → Модульная архитектура (ЭТАПЫ 1-5 завершены)
### 📋 ПРАВИЛА ПРИМЕНЕНИЯ:
1. **ВСЕ НОВЫЕ КОМПОНЕНТЫ >500 строк** → создавать по модульной архитектуре
2. **Существующие компоненты >800 строк** → рефакторить по возможности
3. **Обязательно использовать** этот паттерн для компонентов dashboard, creation, management
## 🎯 Применимость паттерна
### Кандидаты для рефакторинга:
- **Размер**: >800 строк кода
- **Сложность**: Множественные состояния и бизнес-логика
- **Многообразие UI**: Различные секции интерфейса
- **Частые изменения**: Активно развивающиеся компоненты
### Большие компоненты в SFERA (кандидаты):
```
3052 строки - timesheet-demo.tsx
2012 строк - fulfillment-warehouse-dashboard.tsx
1654 строки - navigation-demo.tsx
1637 строк - direct-supply-creation.tsx ✅ ЗАВЕРШЕН (модульная архитектура)
1563 строки - user-settings.tsx
1523 строки - advertising-tab.tsx
1304 строки - wb-product-cards.tsx
```
## 📁 Универсальная структура модуля
```
src/components/[domain]/[component-name]/
├── index.tsx # Главный компонент-оркестратор
├── blocks/ # UI блок-компоненты
│ ├── [Feature]Block.tsx # Функциональные блоки
│ ├── [Section]Block.tsx # Секции интерфейса
│ └── [Action]Block.tsx # Блоки действий
├── hooks/ # Бизнес-логика
│ ├── use[Domain][Action].ts # Специфичная логика
│ ├── use[Entity]Management.ts # CRUD операции
│ └── use[Feature]State.ts # Управление состоянием
├── types/ # TypeScript типы
│ └── index.ts # Все интерфейсы модуля
└── constants/ # Константы (опционально)
└── index.ts # Конфигурация и моки
```
## 🔄 Процесс рефакторинга
### ЭТАП 1: Анализ и планирование
1. **Выделить основные секции UI**
- Определить логические блоки интерфейса
- Найти повторяющиеся паттерны
2. **Проанализировать состояния**
- Сгруппировать связанные useState
- Выделить логику управления состоянием
3. **Найти бизнес-логику**
- useEffect с API вызовами
- Обработчики форм и событий
- Вычисляемые значения
### ЭТАП 2: Создание типов
```typescript
// types/index.ts
export interface [Entity] {
id: string
// ... поля сущности
}
export interface [Component]Props {
// ... props компонента
}
export interface [Block]Props {
// ... props блока
}
```
### ЭТАП 3: Извлечение hooks
```typescript
// hooks/use[Feature].ts
export function use[Feature]() {
const [state, setState] = useState()
const handleAction = useCallback(() => {
// бизнес-логика
}, [dependencies])
return {
state,
handleAction,
// ... другие возвращаемые значения
}
}
```
### ЭТАП 4: Создание блок-компонентов
```typescript
// blocks/[Feature]Block.tsx
export const [Feature]Block = React.memo(function [Feature]Block({
// ... props
}: [Feature]BlockProps) {
return (
<div className="...">
{/* UI блока */}
</div>
)
})
```
### ЭТАП 5: Интеграция в главном компоненте
```typescript
// index.tsx
export function [Component]() {
// Подключение hooks
const featureA = useFeatureA()
const featureB = useFeatureB()
// Обработчики с useCallback
const handleAction = useCallback((data) => {
// координация между features
}, [featureA.method, featureB.method])
return (
<div>
<FeatureABlock {...featureA} onAction={handleAction} />
<FeatureBBlock {...featureB} onAction={handleAction} />
</div>
)
}
```
## ⚡ Оптимизация производительности
### Мемоизация компонентов
```typescript
// Все блоки обернуть в React.memo
export const MyBlock = React.memo(function MyBlock(props) {
// ...
})
// Главный компонент: useCallback для обработчиков
const handleEvent = useCallback(
(data) => {
// логика
},
[dependency1, dependency2],
)
```
### Управление ререндерами
```typescript
// В hooks: useMemo для тяжелых вычислений
const expensiveValue = useMemo(() => {
return heavyCalculation(data)
}, [data])
// Оптимизация зависимостей useCallback
const optimizedHandler = useCallback(
(item) => {
// вместо передачи всего массива
// передавать только нужные методы
},
[addItem, removeItem],
) // не [items, addItem, removeItem]
```
## 🧪 Тестирование модульной архитектуры
### Unit тесты для hooks
```typescript
import { renderHook, act } from '@testing-library/react'
import { useFeature } from '../hooks/useFeature'
describe('useFeature', () => {
it('should handle action correctly', () => {
const { result } = renderHook(() => useFeature())
act(() => {
result.current.handleAction('test')
})
expect(result.current.state).toEqual(expectedState)
})
})
```
### Component тесты для блоков
```typescript
import { render, screen } from '@testing-library/react'
import { FeatureBlock } from '../blocks/FeatureBlock'
describe('FeatureBlock', () => {
it('should render with props', () => {
render(<FeatureBlock data={mockData} onAction={mockHandler} />)
expect(screen.getByText('Expected Text')).toBeInTheDocument()
})
})
```
## 📊 Метрики успеха рефакторинга
### Количественные метрики
- **Размер главного файла**: ↓ на 70-85%
- **Количество модулей**: ↑ в 5-10 раз
- **Время компиляции**: ↓ на 90%+
- **Переиспользуемые компоненты**: ↑ с 0 до N
### Качественные улучшения
- ✅ Читаемость и понимание кода
- ✅ Простота добавления новых фич
- ✅ Изолированное тестирование
- ✅ Переиспользование в других местах
- ✅ Параллельная разработка команды
## 🚨 Частые ошибки и как их избежать
### ❌ Чрезмерное дробление
```typescript
// ПЛОХО: слишком много мелких блоков
<HeaderBlock />
<SubHeaderBlock />
<TitleBlock />
<ButtonBlock />
// ХОРОШО: логичные функциональные блоки
<HeaderSection />
<ActionsSection />
```
### ❌ Плохие зависимости useCallback
```typescript
// ПЛОХО: передача всего объекта
const handler = useCallback(() => {
doSomething(fullObject)
}, [fullObject]) // объект пересоздается каждый рендер
// ХОРОШО: передача только нужных значений
const handler = useCallback(() => {
doSomething(fullObject.id, fullObject.name)
}, [fullObject.id, fullObject.name])
```
### ❌ Неправильное разделение состояния
```typescript
// ПЛОХО: состояние остается в главном компоненте
function MainComponent() {
const [complexState, setComplexState] = useState() // управляется извне
return <FeatureBlock state={complexState} setState={setComplexState} />
}
// ХОРОШО: состояние инкапсулировано в hook
function MainComponent() {
const featureData = useFeature() // управляется внутри hook
return <FeatureBlock {...featureData} />
}
```
## 📋 Чек-лист рефакторинга
### Перед началом
- [ ] Компонент больше 800 строк
- [ ] Есть несколько логических секций UI
- [ ] Множественные useState и useEffect
- [ ] Активно развивающийся функционал
### Планирование
- [ ] Определены основные UI блоки (3-6 штук)
- [ ] Выделена бизнес-логика для hooks (2-5 штук)
- [ ] Созданы TypeScript интерфейсы
- [ ] План поэтапного рефакторинга готов
### Реализация
- [ ] Создана структура папок
- [ ] Extracted типы в types/index.ts
- [ ] Hooks реализованы и протестированы
- [ ] Блоки созданы с React.memo
- [ ] Главный компонент интегрирует все части
- [ ] Старый файл удален
### Оптимизация
- [ ] Все обработчики используют useCallback
- [ ] Зависимости useCallback оптимизированы
- [ ] Тяжелые вычисления в useMemo
- [ ] Компонент работает без ошибок
### Документация
- [ ] README для модуля создан
- [ ] Примеры использования hooks
- [ ] Документация props для блоков
- [ ] Архитектурная диаграмма
## 🎯 Заключение
Модульная архитектура значительно улучшает качество кода, скорость разработки и поддержки. Применяйте этот паттерн к большим компонентам постепенно, следуя принципам безопасного рефакторинга.
---
**Основано на**: Успешном рефакторинге create-suppliers-supply-page.tsx (1,467→240 строк)
**Автор паттерна**: Claude Code
**Дата**: Август 2025

View File

@ -0,0 +1,290 @@
# ПРАВИЛА ЛОГИСТИКИ, СТАТИСТИКИ И СКЛАДСКИХ СИСТЕМ
## 📊 АНАЛИТИЧЕСКИЕ ВЫВОДЫ ИЗ КОДА
### 🚛 1. ЛОГИСТИЧЕСКИЕ МОДУЛИ
#### 1.1 Логистическая Система Перевозок (LogisticsDashboard)
**Файл:** `src/components/logistics/logistics-dashboard.tsx`
**ОБНАРУЖЕННЫЕ ПРАВИЛА:**
- **Статусы маршрутов:** `planned`, `in_transit`, `delivered`, `cancelled`
- **Структура маршрута:** точка отправления → точка назначения
- **Обязательные поля:** номер маршрута, адреса, груз, цена, расстояние, время
- **Цветовая кодировка статусов:**
- Запланировано: синий (`text-blue-300 border-blue-400/30`)
- В пути: желтый (`text-yellow-300 border-yellow-400/30`)
- Доставлено: зеленый (`text-green-300 border-green-400/30`)
- Отменено: красный (`text-red-300 border-red-400/30`)
**КЛЮЧЕВЫЕ ТОЧКИ ДОСТАВКИ:**
- Садовод (14-й км МКАД)
- SFERAV Logistics (Складская, 15)
- Коледино WB (Подольск)
- Тверь Ozon (Складская, 88)
#### 1.2 Система Логистических Заказов (LogisticsOrdersDashboard)
**Файл:** `src/components/logistics-orders/logistics-orders-dashboard.tsx`
**WORKFLOW ЛОГИСТИКИ:**
```
SUPPLIER_APPROVED → LOGISTICS_CONFIRMED → SHIPPED → DELIVERED
↓ ↓ ↓ ↓
Требует Подтверждено В пути Доставлено
подтверждения логистом
```
**ДЕЙСТВИЯ ЛОГИСТА:**
- Подтверждение заказа (`LOGISTICS_CONFIRM_ORDER`)
- Отклонение заказа с причиной (`LOGISTICS_REJECT_ORDER`)
- Статистика по статусам
**ПРАВА ЛОГИСТА:**
- Подтверждать заказы поставщиков
- Отклонять заказы с указанием причины
- Просматривать детали маршрута и товаров
### 📈 2. СИСТЕМА СТАТИСТИКИ И АНАЛИТИКИ
#### 2.1 Статистика Селлера (SellerStatisticsDashboard)
**Файл:** `src/components/seller-statistics/seller-statistics-dashboard.tsx`
**АРХИТЕКТУРА КЭШИРОВАНИЯ:**
- **Локальный кэш:** Map для salesCache и advertisingCache
- **Database кэш:** через GraphQL мутации `SAVE_SELLER_STATS_CACHE`
- **Время жизни кэша:** 24 часа
- **Ключи кэша:** период + даты для custom диапазонов
**ВКЛАДКИ СТАТИСТИКИ:**
1. **Продажи** (SalesTab) - данные о продажах товаров
2. **Реклама** (AdvertisingTab) - рекламная статистика
3. **Иное** - зарезервировано для будущих функций
**ПЕРИОДЫ АНАЛИЗА:**
- Неделя (`week`)
- Месяц
- Квартал
- Пользовательский диапазон (`custom`)
#### 2.2 Статистика Фулфилмента (FulfillmentStatisticsDashboard)
**Файл:** `src/components/fulfillment-statistics/fulfillment-statistics-dashboard.tsx`
**БЛОКИ СТАТИСТИКИ:**
1. **Накопленная статистика:**
- Обработано товаров
- Выявлено брака
- Поставок получено
- Общий доход
2. **Отгрузка на площадки:**
- Wildberries (фиолетовый)
- Ozon (синий)
- Другие маркетплейсы (зеленый)
3. **Аналитика производительности:**
- Среднее время обработки
- Уровень брака
- Уровень возвратов
- Рейтинг качества
4. **AI-аналитика и прогнозы:**
- Прогноз роста (+23% в следующем квартале)
- Оптимизация процессов (-18% времени при автоматизации)
- Сезонные тренды (+45% в ноябре-декабре)
**УПРАВЛЕНИЕ БЛОКАМИ:**
- Все блоки сворачиваемые/разворачиваемые
- Состояние сохраняется в `expandedSections`
#### 2.3 Экономическая Система (Economics Modules)
**Файлы:** `src/components/economics/*.tsx`
**СПЕЦИАЛИЗАЦИЯ ПО ТИПАМ ОРГАНИЗАЦИЙ:**
- `fulfillment-economics-page.tsx` - экономика фулфилмента
- `logist-economics-page.tsx` - экономика логистики
- `seller-economics-page.tsx` - экономика селлера
- `wholesale-economics-page.tsx` - экономика оптовых поставщиков
- `economics-page-wrapper.tsx` - роутер по типу организации
### 🏬 3. СКЛАДСКИЕ СИСТЕМЫ
#### 3.1 Wildberries Warehouse (WBWarehouseDashboard)
**Файл:** `src/components/wb-warehouse/wb-warehouse-dashboard.tsx`
**ИНТЕГРАЦИЯ С WB API:**
- **Аутентификация:** через API ключи в `organization.apiKeys`
- **Валидация ключей:** проверка `marketplace === 'WILDBERRIES'` и `isActive`
- **Токен доступа:** из `validationData.token|apiKey|key`
**АЛГОРИТМ ЗАГРУЗКИ ДАННЫХ:**
1. Получение карточек товаров (`WildberriesService.getAllCards`)
2. Извлечение nmIds из карточек
3. Получение аналитики для каждого nmId (`getStocksReportByOffices`)
4. Комбинирование данных через `combineCardsWithIndividualAnalytics`
5. Кэширование результата через `SAVE_WB_WAREHOUSE_CACHE`
**СТРУКТУРА ДАННЫХ СКЛАДА:**
```typescript
interface WBStock {
nmId: number
vendorCode: string
title: string
brand: string
price: number
stocks: Array<{
warehouseId: number
warehouseName: string
quantity: number
quantityFull: number
inWayToClient: number
inWayFromClient: number
}>
totalQuantity: number
totalReserved: number
// ... media, characteristics, etc
}
```
**СТАТИСТИКА СКЛАДА:**
- Всего товаров
- Общий остаток
- Зарезервировано
- Возвраты от клиентов
- Активные склады
#### 3.2 Общий Склад (WarehouseDashboard)
**Файл:** `src/components/warehouse/warehouse-dashboard.tsx`
**ТОВАРНЫЕ ПОЗИЦИИ:**
- **Типы:** `PRODUCT` (товар) / `CONSUMABLE` (расходник)
- **Поля:** название, артикул, описание, цена, количество
- **Опциональные:** setQuantity, ordered, inTransit, stock, sold
**ИНТЕГРАЦИЯ С РЫНКАМИ:**
- **Поддерживаемые рынки:** `sadovod`, `tyak-moscow`
- **Цветовая кодировка:**
- Садовод: зеленый (`bg-green-500/20 text-green-300`)
- ТЯК Москва: синий (`bg-blue-500/20 text-blue-300`)
**РЕЖИМЫ ОТОБРАЖЕНИЯ:**
- **Карточки** (`cards`) - сетка карточек товаров
- **Таблица** (`table`) - табличное представление
**СТАТУСЫ ОСТАТКОВ:**
- 0 единиц: красный (`text-red-400`)
- < 10 единиц: желтый (`text-yellow-400`)
- 10 единиц: зеленый (`text-green-400`)
#### 3.3 Фулфилмент Склад (FulfillmentWarehouse)
**Файлы:** `src/components/fulfillment-warehouse/*.tsx`
**МОДУЛЬНАЯ АРХИТЕКТУРА:**
- **Основной:** `fulfillment-warehouse-dashboard.tsx`
- **Компоненты:** StatCard, TableHeader, блоки (17 файлов)
- **Специализированные:** поставки, статистика, претензии WB
**ФУНКЦИОНАЛЬНОСТЬ:**
- Управление поставками
- Статистика склада
- Обработка возвратов WB
- Детали доставки
## 🎯 ВЫЯВЛЕННЫЕ СТАНДАРТЫ И ПАТТЕРНЫ
### 1. АРХИТЕКТУРНЫЕ ПАТТЕРНЫ
#### Glass Morphism Design
```css
glass-card: "bg-white/10 backdrop-blur border-white/20"
glass-secondary: "bg-white/5 backdrop-blur border-white/10"
glass-input: прозрачные инпуты с размытием
```
#### Цветовая Система Статусов
- **Синий:** планирование, информация, подтверждение
- **Желтый:** ожидание, предупреждения, в процессе
- **Зеленый:** успех, доставлено, высокие показатели
- **Красный:** ошибки, отмены, критические состояния
- **Фиолетовый:** премиум функции, аналитика, WB
#### Система Кэширования
```javascript
// Паттерн многоуровневого кэша:
1. Local State (Map/useState)
2. GraphQL Cache (Apollo)
3. Database Cache (через мутации)
4. External API (последний ресурс)
```
### 2. БИЗНЕС-ПРАВИЛА
#### Workflow Логистики
```
Поставщик → Логист → Фулфилмент → Маркетплейс → Клиент
↓ ↓ ↓ ↓ ↓
Заказ → Подтверждение → Отгрузка → Доставка → Возврат
```
#### Роли в Логистике
- **Поставщик:** создает заказ поставки
- **Логист:** подтверждает/отклоняет, организует доставку
- **Фулфилмент:** принимает груз, обрабатывает товары
- **Система:** автоматически отслеживает статусы
#### Экономические Правила
- Каждый тип организации имеет свой экономический модуль
- Данные кэшируются на 24 часа
- Поддерживаются пользовательские временные диапазоны
- AI-аналитика предоставляет прогнозы и рекомендации
### 3. ИНТЕГРАЦИОННЫЕ ПРАВИЛА
#### Маркетплейсы
- **Wildberries:** полная интеграция через API, поддержка складов, аналитика
- **Ozon:** поддержка в workflow, данные статистики
- **Другие:** Яндекс.Маркет, Авито (ограниченная поддержка)
#### Склады и Рынки
- **Садовод:** зеленая палитра, первичные поставщики
- **ТЯК Москва:** синяя палитра, альтернативные поставщики
- **WB Склады:** автоматическое определение из API данных
## 🚀 ТЕХНИЧЕСКИЕ ТРЕБОВАНИЯ
### Обязательные Хуки
- `useSidebar()` - управление боковой панелью
- `useAuth()` - аутентификация и права доступа
- Для WB: проверка `hasWBApiKey` перед загрузкой
### GraphQL Операции
**Логистика:**
- `GET_SUPPLY_ORDERS` - получение заказов
- `LOGISTICS_CONFIRM_ORDER` - подтверждение логистом
- `LOGISTICS_REJECT_ORDER` - отклонение с причиной
**Статистика:**
- `GET_SELLER_STATS_CACHE` - кэш статистики селлера
- `SAVE_SELLER_STATS_CACHE` - сохранение кэша
- `GET_WB_WAREHOUSE_DATA` - данные склада WB
**Склады:**
- `GET_MY_PRODUCTS` - товары пользователя
- `SAVE_WB_WAREHOUSE_CACHE` - кэш WB склада
### Внешние Сервисы
- **WildberriesService:** интеграция с API WB
- **Токены:** хранение в `organization.apiKeys`
- **Rate Limiting:** 1 секунда между запросами для WB API
## 📋 ВЫВОДЫ И РЕКОМЕНДАЦИИ
1. **Логистическая система** полноценно реализована с workflow и статусами
2. **Статистические модули** используют сложное многоуровневое кэширование
3. **Складские системы** имеют разную степень интеграции (WB - полная, остальные - базовая)
4. **Экономические модули** специализированы по типам организаций
5. **Дизайн-система** консистентна во всех модулях
**СЛЕДУЮЩИЕ ШАГИ:**
- Документировать API эндпоинты
- Описать административную систему
- Создать руководства по интеграции

View File

@ -902,11 +902,11 @@ function getMarketLabel(market: string): string {
}
```
#### **2. ОПЦИОНАЛЬНЫЕ ПОЛЯ УПАКОВКИ ПРИ ОДОБРЕНИИ:**
#### **2. ОПЦИОНАЛЬНЫЕ ПАРАМЕТРЫ ПОСТАВКИ ПРИ ОДОБРЕНИИ:**
```jsx
{
/* ОПЦИОНАЛЬНЫЕ поля для поставщика - отображаются сразу при одобрении заказа */
/* ОПЦИОНАЛЬНЫЕ параметры поставки для поставщика - отображаются при одобрении заказа */
}
;<div className="grid grid-cols-2 gap-4">
<div>
@ -918,7 +918,7 @@ function getMarketLabel(market: string): string {
aria-describedby="packages-help"
/>
<p id="packages-help" className="text-xs text-white/60 mt-1">
Используется логистикой для расчета тарифов
Параметр поставки для логистических расчетов
</p>
</div>
@ -926,7 +926,27 @@ function getMarketLabel(market: string): string {
<Label htmlFor="volume">Объем груза (м³)</Label>
<Input id="volume" type="number" step="0.01" placeholder="0.00 (опционально)" aria-describedby="volume-help" />
<p id="volume-help" className="text-xs text-white/60 mt-1">
Помогает логистике в планировании маршрутов
Параметр поставки для планирования маршрутов
</p>
</div>
<div>
<Label htmlFor="deliveryDate">Дата поставки</Label>
<GlassDatePicker
id="deliveryDate"
placeholder="Выберите дату поставки"
aria-describedby="delivery-help"
/>
<p id="delivery-help" className="text-xs text-white/60 mt-1">
Основной параметр поставки - когда товары должны быть доставлены
</p>
</div>
<div>
<Label htmlFor="totalAmount">Общая стоимость товаров</Label>
<Input id="totalAmount" type="number" readOnly className="bg-white/5" />
<p className="text-xs text-white/60 mt-1">
Ключевой параметр поставки - автоматически рассчитывается
</p>
</div>