Версия: 4.0.0
Дата: 2025-12-27
Статус: ✅ АКТУАЛЬНЫЙ
Автор: Claude Opus (Архитектор)
ВАЖНО: Это главный документ архитектуры PIM. Все остальные документы архивированы или устарели.
PIM = Product Information Management — централизованный каталог товаров.
Основная идея:
ВХОДЫ (источники) → PIM (ядро) → ВЫХОДЫ (каналы)
1С, прайсы xlsx → PostgreSQL → OZON, сайт, отчёты
Ключевое разделение:
- Товары (pim_products) = характеристики (название, бренд, вес...)
- Цены (price_supplier_cost) = цены закупки от каждого поставщика
- Остатки (stock_inventory) = количество на каждом складе
Подключение:
- Host: localhost:5433
- Database: nocodb
- Schema: pt7k98pv0fwi1el
- User: nocodb
Статус: ✅ Production, 4626 товаров
Подробнее о базах: ../../data/connectors/DATABASE_ARCHITECTURE.md
pim_products (4626 записей)
article — артикул (PK)
name — название
brand — бренд
category — категория
product_type — тип (салют, фонтан, ракета...)
base_price — базовая цена от производителя
weight_grams — вес
ВАЖНО: cost_price и stock_qty УДАЛЕНЫ → данные в отдельных таблицах!
price_supplier_cost (переименовано в v4.0)
id SERIAL PRIMARY KEY
article TEXT NOT NULL
supplier_code TEXT NOT NULL → suppliers(code)
cost_price NUMERIC NOT NULL
valid_from TIMESTAMP -- начало действия
valid_to TIMESTAMP -- конец действия (NULL = актуально)
source_file TEXT
Пример:
Товар: JF-001
├─ jf: 43.92₽ (2025-11-01 → ∞)
├─ maxsem: 45.00₽ (2025-11-07 → ∞)
└─ pirosnab: 48.00₽ (2025-12-26 → ∞)
suppliers (справочник)
code — jf, maxsem, pirosnab, gordeev...
name — JF-Pyro, Maxsem, Pirosnab...
api_type — json, xlsx, web_scraper
A. Остатки НА СКЛАДАХ (stock_inventory) — где физически лежит товар
id SERIAL PRIMARY KEY
article TEXT NOT NULL
warehouse_id INTEGER → stock_warehouses(id)
quantity INTEGER
reserved INTEGER
available INTEGER
stock_warehouses (склады)
id code name warehouse_type
1 gordeev_1 ИП Гордеев основной supplier
9 pirosnab Pirosnab Софийская supplier
2 ozon_o1_fbo OZON O1 FBO marketplace
B. Остатки У ПОСТАВЩИКОВ (stock_supplier) — сколько поставщик может дать
id SERIAL PRIMARY KEY
article TEXT NOT NULL
supplier_code TEXT NOT NULL → suppliers(code)
quantity INTEGER
Разница:
- stock_inventory = физические места хранения (склад Pirosnab на Софийской)
- stock_supplier = источники закупки (сколько JF может поставить)
PriceList — загруженные прайсы
name — "Pirosnab прайс-лист 2025-12"
supplier_code — pirosnab
source_file — pirosnab.spb.ru (API)
loaded_at — 2025-12-26 15:30
PriceListItem — позиции прайсов
pricelist_id → PriceList
article
cost_price
Связь: PriceListItem → price_supplier_cost (при импорте)
1С JSON → pim_products (характеристики)
Скрипт: app/pim/import_from_1c.py
Частота: Ежедневно
Что обновляет: name, brand, category, base_price
Прайс xlsx/API → PriceList + PriceListItem → supplier_cost
Примеры:
- Pirosnab: data/connectors/import_pirosnab_to_pim_v3.py (веб-скрейпер)
- Maxsem: data/scripts/import_maxsem_price.py (xlsx)
- JF: data/scripts/import_jf_price.py (json)
Что происходит:
1. Загрузить прайс → PriceList (запись о файле)
2. Парсить позиции → PriceListItem (все строки)
3. Обновить цены → price_supplier_cost (с valid_from=NOW, закрыть старые)
API склада → stock_inventory (warehouse_id, quantity)
Примеры:
- OZON API → stock_inventory (warehouse_id=2, OZON FBO)
- Pirosnab API → stock_inventory (warehouse_id=9, Pirosnab)
Прайс поставщика → stock_supplier (supplier_code, quantity)
# Получить лучшую цену среди поставщиков
cost, supplier = get_best_supplier_cost(article)
# Применить наценку
retail_price = cost * (1 + markup) * (1 - brand_discount)
Правила наценки: price_cost_rules, price_sale_rules
| Коннектор | Путь | Что импортирует |
|---|---|---|
| 1C | data/scripts/sync_1c_to_nocodb.py |
Товары (характеристики) |
| Pirosnab | data/connectors/import_pirosnab_to_pim_v3.py |
Цены + остатки |
| JF-Pyro | data/scripts/import_jf_price.py |
Цены |
| Maxsem | data/scripts/import_maxsem_price.py |
Цены |
| Канал | Путь | Что экспортирует |
|---|---|---|
| OZON | app/mp1/ |
Товары + цены + остатки |
| Сайт pirotehnika.spb.ru | OpenCart | Каталог + цены |
| Отчёты | app/pim/api/ |
Аналитика, выгрузки |
Сервер: uvicorn pim.main:app --port 8000
| Endpoint | Метод | Описание |
|---|---|---|
/products |
GET | Список товаров |
/products/{article} |
GET | Детали товара |
/products/{article}/costs |
GET | Цены от всех поставщиков |
/products/{article}/stocks |
GET | Остатки на всех складах |
/suppliers/{code}/products |
GET | Товары от поставщика |
/warehouses/{code}/stocks |
GET | Остатки на складе |
app/pim/
├── api/ ← REST API (FastAPI)
│ ├── products.py
│ ├── prices.py
│ └── stocks.py
├── core/ ← Бизнес-логика
│ ├── products.py ← ProductService
│ ├── pricing.py ← PricingService
│ └── stocks.py ← StocksService
├── models/ ← SQLAlchemy модели
│ ├── product.py
│ ├── supplier.py ← NEW: Supplier, SupplierCost, SupplierStock
│ └── warehouse.py ← Warehouse, Stock
├── utils/
│ └── supplier_helpers.py ← NEW: get_supplier_cost(), update_supplier_cost()
└── tests/
Файл: utils/supplier_helpers.py
from utils.supplier_helpers import (
get_supplier_cost, # Цена от поставщика
get_best_supplier_cost, # Лучшая цена среди всех
get_supplier_stock, # Остаток у поставщика
update_supplier_cost # Обновить цену (с историей)
)
# Пример
cost = get_supplier_cost('JF-001', supplier_code='jf')
# → 43.92
cost, supplier = get_best_supplier_cost('JF-001')
# → (43.92, 'jf')
| Документ | Описание |
|---|---|
| ARCHITECTURE.md | Этот файл — главный! |
| standards/DATABASE.md | Структура БД (будет обновлён до v3.0) |
| CLAUDE.md | Контекст для Claude (API, процессы) |
| ../../data/PRODUCT_DATA_STANDARD.md | Стандарт работы с данными |
| ../../data/PRODUCT_RULES.md | Правила разбивки товаров |
| ../../data/connectors/MIGRATION_PLAN.md | План миграции на v3.0 |
| ../../data/connectors/ARCHITECTURE_COMPARISON.md | Анализ проблемы |
Папка: archive/2025-12-26-refactoring/
Устаревшие документы о рефакторинге (применялись к SQLite вместо PostgreSQL):
- REFACTORING_REPORT.md
- REFACTORING_FINAL_REPORT.md
- COMBINED_REFACTORING_PLAN.md
- DATA_REFACTORING_REPORT.md
- COMPLETION_REPORT.md
- PRICING_STOCK_TABLES.md
Причина архивации: Описывают SQLite базу, которая не используется в production.
| Компонент | PostgreSQL | SQLite | Статус |
|---|---|---|---|
| pim_products | ✅ 4626 товаров | ✅ 424 товара | Production = PostgreSQL |
| price_supplier_cost | ✅ 3268 цен | ✅ 424 цены | Production = PostgreSQL |
| stock_supplier | ✅ Есть | ✅ 27 остатков | Production = PostgreSQL |
| stock_inventory | ✅ 122 остатка | ❌ Нет | Только в PostgreSQL |
| stock_warehouses | ✅ 9 складов | ❌ Нет | Только в PostgreSQL |
Миграция 013 + 014 выполнена 2025-12-27:
/tmp/backup_before_013_20251227_065547.sql)Отчёт: /tmp/MIGRATION_013_014_COMPLETE_REPORT.md
Грузить в PIM только:
- ТипНоменклатуры='Запас' AND ЭтоНабор=false
Игнорировать:
- Наборы (ЭтоНабор=true)
- Услуги (ТипНоменклатуры='Услуга')
Подробнее: ../../CLAUDE.md (раздел "Правила работы с товарами")
Пример:
От поставщика: JF-DM30 (коробка 60 шт, все цвета)
↓ разбивка в PIM
JF-DM30-красный-1шт
JF-DM30-красный-3шт
JF-DM30-синий-1шт
...
Подробнее: ../../data/PRODUCT_RULES.md
| Версия | Дата | Изменения |
|---|---|---|
| 4.0.0 | 2025-12-27 | BREAKING: Переименованы все таблицы PRICE/STOCK с префиксами. Объединены системы ценообразования (миграции 013+014) |
| 3.0.0 | 2025-12-26 | Разделение цен (supplier_cost) и остатков (supplier_stock, stocks) |
| 2.0.0 | 2025-12-25 | Добавлены warehouses, stocks, PriceList/PriceListItem |
| 1.0.0 | 2025-12-23 | Первая версия PIM на PostgreSQL NocoDB |
Версия: 4.0.0
Автор: Claude Opus (Архитектор)
Дата: 2025-12-27
Для Claude: При любых вопросах по PIM — читать этот файл первым. Он актуальный и главный.