# WORKFLOW ЦЕПОЧКИ ПОСТАВОК СИСТЕМЫ SFERA

## 🎯 ОБЗОР СИСТЕМЫ

Система поставок SFERA работает по 8-статусной модели с участием 4 типов организаций:

- **SELLER** - инициатор поставки
- **WHOLESALE** - поставщик товаров
- **LOGIST** - доставка
- **FULFILLMENT** - получатель и обработчик

## 🔄 СТАТУСЫ ПОСТАВОК (SupplyOrderStatus)

```mermaid
graph TD
    A[PENDING] --> B[SUPPLIER_APPROVED]
    A --> X[CANCELLED]
    B --> C[LOGISTICS_CONFIRMED]
    B --> X
    C --> D[SHIPPED]
    C --> X
    D --> E[DELIVERED]
    D --> X

    F[CONFIRMED*] -.-> B
    G[IN_TRANSIT*] -.-> D

    style F fill:#f9f,stroke:#333,stroke-dasharray: 5 5
    style G fill:#f9f,stroke:#333,stroke-dasharray: 5 5
```

\*Устаревшие статусы для обратной совместимости

### 📋 ДЕТАЛЬНОЕ ОПИСАНИЕ СТАТУСОВ

#### 1. PENDING (Ожидает одобрения поставщика)

- **Инициатор**: SELLER создает заказ поставки
- **Ответственный**: WHOLESALE (поставщик)
- **Действия**:
  - Поставщик проверяет наличие товаров
  - Подтверждает возможность поставки
  - Может отклонить заказ → CANCELLED

#### 2. SUPPLIER_APPROVED (Поставщик одобрил)

- **Предыдущий статус**: PENDING
- **Ответственный**: LOGIST (логистика)
- **Действия**:
  - Логистика рассчитывает маршрут и стоимость
  - Подтверждает возможность доставки
  - Планирует график забора/доставки

**GraphQL мутация подтверждения поставщиком:**

```graphql
# Поставщик указывает детали упаковки при одобрении (опционально)
mutation SupplierApproveOrderWithPackaging($id: ID!, $packagesCount: Int, $volume: Float) {
  supplierApproveOrderWithPackaging(
    id: $id
    packagesCount: $packagesCount # Опционально: количество грузовых мест
    volume: $volume # Опционально: объём в м³ для расчета логистических тарифов
  ) {
    success
    message
    order {
      id
      status
      packagesCount # null если не указано
      volume # null если не указано
    }
  }
}
```

#### 3. LOGISTICS_CONFIRMED (Логистика подтвердила)

- **Предыдущий статус**: SUPPLIER_APPROVED
- **Ответственный**: WHOLESALE (поставщик)
- **Действия**:
  - Поставщик готовит товары к отгрузке
  - Упаковывает заказ
  - Передает логистике

**Реальная мутация подтверждения логистикой:**

```typescript
// Из src/graphql/resolvers/logistics.ts
logisticsConfirmOrder: async (_: unknown, args: { id: string }, context: Context) => {
  if (!context.user) {
    throw new GraphQLError('Требуется авторизация')
  }

  const currentUser = await prisma.user.findUnique({
    where: { id: context.user.id },
    include: { organization: true },
  })

  // Проверка, что это логистическая компания
  if (currentUser.organization.type !== 'LOGIST') {
    throw new GraphQLError('Только логистические компании могут подтверждать заказы')
  }

  // Ищем заказ где мы назначены логистикой
  const existingOrder = await prisma.supplyOrder.findFirst({
    where: {
      id: args.id,
      logisticsPartnerId: currentUser.organization.id, // Мы - назначенная логистика
      status: 'SUPPLIER_APPROVED', // Поставщик уже одобрил
    },
  })

  if (!existingOrder) {
    throw new GraphQLError('Заказ не найден или нет доступа')
  }

  // Обновляем статус на LOGISTICS_CONFIRMED
  const updatedOrder = await prisma.supplyOrder.update({
    where: { id: args.id },
    data: { status: 'LOGISTICS_CONFIRMED' },
  })

  return {
    success: true,
    message: 'Заказ подтвержден логистикой',
    order: updatedOrder,
  }
}
```

