projects/org/pirotehnika/app/pim/DATA_FLOW_STRATEGY.md

Стратегия потоков данных PIM Service

1. Источник истины (Source of Truth)

Текущая ситуация: Неопределенность

Предлагаемое решение: Разделение ответственности

┌─────────────────────────────────────────────────────────────┐
│  ИСТОЧНИКИ ИСТИНЫ ПО ТИПАМ ДАННЫХ                           │
├─────────────────────────────────────────────────────────────┤
│                                                               │
│  1. Номенклатура (артикулы, названия)                        │
│     ✓ Эталон: 1С (главная учетная система)                  │
│     ✓ Таблица: 1c_products (READ-ONLY клон 1С)              │
│                                                               │
│  2. Цены и характеристики                                    │
│     ✓ Эталон: PIM (обогащенные данные)                      │
│     ✓ Таблица: pim_products (мастер-данные)                 │
│                                                               │
│  3. Правила ценообразования                                  │
│     ✓ Эталон: PIM                                            │
│     ✓ Таблица: price_cost_rules                               │
│                                                               │
│  4. Сырые данные от поставщиков                              │
│     ✓ Эталон: Файлы + метаданные в БД                       │
│     ✓ Хранение: файловая система + таблица staging          │
│                                                               │
└─────────────────────────────────────────────────────────────┘

2. Основной поток данных

Вариант 1: 1С → PIM → 1С (Рекомендуемый)

┌─────────────────────────────────────────────────────────────┐
│  ШАГ 1: Синхронизация номенклатуры из 1С                    │
└─────────────────────────────────────────────────────────────┘
                           │
                           ▼
              ┌────────────────────────┐
              │   1C Export (JSON/XML) │
              └────────────────────────┘
                           │
                           ▼
              ┌────────────────────────┐
              │   Импорт в 1c_products │ ← READ-ONLY таблица
              │   (полная перезапись)  │   (клон 1С)
              └────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│  ШАГ 2: Анализ и валидация                                  │
└─────────────────────────────────────────────────────────────┘
                           │
                           ▼
              ┌────────────────────────┐
              │  Поиск ошибок:         │
              │  - Пустые названия     │
              │  - Дубли артикулов     │
              │  - Отсутствие цен      │
              │  - Невалидные данные   │
              └────────────────────────┘
                           │
                           ▼
              ┌────────────────────────┐
              │  Логирование ошибок    │
              │  → pim_validation_log  │
              └────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│  ШАГ 3: Обогащение в PIM                                    │
└─────────────────────────────────────────────────────────────┘
                           │
                           ▼
         ┌─────────────────┴────────────────┐
         │                                   │
         ▼                                   ▼
┌──────────────────┐            ┌──────────────────────┐
│ Новые товары     │            │ Существующие товары  │
│ из 1c_products   │            │ обновление данных    │
└──────────────────┘            └──────────────────────┘
         │                                   │
         └─────────────────┬────────────────┘
                           ▼
              ┌────────────────────────┐
              │  1. Применить правила  │
              │     ценообразования    │
              │  2. Добавить характ-ки │
              │  3. Обновить описания  │
              │                        │
              │  → pim_products        │ ← MASTER данные
              └────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│  ШАГ 4: Экспорт обратно в 1С                                │
└─────────────────────────────────────────────────────────────┘
                           │
                           ▼
              ┌────────────────────────┐
              │  Формирование файла    │
              │  для импорта в 1С:     │
              │  - cost_price          │
              │  - updated_description │
              │  - characteristics     │
              └────────────────────────┘
                           │
                           ▼
              ┌────────────────────────┐
              │  1С Import             │
              │  (обновление цен)      │
              └────────────────────────┘

3. Где хранить данные от парсеров?

Проблема выбора: База vs Файлы

Рассмотрим типы данных:

A. Сырые прайс-листы от поставщиков

Характеристики:
- Файлы Excel/CSV/PDF (1-5 МБ каждый)
- Приходят 1-2 раза в неделю
- Нужно хранить историю (для аудита)
- Структура файлов разная у каждого бренда

Решение: Файловая система + метаданные в БД

/data/price-lists/
  ├── ФЕЙЕРВЕРК/
     ├── 2024-01-15_price.xlsx
     ├── 2024-01-22_price.xlsx
     └── 2024-01-29_price.xlsx
  ├── САЛЮТ/
     ├── 2024-01-10_catalog.csv
     └── 2024-01-20_catalog.csv
  └── ...

БД: pim_price_file_uploads
  ├── id
  ├── brand
  ├── file_path               путь к файлу
  ├── upload_date
  ├── file_size
  ├── file_hash               для дедупликации
  ├── parsed_status           'pending', 'parsed', 'error'
  └── parsed_at

