# 🔄 ПРАВИЛА СИНХРОНИЗАЦИИ ДАННЫХ МЕЖДУ КОМПОНЕНТАМИ

> **Цель:** Обеспечить консистентность данных между различными компонентами системы SFERA

## 📋 **ОСНОВНЫЕ ПРИНЦИПЫ**

### 1. **ЕДИНЫЙ ИСТОЧНИК ИСТИНЫ (Single Source of Truth)**

- ✅ **Один резолвер = одна таблица БД** для одного типа данных
- ✅ **V2 система** - приоритет над legacy кодом
- ❌ **Никогда не дублируйте** одни данные в разных резолверах

### 2. **КОНСИСТЕНТНАЯ CACHE POLICY**

```typescript
// ✅ ПРАВИЛЬНО - одинаковая policy для связанных данных
const query1 = useQuery(QUERY_A, { fetchPolicy: 'cache-and-network' })
const query2 = useQuery(QUERY_B, { fetchPolicy: 'cache-and-network' })

// ❌ НЕПРАВИЛЬНО - разные policies создают рассинхронизацию
const query1 = useQuery(QUERY_A, { fetchPolicy: 'cache-and-network' })
const query2 = useQuery(QUERY_B, {}) // default cache-first policy
```

### 3. **ОБЯЗАТЕЛЬНЫЕ FETCH POLICIES**

#### **Для связанных компонентов:**

```typescript
fetchPolicy: 'cache-and-network', // Всегда актуальные данные
pollInterval: 30000,              // Автообновление каждые 30 сек
```

#### **Для dashboard/статистики:**

```typescript
fetchPolicy: 'cache-and-network', // Синхронизация с основными данными
pollInterval: 30000,              // Регулярное обновление
errorPolicy: 'all',               // Показывать частичные данные при ошибках
```

---

## 🏢 **ПРАВИЛА ДЛЯ ФУЛФИЛМЕНТА**

### **ТАБЛИЦЫ V2 СИСТЕМЫ**

- **`fulfillmentConsumableInventory`** - складские остатки расходников
- **`fulfillmentConsumableSupplyOrder`** - поставки расходников
- **`fulfillmentInventoryV2`** - общий инвентарь фулфилмента

### **СВЯЗАННЫЕ КОМПОНЕНТЫ**

```typescript
// Все эти компоненты должны использовать одинаковые данные:

1. Главный dashboard склада (/fulfillment-warehouse)
   - Карточка "РАСХОДНИКИ ФУЛФИЛМЕНТА"

2. Подраздел расходников (/fulfillment-warehouse/supplies)
   - Карточка "ОСТАТОК"
   - Главная таблица поставок

3. Раздел услуг (/services)
   - Вкладка "Расходники"

4. История поставок
   - Раскрывающиеся детали каждого товара
```

### **СИНХРОНИЗИРОВАННЫЕ ПОЛЯ**

```typescript
interface SynchronizedData {
  currentStock: number // Текущий остаток (одинаковый везде)
  totalReceived: number // Общее количество поступлений
  productId: string // ID для группировки истории поставок
  fulfillmentCenterId: string // ID фулфилмент-центра
}
```

---

## 📊 **ПРАВИЛА ДЛЯ СТАТИСТИЧЕСКИХ КОМПОНЕНТОВ**

### **1. АРХИТЕКТУРА MASTER-DETAIL**

```typescript
// MASTER компонент (главный раздел)
const masterStats = useQuery(GET_WAREHOUSE_STATS, {
  fetchPolicy: 'cache-and-network',
  pollInterval: 30000,
})

// DETAIL компонент (подраздел)
const detailStats = useQuery(GET_DETAILED_SUPPLIES, {
  fetchPolicy: 'cache-and-network', // ← ОБЯЗАТЕЛЬНО то же самое!
  pollInterval: 30000, // ← ОБЯЗАТЕЛЬНО то же самое!
})

// Вычисление должно давать одинаковые результаты
const masterValue = masterStats.data?.fulfillmentSupplies?.current
const detailValue = detailStats.data?.supplies?.reduce((sum, s) => sum + s.currentStock, 0)
// masterValue === detailValue ← ОБЯЗАТЕЛЬНО!
```

### **2. ПРАВИЛА АГРЕГАЦИИ**

```typescript
// ✅ ПРАВИЛЬНО - агрегация из одного источника данных
const totalStock = inventoryItems.reduce((sum, item) => sum + item.currentStock, 0)

// ❌ НЕПРАВИЛЬНО - агрегация из разных источников
const totalStock1 = supplies.reduce((sum, s) => sum + s.currentStock, 0) // источник A
const totalStock2 = inventory.reduce((sum, i) => sum + i.remainingStock, 0) // источник B
```

---

## 🔗 **ПРАВИЛА СВЯЗЫВАНИЯ ДАННЫХ**

### **ОБЯЗАТЕЛЬНЫЕ ПОЛЯ ДЛЯ СВЯЗИ**