#### 4. SHIPPED (Отправлено поставщиком)

- **Предыдущий статус**: LOGISTICS_CONFIRMED
- **Ответственный**: LOGIST (в пути)
- **Действия**:
  - Товар забран у поставщика
  - Доставка по маршруту к фулфилменту
  - Трекинг перемещения

#### 5. DELIVERED (Доставлено и принято)

- **Предыдущий статус**: SHIPPED
- **Ответственный**: FULFILLMENT
- **Действия**:
  - Приемка товаров на складе
  - Проверка качества и количества
  - Размещение на складе
  - **ЗАВЕРШЕНИЕ WORKFLOW**

**Реальная реализация перехода SHIPPED → DELIVERED:**

```typescript
// Мутация фулфилмента для приемки товаров (из реального кода)
fulfillmentReceiveOrder: async (_: unknown, args: { id: string }, context: Context) => {
  // Проверка авторизации
  if (!context.user) {
    throw new GraphQLError('Требуется авторизация')
  }

  const currentUser = await prisma.user.findUnique({
    where: { id: context.user.id },
    include: { organization: true },
  })

  // Проверка, что это заказ для нашего фулфилмент-центра
  const existingOrder = await prisma.supplyOrder.findFirst({
    where: {
      id: args.id,
      fulfillmentCenterId: currentUser.organization.id, // Мы - получатель
      status: 'SHIPPED', // Должен быть в пути
    },
  })

  if (!existingOrder) {
    throw new GraphQLError('Заказ не найден или нет доступа')
  }

  // Обновляем статус на DELIVERED
  const updatedOrder = await prisma.supplyOrder.update({
    where: { id: args.id },
    data: { status: 'DELIVERED' },
  })

  return {
    success: true,
    message: 'Заказ успешно принят на складе',
    order: updatedOrder,
  }
}
```

#### 6. CANCELLED (Отменено)

- **Может произойти на любом этапе**
- **Инициатор**: Любой участник процесса
- **Причины**:
  - Отсутствие товаров у поставщика
  - Невозможность доставки
  - Изменение планов селлера
  - **ЗАВЕРШЕНИЕ WORKFLOW**

## 🔄 ПРАВИЛА ПЕРЕХОДОВ МЕЖДУ СТАТУСАМИ

### РАЗРЕШЕННЫЕ ПЕРЕХОДЫ:

```typescript
const allowedTransitions = {
  PENDING: ['SUPPLIER_APPROVED', 'CANCELLED'],
  SUPPLIER_APPROVED: ['LOGISTICS_CONFIRMED', 'CANCELLED'],
  LOGISTICS_CONFIRMED: ['SHIPPED', 'CANCELLED'],
  SHIPPED: ['DELIVERED', 'CANCELLED'],
  DELIVERED: [], // Финальный статус
  CANCELLED: [], // Финальный статус
}
```

### ЗАПРЕЩЕННЫЕ ДЕЙСТВИЯ:

- ❌ Возврат к предыдущим статусам
- ❌ Пропуск промежуточных статусов
- ❌ Изменение DELIVERED/CANCELLED заказов

## 🏢 РОЛИ И ОТВЕТСТВЕННОСТЬ

### SELLER (Селлер-инициатор)

**Создание заказа:**

```typescript
// Создание поставки селлером
createSupplyOrder(input: {
  partnerId: ID!           // Поставщик (WHOLESALE)
  deliveryDate: DateTime!  // Желаемая дата доставки
  fulfillmentCenterId: ID  // Фулфилмент-получатель
  logisticsPartnerId: ID   // Логистика (опционально)
})
```

**Возможности:**

- ✅ Создавать новые заказы поставок
- ✅ Отменять свои заказы (→ CANCELLED)
- ✅ Просматривать статус поставок
- ❌ Изменять статусы напрямую

### WHOLESALE (Поставщик)

**Обработка входящих заказов:**

```typescript
// Поставщик получает заказы где он является поставщиком
const supplierOrders = await prisma.supplyOrder.findMany({
  where: {
    partnerId: currentUser.organization.id, // Мы - поставщик
    status: 'PENDING', // Ожидает подтверждения
  },
})
```