Почему файлы:
- ✓ Сохраняем оригинал (аудит)
- ✓ Не раздуваем БД
- ✓ Можно переобработать при ошибке
- ✓ Легко бэкапить


B. Распарсенные данные из прайсов (staging)

Характеристики:
- Структурированные данные после парсинга
- Временные (промежуточные)
- Нужна валидация перед импортом в pim_products

Решение: Staging таблица в БД

CREATE TABLE pim_staging_products (
    id SERIAL PRIMARY KEY,

    -- Источник
    upload_id INTEGER REFERENCES pim_price_file_uploads(id),
    brand VARCHAR(100),

    -- Данные товара (как распознали из файла)
    article VARCHAR(100),
    name TEXT,
    base_price NUMERIC(10, 2),
    characteristics JSONB,  -- гибкая структура

    -- Статус обработки
    status VARCHAR(20),  -- 'pending', 'matched', 'imported', 'error'
    matched_article VARCHAR(100),  -- сопоставленный артикул из 1c_products
    match_confidence NUMERIC(3, 2),  -- 0.0 - 1.0

    -- Ошибки
    validation_errors JSONB,

    -- Метаданные
    created_at TIMESTAMP DEFAULT NOW(),
    processed_at TIMESTAMP
);

Почему staging в БД:
- ✓ Удобно делать SQL-запросы для сопоставления
- ✓ Можно хранить статус обработки
- ✓ Легко показать в UI для ручной модерации
- ✓ Очищаем после успешного импорта


C. Данные с сайтов (фото, описания)

Характеристики:
- Изображения (JPG/PNG, 100-500 КБ)
- HTML описания
- Сертификаты (PDF)

Решение: Комбинированное

Изображения → S3/MinIO + URL в БД
  /images/products/
    ├── ФЕЙЕРВЕРК/
    │   ├── ART123_main.jpg
    │   ├── ART123_gallery_1.jpg
    │   └── ...
    └── ...

Описания → БД (TEXT/JSONB)
  pim_products.description_html

Сертификаты → Файловая система + URL в БД
  /data/certificates/
    ├── ФЕЙЕРВЕРК/
    │   ├── cert_2024.pdf
    │   └── ...
    └── ...

Таблица метаданных:

CREATE TABLE pim_product_images (
    id SERIAL PRIMARY KEY,
    article VARCHAR(100) REFERENCES pim_products(article),
    image_url VARCHAR(500),        -- URL в S3/CDN
    image_type VARCHAR(20),        -- 'main', 'gallery', 'certificate'
    source VARCHAR(100),           -- откуда получено
    downloaded_at TIMESTAMP,
    file_size INTEGER,
    is_active BOOLEAN DEFAULT TRUE
);

4. Итоговая схема хранения

┌─────────────────────────────────────────────────────────────┐
  ТИП ДАННЫХ                ХРАНИЛИЩЕ            ТАБЛИЦА   
├─────────────────────────────────────────────────────────────┤
                                                               
  Номенклатура из 1С        PostgreSQL           1c_products (клон, read-only)
                                                             
  Мастер-данные PIM         PostgreSQL           pim_products (эталон цен)
                                                             
  Сырые прайсы              Файловая система     pim_price_file_uploads (метаданные)
                            /data/price-lists/               
                                                             
  Staging (парсинг)         PostgreSQL           pim_staging_products (временные)
                                                             
  Изображения товаров       S3 / MinIO           pim_product_images (URLs)
                                                             
  Сертификаты               Файловая система     pim_product_images (URLs)
                            /data/certificates/              
                                                             
  Логи синхронизации        PostgreSQL           pim_import_logs
                                                             
  История цен               PostgreSQL           pim_price_history
                                                             
  Ошибки валидации          PostgreSQL           pim_validation_log
                                                             
└─────────────────────────────────────────────────────────────┘

5. Новые таблицы для staging и валидации

pim_price_file_uploads

class PimPriceFileUpload(Base):
    """Загруженные файлы прайсов"""
    __tablename__ = "pim_price_file_uploads"

    id = Column(Integer, primary_key=True)
    brand = Column(String(100), nullable=False)

    file_path = Column(String(500), nullable=False)
    file_name = Column(String(255), nullable=False)
    file_size = Column(Integer)
    file_hash = Column(String(64))  # SHA256 для дедупликации

    uploaded_at = Column(DateTime, default=datetime.utcnow)
    uploaded_by = Column(String(100))  # user ID

    parsed_status = Column(String(20), default='pending')  # pending, parsed, error
    parsed_at = Column(DateTime)
    parsed_count = Column(Integer)  # кол-во распознанных товаров
    parse_errors = Column(Text)

pim_staging_products

