infra/@nocodb.app/DATA_FLOW.md

Зависимости данных и порядок загрузки

Версия: 1.0.0
Дата: 2025-11-28


Граф зависимостей

┌─────────────────────────────────────────────────────────────────┐
│                        УРОВЕНЬ 0                                │
│                    (нет зависимостей)                           │
│  ┌────────────┐    ┌────────────┐                              │
│  │ Suppliers  │    │ Categories │                              │
│  └─────┬──────┘    └─────┬──────┘                              │
└────────┼─────────────────┼──────────────────────────────────────┘
         │                 │
         ▼                 ▼
┌─────────────────────────────────────────────────────────────────┐
│                        УРОВЕНЬ 1                                │
│              (зависит от справочников)                          │
│            ┌─────────────────────────┐                         │
│            │       Products          │                         │
│            │  supplier → Suppliers   │                         │
│            │  category → Categories  │                         │
│            └───────────┬─────────────┘                         │
└────────────────────────┼────────────────────────────────────────┘
                         │
         ┌───────┬───────┼───────┬───────┐
         ▼       ▼       ▼       ▼       ▼
┌─────────────────────────────────────────────────────────────────┐
│                        УРОВЕНЬ 2                                │
│                (зависит от Products)                            │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐              │
│  │ PIM_Catalog │ │ SKU_Mappings│ │   Images    │              │
│  │ article →   │ │ article →   │ │ article →   │              │
│  │ Products    │ │ Products    │ │ Products    │              │
│  └─────────────┘ └─────────────┘ └─────────────┘              │
│                                                                 │
│  ┌─────────────────────┐  ┌─────────────────────┐             │
│  │   Prices_History    │  │      Bundles        │             │
│  │ article → Products  │  │ bundle_article →    │             │
│  │ supplier → Suppliers│  │   Products          │             │
│  └─────────────────────┘  │ component_article → │             │
│                           │   Products          │             │
│                           └─────────────────────┘             │
└─────────────────────────────────────────────────────────────────┘

Порядок загрузки

Этап 0: Справочники (без зависимостей)

1. Suppliers       поставщики
2. Categories      категории товаров

Источники:
- Suppliers: ручной ввод или pirotehnika/products/CLAUDE.md
- Categories: из 1С или ручной ввод

Валидация:
- code уникален
- Обязательные поля заполнены


Этап 1: Мастер товаров

3. Products        основной каталог

Зависимости:
- supplier → должен существовать в Suppliers.code
- category → должен существовать в Categories.code

Источники:
- Экспорт из 1С
- Прайс-листы поставщиков (первичное создание)

Валидация:
- article уникален
- supplier существует в Suppliers
- category существует в Categories


Этап 2: Расширенные данные

4. PIM_Catalog     характеристики пиротехники
5. SKU_Mappings    маппинг на маркетплейсы
6. Images          изображения
7. Prices_History  история цен
8. Bundles         наборы

Зависимости:

Таблица Поле Ссылается на
PIM_Catalog article Products.article
SKU_Mappings article Products.article
Images article Products.article
Prices_History article Products.article
Prices_History supplier Suppliers.code
Bundles bundle_article Products.article
Bundles component_article Products.article

Потоки данных

Поток 1: Новый прайс от поставщика

┌──────────────┐
│ Прайс XLSX   │
│ от поставщика│
└──────┬───────┘
       ▼
┌──────────────┐     ┌──────────────┐
│ Парсинг      │────▶│Prices_History│  (история)
│ прайса       │     └──────────────┘
└──────┬───────┘
       │
       ▼ новые артикулы?
┌──────────────┐     ┌──────────────┐
│ Создать      │────▶│  Products    │  (новые товары)
│ Products     │     └──────────────┘
└──────┬───────┘
       │
       ▼ обновить цены
┌──────────────┐
│ Пересчёт цен │
│ base → ozon  │
│ base → wb    │
└──────────────┘

Поток 2: Синхронизация с 1С

┌──────────────┐
│ Экспорт 1С   │
│ (XML/CSV)    │
└──────┬───────┘
       ▼
┌──────────────┐
│ Сравнение с  │
│ Products     │
└──────┬───────┘
       │
       ├──▶ Новые → INSERT в Products
       ├──▶ Изменённые → UPDATE Products
       └──▶ Удалённые → пометить archived

Поток 3: Публикация на маркетплейс

┌──────────────┐
│  Products    │
│  (active)    │
└──────┬───────┘
       │
       ▼
┌──────────────┐     ┌──────────────┐
│ Проверка     │────▶│ PIM_Catalog  │  (характеристики)
│ готовности   │     └──────────────┘
└──────┬───────┘     ┌──────────────┐
       │        ────▶│   Images     │  (фото)
       │             └──────────────┘
       ▼
┌──────────────┐
│ Создать SKU  │
│ на OZON/WB   │
└──────┬───────┘
       │
       ▼
┌──────────────┐
│ SKU_Mappings │  (сохранить связь)
└──────────────┘

Поток 4: Создание бандла