**Действия поставщика:**

```graphql
# Одобрение заказа
mutation SupplierApproveOrder($orderId: ID!) {
  supplierApproveOrder(id: $orderId) {
    success
    order {
      id
      status
    } # PENDING → SUPPLIER_APPROVED
  }
}

# Отклонение заказа
mutation SupplierRejectOrder($orderId: ID!, $reason: String) {
  supplierRejectOrder(id: $orderId, reason: $reason) {
    success
    message
  }
}

# Отгрузка товара (после подтверждения логистики)
mutation SupplierShipOrder($orderId: ID!) {
  supplierShipOrder(id: $orderId) {
    success
    order {
      id
      status
    } # LOGISTICS_CONFIRMED → SHIPPED
  }
}
```

**Компоненты поставщика:**

```typescript
// Техническая реализация кабинета поставщика
src/components/supplier-orders/
├── supplier-orders-dashboard.tsx  # Главный dashboard
├── supplier-order-card.tsx       # Карточка заказа
├── supplier-orders-tabs.tsx      # Табы по статусам
├── supplier-orders-search.tsx    # Поиск и фильтры
└── supplier-order-stats.tsx      # Статистика заказов
```

**Возможности:**

- ✅ Просматривать входящие заказы (PENDING)
- ✅ Одобрять заказы (PENDING → SUPPLIER_APPROVED)
- ✅ Отклонять заказы (PENDING → CANCELLED)
- ✅ Отгружать товары (LOGISTICS_CONFIRMED → SHIPPED)
- ❌ Изменять детали заказа после создания
- ❌ Видеть заказы других поставщиков

## 🚨 КРИТИЧЕСКИЕ ПРОБЛЕМЫ WORKFLOW

### ВЫЯВЛЕННЫЕ ПРОБЛЕМЫ В ЦЕПОЧКЕ ПОСТАВОК:

#### ❌ **ПРОБЛЕМА 1: Неправильное отображение статусов у поставщика**

```typescript
// ПРОБЛЕМА: Поставщик видит "ожидает подтверждения" вместо только кнопок
// РЕШЕНИЕ: Показывать только кнопки действий, скрывать статусы

// Текущий код (неправильно):
<StatusBadge status={order.status} />
<ActionButtons />

// Правильный код:
{user?.organization?.type === 'WHOLESALE' ? (
  <ActionButtons only /> // Только кнопки, без статуса
) : (
  <StatusBadge status={order.status} />
)}
```

#### ❌ **ПРОБЛЕМА 2: Отсутствие полей ввода у поставщика**

```typescript
// ПРОБЛЕМА: Поставщик не может указать важные данные при одобрении
interface SupplierPackagingFields {
  packagesCount?: number   // ОПЦИОНАЛЬНО: Количество грузовых мест
  volume?: number         // ОПЦИОНАЛЬНО: Объем груза для логистических расчетов
  readyDate?: DateTime    // ОПЦИОНАЛЬНО: Дата готовности к отгрузке
  notes?: string         // ОПЦИОНАЛЬНО: Комментарии для логистики
}

// ТРЕБОВАНИЯ:
// ✅ Поля НЕ обязательные - заказ можно одобрить без них
// ✅ Показываются сразу при одобрении для удобства заполнения
// ✅ Используются логистикой для расчета тарифов и планирования
// ✅ Отображаются на 1-м уровне визуализации поставки

// РЕШЕНИЕ: Расширить мутацию supplierApproveOrder
mutation SupplierApproveOrder($input: SupplierApprovalInput!) {
  supplierApproveOrder(input: $input) {
    success
    order {
      id, status, packagesCount, volume, readyDate, notes
    }
  }
}
```

#### ❌ **ПРОБЛЕМА 3: Конфликт статусов в приемке фулфилмента**