class PimStagingProduct(Base):
    """Staging таблица для импорта из парсеров"""
    __tablename__ = "pim_staging_products"

    id = Column(Integer, primary_key=True)
    upload_id = Column(Integer, ForeignKey('pim_price_file_uploads.id'))

    # Источник данных
    brand = Column(String(100))
    source = Column(String(50))  # 'price_parser', 'site_parser', 'manual'

    # Распознанные данные
    article = Column(String(100))
    name = Column(Text)
    base_price = Column(Numeric(10, 2))
    characteristics = Column(JSON)  # гибкая структура

    # Сопоставление с существующими товарами
    status = Column(String(20), default='pending')  # pending, matched, imported, error
    matched_article = Column(String(100))  # артикул из 1c_products
    match_confidence = Column(Numeric(3, 2))  # 0.0-1.0
    match_method = Column(String(50))  # 'exact', 'fuzzy', 'manual'

    # Валидация
    validation_errors = Column(JSON)
    is_valid = Column(Boolean, default=False)

    # Метаданные
    created_at = Column(DateTime, default=datetime.utcnow)
    processed_at = Column(DateTime)
    processed_by = Column(String(100))

pim_validation_log

class PimValidationLog(Base):
    """Лог ошибок валидации"""
    __tablename__ = "pim_validation_log"

    id = Column(Integer, primary_key=True)

    # Что проверяли
    table_name = Column(String(50))  # '1c_products', 'staging', 'pim_products'
    record_id = Column(String(100))  # article или ID

    # Ошибка
    error_type = Column(String(50))  # 'missing_field', 'invalid_price', 'duplicate'
    error_message = Column(Text)
    severity = Column(String(20))  # 'error', 'warning', 'info'

    # Контекст
    import_log_id = Column(Integer, ForeignKey('pim_import_logs.id'))
    detected_at = Column(DateTime, default=datetime.utcnow)

    # Исправление
    resolved = Column(Boolean, default=False)
    resolved_at = Column(DateTime)
    resolved_by = Column(String(100))
    resolution_note = Column(Text)

6. Workflow с новыми таблицами

1. ЗАГРУЗКА ПРАЙСА
   ├─→ Сохраняем файл в /data/price-lists/BRAND/
   ├─→ Создаем запись в pim_price_file_uploads
   └─→ file_hash проверяет дубли

2. ПАРСИНГ
   ├─→ Читаем файл
   ├─→ Извлекаем данные  pim_staging_products
   ├─→ Обновляем pim_price_file_uploads.parsed_status = 'parsed'
   └─→ Количество записей  parsed_count

3. ВАЛИДАЦИЯ STAGING
   ├─→ Проверяем обязательные поля
   ├─→ Проверяем диапазоны цен
   ├─→ Ошибки  pim_validation_log
   └─→ Статус  validation_errors, is_valid

4. СОПОСТАВЛЕНИЕ (MATCHING)
   ├─→ Точное совпадение по article
   ├─→ Fuzzy matching по name
   ├─→ Записываем matched_article, match_confidence
   └─→ Низкая уверенность  ручная модерация

5. ИМПОРТ В PIM_PRODUCTS
   ├─→ Только is_valid = TRUE
   ├─→ Создаем/обновляем pim_products
   ├─→ История  pim_price_history
   ├─→ Лог  pim_import_logs
   └─→ Обновляем staging.status = 'imported'

6. ОЧИСТКА STAGING
   └─→ Удаляем успешно импортированные записи старше 30 дней

7. Рекомендации по архитектуре

✓ Правило источника истины:

1C  номенклатура (артикулы, базовая информация)
       
   1c_products (read-only клон)
       
PIM  цены, характеристики, обогащение
       
   pim_products (master-данные)
       
1C  обновленные цены и данные

✓ Правило хранения файлов:

Файлы < 1 МБ, структурированные → БД
Файлы > 1 МБ, бинарные → Файловая система
Изображения → S3/MinIO/CDN

✓ Правило staging:

Все внешние данные сначала → staging
Валидация → staging
Модерация → staging
Импорт → master таблицы

Резюме

Где хранить:

Тип данных Хранилище Причина
Номенклатура 1С 1c_products (БД) Клон 1С, read-only
Мастер-данные pim_products (БД) Источник истины для цен
Сырые прайсы Файлы + метаданные Аудит, переобработка
Staging парсинга pim_staging_products (БД) Валидация, модерация
Изображения S3 + URLs в БД Масштабируемость
Сертификаты Файлы + URLs в БД Долгосрочное хранение

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

1С  1c_products  Валидация  Обогащение  pim_products  Экспорт  1С
                                    
                              Парсеры  Staging  Валидация  Matching

Создавать реализацию?