```typescript
// В GraphQL схемах ОБЯЗАТЕЛЬНО указывать связующие поля:

type Supply {
  id: ID!
  productId: ID!           // ← Для фильтрации истории поставок
  fulfillmentCenterId: ID  // ← Для привязки к фулфилменту
}

type SupplyOrder {
  id: ID!
  fulfillmentCenterId: ID! // ← Для фильтрации по фулфилменту
  items: [SupplyOrderItem!]!
}

type SupplyOrderItem {
  productId: ID!           // ← Для связи с inventory
  requestedQuantity: Int!
  receivedQuantity: Int!   // ← Для подсчёта остатков
}
```

### **ФИЛЬТРАЦИЯ ПО СВЯЗАННЫМ ДАННЫМ**

```typescript
// ✅ ПРАВИЛЬНО - фильтрация по ID
const getSupplyHistory = (supply: Supply) => {
  return allDeliveries.filter((delivery) => delivery.items?.some((item) => item.productId === supply.productId))
}

// ❌ НЕПРАВИЛЬНО - фильтрация по названию (может быть дубли)
const getSupplyHistory = (supply: Supply) => {
  return allDeliveries.filter((delivery) => delivery.name === supply.name && delivery.category === supply.category)
}
```

---

## ⚡ **ПРАВИЛА REAL-TIME ОБНОВЛЕНИЙ**

### **АВТОМАТИЧЕСКАЯ СИНХРОНИЗАЦИЯ**

```typescript
// Компоненты должны обновляться автоматически при изменениях

// 1. При приёме поставки на склад
prisma.fulfillmentConsumableInventory.update({
  where: { id: itemId },
  data: { currentStock: newStock },
})
// → Автоматически обновятся: статистика, таблица, услуги

// 2. При отгрузке товаров
prisma.fulfillmentConsumableInventory.update({
  where: { id: itemId },
  data: { currentStock: { decrement: shippedQuantity } },
})
// → Автоматически обновятся все связанные компоненты
```

### **POLLING ИНТЕРВАЛЫ**

```typescript
pollInterval: 30000 // 30 сек - для статистики и dashboard
pollInterval: 60000 // 1 мин - для списков и таблиц
pollInterval: 120000 // 2 мин - для отчётов и архивных данных
```

---

## 🚨 **АНТИ-ПАТТЕРНЫ И ТИПИЧНЫЕ ОШИБКИ**

### **❌ НИКОГДА НЕ ДЕЛАЙТЕ:**

#### **1. Разные источники для одних данных**

```typescript
// ❌ ПЛОХО
const stats = useQuery(GET_OLD_SUPPLIES) // legacy таблица
const details = useQuery(GET_NEW_SUPPLIES) // V2 таблица
```

#### **2. Разные cache policies для связанных данных**

```typescript
// ❌ ПЛОХО
const master = useQuery(QUERY_A, { fetchPolicy: 'cache-and-network' })
const detail = useQuery(QUERY_B, {}) // default cache-first
```

#### **3. Ручная синхронизация через состояние**

```typescript
// ❌ ПЛОХО
const [manualSync, setManualSync] = useState(0)
useEffect(() => {
  // Попытка ручной синхронизации через state
}, [manualSync])
```

#### **4. Группировка по нестабильным полям**

```typescript
// ❌ ПЛОХО - названия могут изменяться
supplies.filter((s) => s.name === targetName)

// ✅ ХОРОШО - ID стабильны
supplies.filter((s) => s.productId === targetProductId)
```

---

## 🎯 **ЧЕКЛИСТ СИНХРОНИЗАЦИИ**

### **При создании связанных компонентов:**

- [ ] Используют одну таблицу БД как источник данных
- [ ] Одинаковые `fetchPolicy` настройки
- [ ] Одинаковые `pollInterval` значения
- [ ] Связь через стабильные ID поля
- [ ] Одинаковая логика агрегации/фильтрации
- [ ] Обработка ошибок и loading состояний

### **При тестировании синхронизации:**

- [ ] Изменение данных обновляет все связанные компоненты
- [ ] Значения в master и detail компонентах совпадают
- [ ] Нет задержек между обновлениями (< 30 сек)
- [ ] Ошибки в одном компоненте не ломают другие

---

## 📈 **МОНИТОРИНГ СИНХРОНИЗАЦИИ**

### **DEBUG ЛОГИ**

```typescript
// Добавляйте логи для контроля синхронизации
console.warn('SYNC CHECK:', {
  masterValue: masterData?.totalStock,
  detailValue: detailData?.reduce(sum, 0),
  timestamp: new Date().toISOString(),
  source: 'fulfillmentInventorySync',
})
```

### **УВЕДОМЛЕНИЯ О РАССИНХРОНИЗАЦИИ**

```typescript
// Проверка консистентности данных
if (Math.abs(masterValue - detailValue) > 0) {
  console.error('🚨 DATA SYNCHRONIZATION ERROR', {
    master: masterValue,
    detail: detailValue,
    component: 'FulfillmentStats',
  })

  // Отправка уведомления разработчикам
  toast.error('Обнаружена рассинхронизация данных')
}
```

**Следование этим правилам обеспечит надёжную синхронизацию данных между всеми компонентами системы!** 🚀