```typescript
// КРИТИЧЕСКАЯ ОШИБКА: Резолвер ожидает SHIPPED, но получает SUPPLIER_APPROVED
if (supplyOrder.status !== 'SHIPPED') {
  return {
    success: false,
    message: 'Заказ должен быть в статусе SHIPPED для приемки', // ❌ БЛОКИРУЕТ ПРОЦЕСС
  }
}

// РЕШЕНИЕ: Исправить проверку статуса
if (!['SUPPLIER_APPROVED', 'LOGISTICS_CONFIRMED'].includes(supplyOrder.status)) {
  return {
    success: false,
    message: 'Заказ должен быть одобрен поставщиком для приемки',
  }
}
```

#### ❌ **ПРОБЛЕМА 4: Отсутствие уведомлений поставщика**

```typescript
// ПРОБЛЕМА: Поставщик не знает о новых заказах в реальном времени
// РЕШЕНИЕ: Добавить систему уведомлений

interface SupplierNotifications {
  newOrder: 'Новый заказ от {sellerName} на сумму {amount}'
  orderCancelled: 'Заказ #{orderNumber} отменен заказчиком'
  logistics: 'Логистика подтверждена для заказа #{orderNumber}'
}
```

### ПЛАН ИСПРАВЛЕНИЯ WORKFLOW:

```typescript
interface WorkflowFixes {
  // Фаза 1: UI поставщика
  supplierInterface: {
    hideStatuses: 'Показывать только кнопки действий'
    addFields: 'Поля для packagesCount, volume, readyDate'
    realtime: 'Уведомления о новых заказах'
  }

  // Фаза 2: Backend логика
  backendLogic: {
    expandMutation: 'Расширить supplierApproveOrder с дополнительными полями'
    fixStatusCheck: 'Исправить проверку статусов в fulfillmentReceiveOrder'
    notifications: 'Система реалтайм уведомлений'
  }

  // Фаза 3: Интеграция
  integration: {
    validation: 'Валидация минимальных количеств заказа'
    inventory: 'Проверка доступности товаров у поставщика'
    logistics: 'Автоматическое назначение логистики'
  }
}
```

### ТРЕБОВАНИЯ К РЕАЛИЗАЦИИ:

```typescript
// 1. Исправленная фильтрация заказов для поставщика
const fixedSupplierFilter = `
  if (currentUser.organization.type === 'WHOLESALE') {
    whereClause = {
      partnerId: currentUser.organization.id, // Мы - поставщик
    }
  } else {
    whereClause = {
      organizationId: currentUser.organization.id, // Мы - заказчик
    }
  }
`

// 2. Правильная обработка статусов
const correctStatusHandling = `
  // Поставщик видит только кнопки, без статусов
  {userRole === 'WHOLESALE' && status === 'PENDING' && (
    <ApproveRejectButtons orderId={order.id} />
  )}
  
  // Остальные видят статусы
  {userRole !== 'WHOLESALE' && (
    <StatusBadge status={status} />
  )}
`
```

```typescript
// Из кода resolvers.ts:
const incomingSupplierOrders = await prisma.supplyOrder.count({
  where: {
    partnerId: currentUser.organization.id, // Мы - поставщик
    status: 'PENDING', // Ожидает подтверждения от поставщика
  },
})
```

**Возможности:**

- ✅ PENDING → SUPPLIER_APPROVED (подтверждение заказа)
- ✅ LOGISTICS_CONFIRMED → SHIPPED (отгрузка товара)
- ✅ Отменять заказы (→ CANCELLED)
- ❌ Минуя логистические этапы

### LOGIST (Логистика)

**Обработка подтвержденных заказов:**

```typescript
// Из кода resolvers.ts:
const logisticsOrders = await prisma.supplyOrder.count({
  where: {
    logisticsPartnerId: currentUser.organization.id, // Мы - логистика
    status: {
      in: [
        'CONFIRMED', // Устаревший - для совместимости
        'SUPPLIER_APPROVED', // Ждет подтверждения логистики
        'LOGISTICS_CONFIRMED', // Подтверждено - нужно забрать товар
      ],
    },
  },
})
```

**Возможности:**

- ✅ SUPPLIER_APPROVED → LOGISTICS_CONFIRMED (подтверждение логистики)
- ✅ Планирование маршрутов доставки
- ✅ Отменять заказы (→ CANCELLED)
- ❌ Изменение статусов поставщика