┌──────────────┐
 Выбрать      
 компоненты   
 из Products  
└──────┬───────┘
       
       
┌──────────────┐
 Создать      
 Products       (bundle_article)
 type=bundle  
└──────┬───────┘
       
       
┌──────────────┐
  Bundles       (связи компонентов)
 bundle  PRD 
 component   
   PRD-001    
   PRD-002    
└──────────────┘

Скрипты загрузки

load_order.py

LOAD_ORDER = [
    # Этап 0: Справочники
    ("Suppliers", "suppliers.csv", []),
    ("Categories", "categories.csv", []),

    # Этап 1: Мастер
    ("Products", "products.csv", ["Suppliers", "Categories"]),

    # Этап 2: Детали
    ("PIM_Catalog", "pim.csv", ["Products"]),
    ("SKU_Mappings", "sku_mappings.csv", ["Products"]),
    ("Images", "images.csv", ["Products"]),
    ("Prices_History", "prices.csv", ["Products", "Suppliers"]),
    ("Bundles", "bundles.csv", ["Products"]),
]

def load_all():
    loaded = set()
    for table, file, deps in LOAD_ORDER:
        # Проверить зависимости
        for dep in deps:
            if dep not in loaded:
                raise Exception(f"{table} requires {dep}")

        # Загрузить
        load_table(table, file)
        loaded.add(table)

Валидация целостности

Проверки перед загрузкой

-- Products: проверить supplier
SELECT p.article, p.supplier
FROM Products p
LEFT JOIN Suppliers s ON p.supplier = s.code
WHERE s.code IS NULL;

-- Products: проверить category
SELECT p.article, p.category
FROM Products p
LEFT JOIN Categories c ON p.category = c.code
WHERE c.code IS NULL;

-- SKU_Mappings: проверить article
SELECT sm.article
FROM SKU_Mappings sm
LEFT JOIN Products p ON sm.article = p.article
WHERE p.article IS NULL;

-- Bundles: проверить компоненты
SELECT b.bundle_article, b.component_article
FROM Bundles b
LEFT JOIN Products p ON b.component_article = p.article
WHERE p.article IS NULL;

Хранение изображений

Принцип: Единый источник

┌─────────────────────────────────────────────────────────┐
│                    S3 HUB (Beget)                       │
│              /pim/images/*.jpg                          │
│           ЕДИНСТВЕННОЕ МЕСТО ХРАНЕНИЯ                   │
│                  файлов картинок                        │
└─────────────────────┬───────────────────────────────────┘
                      │
                      │ публичный URL
                      │
        ┌─────────────┼─────────────┬─────────────┐
        │             │             │             │
        ▼             ▼             ▼             ▼
   ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐
   │ MongoDB │  │ Drupal  │  │ NocoDB  │  │  OZON   │
   │ (ссылка)│  │ (ссылка)│  │ (ссылка)│  │ (ссылка)│
   └─────────┘  └─────────┘  └─────────┘  └─────────┘

Правило: Файлы НЕ дублируются. Везде только URL на S3.

Конфигурация S3

Параметр Значение
Провайдер Beget S3
Bucket fe2c1d30dc11-s3-0kt
Папка /pim/images/
Файлов 227
Доступ Публичный (read)
Base URL https://s3.ru1.storage.beget.cloud/fe2c1d30dc11-s3-0kt/

Формат URL

https://s3.ru1.storage.beget.cloud/fe2c1d30dc11-s3-0kt/pim/images/{filename}.jpg

Индекс картинок

Файл: pim/images/images_index.xlsx

Колонка Описание
Производитель Название поставщика
Артикул Ключ связи с Products
Название Название товара
S3 URL Полный публичный URL
S3 Key Путь внутри bucket
Имя файла filename.jpg

Синхронизация ссылок

При добавлении картинки:
1. Загрузить файл → S3 /pim/images/
2. Обновить images_index.xlsx
3. Обновить ссылку в MongoDB
4. Обновить ссылку в Drupal
5. Обновить ссылку в NocoDB Images

NocoDB Images — только ссылки

Images таблица:
├── article        → Products.article
├── image_url      → S3 публичный URL (НЕ файл!)
├── is_main        → главное фото
├── sort_order     → порядок
└── source         → откуда взято (supplier/manual)

Статусы записей

Products.status

Статус Описание Можно публиковать
draft Черновик
active Активный
paused Приостановлен
archived В архиве
out_of_stock Нет в наличии

SKU_Mappings.status

Статус Описание
active Опубликован
paused Снят с продажи
archived Удалён
pending Ожидает модерации

Триггеры (TODO)

При изменении Products

ON UPDATE Products.purchase_price:
  → Пересчитать base_price, ozon_price, wb_price
  → Записать в Prices_History

ON DELETE Products:
  → Проверить нет ли в Bundles
  → Пометить SKU_Mappings как archived

При изменении Suppliers

ON UPDATE Suppliers.code:
  → Обновить Products.supplier
  → Обновить Prices_History.supplier

Версия: 1.0.0