### FULFILLMENT (Получатель)

**Приемка товаров:**

```typescript
// Фулфилмент получает:
// 1. Свои заказы расходников (ourSupplyOrders)
// 2. Заказы от селлеров (sellerSupplyOrders)
```

**Возможности:**

- ✅ SHIPPED → DELIVERED (приемка товаров)
- ✅ Контроль качества и количества
- ✅ Отменять заказы (→ CANCELLED)
- ❌ Вмешательство в процесс до доставки

## 📊 ТИПЫ ПОСТАВОК ПО КОНТЕНТУ

### FULFILLMENT_CONSUMABLES

**Описание**: Расходники для операций фулфилмента

- **Инициатор**: FULFILLMENT заказывает у WHOLESALE
- **Назначение**: Операционные нужды (упаковка, маркировка, etc.)
- **Склад**: Остается на складе фулфилмента

### SELLER_CONSUMABLES

**Описание**: Расходники селлеров на хранении

- **Инициатор**: SELLER заказывает у WHOLESALE
- **Назначение**: Компоненты для продуктов селлера
- **Склад**: Размещается на складе фулфилмента для селлера

### PRODUCTS (Товары селлеров)

**Описание**: Готовые товары для отправки на маркетплейсы

- **Инициатор**: SELLER заказывает у WHOLESALE
- **Назначение**: Пополнение товарного запаса
- **Склад**: Готовые к отправке товары

## ⚠️ КРИТИЧЕСКИЕ ПРАВИЛА WORKFLOW

### 1. ПРИНЦИП ОТВЕТСТВЕННОСТИ

> Каждый статус имеет единственного ответственного за переход к следующему

### 2. ПРИНЦИП НЕОБРАТИМОСТИ

> Невозможно вернуться к предыдущим статусам - только вперед или отмена

### 3. ПРИНЦИП ПРОЗРАЧНОСТИ

> Все участники видят текущий статус и следующие шаги

### 4. ПРИНЦИП АВТОНОМНОСТИ

> Каждый участник может отменить заказ на своем этапе

## 🔍 LEGACY СТАТУСЫ (Обратная совместимость)

### CONFIRMED (устаревший)

- **Маппинг**: → SUPPLIER_APPROVED
- **Причина**: Переименование для ясности
- **Использование**: Только в старых записях БД

### IN_TRANSIT (устаревший)

- **Маппинг**: → SHIPPED
- **Причина**: Более точное описание статуса
- **Использование**: Только в старых записях БД

## 🚀 ДЕТАЛЬНЫЕ МУТАЦИИ WORKFLOW (РЕАЛЬНЫЙ КОД)

### Создание поставки (createSupplyOrder)

```typescript
// Полная реализация из resolvers.ts:4828-4927
createSupplyOrder: async (_: unknown, args: { input: SupplyOrderInput }, context: Context) => {
  console.warn('🚀 CREATE_SUPPLY_ORDER RESOLVER - ВЫЗВАН:', {
    hasUser: !!context.user,
    userId: context.user?.id,
    inputData: args.input,
    timestamp: new Date().toISOString(),
  })

  if (!context.user) {
    throw new GraphQLError('Требуется авторизация')
  }

  const currentUser = await prisma.user.findUnique({
    where: { id: context.user.id },
    include: { organization: true },
  })

  // Проверка типа организации
  const allowedTypes = ['FULFILLMENT', 'SELLER', 'LOGIST']
  if (!allowedTypes.includes(currentUser.organization.type)) {
    throw new GraphQLError('Заказы поставок недоступны для данного типа организации')
  }

  // Определяем роль организации в процессе поставки
  const organizationRole = currentUser.organization.type
  let fulfillmentCenterId = args.input.fulfillmentCenterId

  // Если заказ создает фулфилмент-центр, он сам является получателем
  if (organizationRole === 'FULFILLMENT') {
    fulfillmentCenterId = currentUser.organization.id
  }

  // Проверяем существование фулфилмент-центра
  if (fulfillmentCenterId) {
    const fulfillmentCenter = await prisma.organization.findFirst({
      where: {
        id: fulfillmentCenterId,
        type: 'FULFILLMENT',
      },
    })

    if (!fulfillmentCenter) {
      return {
        success: false,
        message: 'Указанный фулфилмент-центр не найден',
      }
    }
  }

  // Создание заказа с проверкой партнерских связей...
}
```

### Универсальное обновление статуса (updateSupplyOrderStatus)

```typescript
// Реализация из resolvers.ts:6900-6950
updateSupplyOrderStatus: async (_: unknown, args: { id: string; status: SupplyOrderStatus }, context: Context) => {
  console.warn(`[DEBUG] updateSupplyOrderStatus вызван для заказа ${args.id} со статусом ${args.status}`)

  if (!context.user) {
    throw new GraphQLError('Требуется авторизация')
  }

  const currentUser = await prisma.user.findUnique({
    where: { id: context.user.id },
    include: { organization: true },
  })

  // Находим заказ поставки с проверкой доступа
  const existingOrder = await prisma.supplyOrder.findFirst({
    where: {
      id: args.id,
      OR: [
        { organizationId: currentUser.organization.id }, // Создатель заказа
        { partnerId: currentUser.organization.id }, // Поставщик
        { fulfillmentCenterId: currentUser.organization.id }, // Фулфилмент-центр
        { logisticsPartnerId: currentUser.organization.id }, // Логистика
      ],
    },
    include: {
      items: {
        include: {
          product: {
            include: { category: true },
          },
        },
      },
      organization: true,
      partner: true,
      fulfillmentCenter: true,
      logisticsPartner: true,
    },
  })

  if (!existingOrder) {
    return {
      success: false,
      message: 'Заказ не найден или нет доступа к этому заказу',
    }
  }

  // БИЗНЕС-ПРАВИЛА ПЕРЕХОДОВ СТАТУСОВ
  const validateStatusTransition = (currentStatus: string, newStatus: string, userOrgType: string) => {
    const transitions = {
      PENDING: {
        SUPPLIER_APPROVED: ['WHOLESALE'], // Только поставщик может одобрить
        CANCELLED: ['SELLER', 'WHOLESALE', 'FULFILLMENT'], // Участники могут отменить
      },
      SUPPLIER_APPROVED: {
        LOGISTICS_CONFIRMED: ['LOGIST'], // Только логистика может подтвердить
        CANCELLED: ['WHOLESALE', 'LOGIST', 'FULFILLMENT'],
      },
      LOGISTICS_CONFIRMED: {
        SHIPPED: ['WHOLESALE'], // Только поставщик может отгрузить
        CANCELLED: ['WHOLESALE', 'LOGIST', 'FULFILLMENT'],
      },
      SHIPPED: {
        DELIVERED: ['FULFILLMENT'], // Только фулфилмент может принять
        CANCELLED: ['LOGIST', 'FULFILLMENT'], // В крайних случаях
      },
    }

    const allowedRoles = transitions[currentStatus]?.[newStatus]
    if (!allowedRoles || !allowedRoles.includes(userOrgType)) {
      throw new GraphQLError(`Переход ${currentStatus} → ${newStatus} недоступен для организации типа ${userOrgType}`)
    }
  }

  // Валидируем переход статуса
  validateStatusTransition(existingOrder.status, args.status, currentUser.organization.type)

  // Обновляем статус заказа
  const updatedOrder = await prisma.supplyOrder.update({
    where: { id: args.id },
    data: { status: args.status },
    include: {
      items: {
        include: {
          product: {
            include: { category: true },
          },
        },
      },
      organization: true,
      partner: true,
      fulfillmentCenter: true,
      logisticsPartner: true,
    },
  })

  return {
    success: true,
    message: `Статус заказа успешно изменен на ${args.status}`,
    order: updatedOrder,
  }
}
```

### Подтверждение логистики (logisticsConfirmOrder)

```typescript
// Реализация из resolvers.ts:7681-7720
logisticsConfirmOrder: async (_: unknown, args: { id: string }, context: Context) => {
  if (!context.user) {
    throw new GraphQLError('Требуется авторизация')
  }

  const currentUser = await prisma.user.findUnique({
    where: { id: context.user.id },
    include: { organization: true },
  })

  // ПРОВЕРКА РОЛИ: только логистические компании
  if (currentUser.organization.type !== 'LOGIST') {
    throw new GraphQLError('Только логистические компании могут подтверждать заказы')
  }

  // Ищем заказ где мы назначены логистикой
  const existingOrder = await prisma.supplyOrder.findFirst({
    where: {
      id: args.id,
      logisticsPartnerId: currentUser.organization.id, // Мы - назначенная логистика
      status: 'SUPPLIER_APPROVED', // Поставщик уже одобрил
    },
    include: {
      organization: true,
      partner: true,
      fulfillmentCenter: true,
    },
  })

  if (!existingOrder) {
    return {
      success: false,
      message: 'Заказ не найден, не назначен вашей компании, или находится в неподходящем статусе',
    }
  }

  // БИЗНЕС-ЛОГИКА: обновляем статус на LOGISTICS_CONFIRMED
  const updatedOrder = await prisma.supplyOrder.update({
    where: { id: args.id },
    data: { status: 'LOGISTICS_CONFIRMED' },
    include: {
      items: {
        include: {
          product: true,
        },
      },
      organization: true,
      partner: true,
      fulfillmentCenter: true,
      logisticsPartner: true,
    },
  })

  return {
    success: true,
    message: 'Заказ подтвержден логистической компанией. Поставщик может приступать к отгрузке.',
    order: updatedOrder,
  }
}
```

### Создание поставки Wildberries (createWildberriesSupply)

```typescript
// Специализированная мутация для маркетплейса WB (из resolvers.ts:6772-6800)
createWildberriesSupply: async (_: unknown, args: { input: WildberriesSupplyInput }, context: Context) => {
  if (!context.user) {
    throw new GraphQLError('Требуется авторизация')
  }

  const currentUser = await prisma.user.findUnique({
    where: { id: context.user.id },
    include: { organization: true },
  })

  if (!currentUser?.organization) {
    throw new GraphQLError('У пользователя нет организации')
  }

  // ПРОВЕРКА ТИПА: только селлеры могут создавать поставки WB
  if (currentUser.organization.type !== 'SELLER') {
    throw new GraphQLError('Поставки Wildberries доступны только для селлеров')
  }

  try {
    // БИЗНЕС-ЛОГИКА: создание специализированной поставки для WB
    const supplyData = {
      organizationId: currentUser.organization.id,
      type: 'WILDBERRIES_SUPPLY',
      status: 'PENDING',
      cards: args.input.cards.map((card) => ({
        price: card.price,
        discountedPrice: card.discountedPrice,
        selectedQuantity: card.selectedQuantity,
        selectedServices: card.selectedServices || [],
      })),
      createdAt: new Date(),
    }

    // Интеграция с API Wildberries для создания поставки...

    return {
      success: true,
      message: 'Поставка Wildberries успешно создана',
      supply: supplyData,
    }
  } catch (error) {
    console.error('Ошибка создания поставки WB:', error)
    return {
      success: false,
      message: 'Ошибка при создании поставки Wildberries',
    }
  }
}
```

## 📋 СИСТЕМА СЧЕТЧИКОВ ПО РОЛЯМ

### Динамические счетчики для UI (из реального кода)

```typescript
// Логика подсчета pending заказов по типам организаций (resolvers.ts:850-950)
let pendingSupplyOrders = 0

if (currentUser.organization.type === 'FULFILLMENT') {
  // ДЛЯ ФУЛФИЛМЕНТА: собственные + заказы от селлеров
  const ourSupplyOrders = await prisma.supplyOrder.count({
    where: {
      organizationId: currentUser.organization.id, // Мы создали заказ
      status: { in: ['PENDING', 'SUPPLIER_APPROVED', 'LOGISTICS_CONFIRMED', 'SHIPPED'] },
    },
  })

  const sellerSupplyOrders = await prisma.supplyOrder.count({
    where: {
      fulfillmentCenterId: currentUser.organization.id, // Мы - получатель
      organizationId: { not: currentUser.organization.id }, // Не наши заказы
      status: { in: ['PENDING', 'SUPPLIER_APPROVED', 'LOGISTICS_CONFIRMED', 'SHIPPED'] },
    },
  })

  pendingSupplyOrders = ourSupplyOrders + sellerSupplyOrders
} else if (currentUser.organization.type === 'WHOLESALE') {
  // ДЛЯ ПОСТАВЩИКА: входящие заказы для подтверждения
  const incomingSupplierOrders = await prisma.supplyOrder.count({
    where: {
      partnerId: currentUser.organization.id, // Мы - поставщик
      status: 'PENDING', // Ожидает подтверждения от поставщика
    },
  })

  pendingSupplyOrders = incomingSupplierOrders
} else if (currentUser.organization.type === 'LOGIST') {
  // ДЛЯ ЛОГИСТИКИ: заказы требующие действий
  const logisticsOrders = await prisma.supplyOrder.count({
    where: {
      logisticsPartnerId: currentUser.organization.id, // Мы - логистика
      status: {
        in: [
          'CONFIRMED', // Legacy: Подтверждено фулфилментом
          'SUPPLIER_APPROVED', // Подтверждено поставщиком - нужно подтвердить логистикой
          'LOGISTICS_CONFIRMED', // Подтверждено логистикой - нужно забрать товар
        ],
      },
    },
  })

  pendingSupplyOrders = logisticsOrders
} else if (currentUser.organization.type === 'SELLER') {
  // ДЛЯ СЕЛЛЕРА: созданные заказы в процессе
  const sellerOrders = await prisma.supplyOrder.count({
    where: {
      organizationId: currentUser.organization.id, // Мы создали заказ
      status: { in: ['PENDING', 'SUPPLIER_APPROVED', 'LOGISTICS_CONFIRMED', 'SHIPPED'] },
    },
  })

  pendingSupplyOrders = sellerOrders
}
```

## 🔄 РАСШИРЕННЫЕ ПРАВИЛА СТАТУСНЫХ ПЕРЕХОДОВ

### Матрица доступных действий

```typescript
// Карта доступных действий по статусам и ролям
const statusActionMatrix = {
  PENDING: {
    WHOLESALE: ['approve', 'cancel', 'add_packaging_details'], // Поставщик может одобрить или отменить
    SELLER: ['cancel', 'modify'], // Селлер может отменить или изменить
    FULFILLMENT: ['cancel'], // ФФ может отменить свои заказы
    LOGIST: [], // Логистика не участвует на этом этапе
  },

  SUPPLIER_APPROVED: {
    WHOLESALE: ['cancel', 'update_packaging'], // Поставщик может отменить или уточнить упаковку
    LOGIST: ['confirm', 'cancel', 'set_route'], // Логистика может подтвердить или отменить
    SELLER: ['cancel'], // Селлер может отменить
    FULFILLMENT: ['cancel'], // ФФ может отменить
  },

  LOGISTICS_CONFIRMED: {
    WHOLESALE: ['ship', 'cancel'], // Поставщик может отгрузить или отменить
    LOGIST: ['cancel', 'update_route'], // Логистика может отменить или изменить маршрут
    SELLER: ['cancel'], // Селлер может отменить
    FULFILLMENT: ['cancel'], // ФФ может отменить
  },

  SHIPPED: {
    FULFILLMENT: ['receive', 'report_issues'], // ФФ может принять или сообщить о проблемах
    LOGIST: ['update_tracking', 'report_delay'], // Логистика может обновить трекинг
    WHOLESALE: [], // Поставщик ждет
    SELLER: [], // Селлер ждет
  },

  DELIVERED: {
    // Финальный статус - никто не может изменить
  },

  CANCELLED: {
    // Финальный статус - никто не может изменить
  },
}
```

---

_Дополнено реальными мутациями из кода: createSupplyOrder, updateSupplyOrderStatus, logisticsConfirmOrder, createWildberriesSupply_  
_Источники: src/graphql/resolvers.ts:4828+, 6900+, 7681+, 6772+_
_Обновлено: 2025-08-21_
