docs: add bible/ as single source of architectural truth
- Created bible/ with hierarchical documentation (architecture, pricelists, vendor mapping, background tasks, data rules, patterns, API, operations, history) - CLAUDE.md reduced to one instruction: read and follow the bible - README.md reduced to quick start only - Removed MEMORY.md and csv_export.md (content consolidated into bible/) - Fixed stale facts found during audit: weighted_avg (not weighted_median), correct API route names (/export-csv, /recalculate-all) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
158
CLAUDE.md
158
CLAUDE.md
@@ -1,157 +1,3 @@
|
||||
# PriceForge - Claude Code Instructions
|
||||
# PriceForge
|
||||
|
||||
## Overview
|
||||
Администратор цен для работы с прайслистами, складскими справками и алертами.
|
||||
Источник данных: MariaDB. Локальная БД используется только для настроек и техданных приложения.
|
||||
|
||||
## Scope
|
||||
- Управление ценами и котировками компонентов
|
||||
- Управление прайслистами и их версиями
|
||||
- Импорт складских справок
|
||||
- Алерты по ценам/свежести данных
|
||||
- Подготовка к интеграции с API B2B площадок для автоматических котировок
|
||||
|
||||
## API Endpoints
|
||||
|
||||
| Group | Endpoints |
|
||||
|-------|-----------|
|
||||
| Setup | GET/POST /setup, POST /setup/test, GET /setup/status |
|
||||
| System | GET /health, GET /api/ping, GET /api/db-status, GET /api/current-user |
|
||||
| Components | GET /api/components, GET /api/components/:lot_name, GET /api/categories |
|
||||
| Pricelists | CRUD /api/pricelists, GET /api/pricelists/latest |
|
||||
| Pricing Admin | /api/admin/pricing/* |
|
||||
| Sync (diagnostics/minimal) | GET /api/sync/status, GET /api/sync/info, POST /api/sync/components, POST /api/sync/pricelists |
|
||||
|
||||
## Commands
|
||||
```bash
|
||||
# Development
|
||||
go run ./cmd/pfs
|
||||
make run
|
||||
|
||||
# Build
|
||||
make build
|
||||
make build-release
|
||||
|
||||
# Version
|
||||
./bin/pfs -version
|
||||
```
|
||||
|
||||
## Tech Stack
|
||||
Go 1.22+ | Gin | GORM | MariaDB 11 | htmx + Tailwind CDN | excelize
|
||||
|
||||
## Pricelist Sources
|
||||
|
||||
### Estimate
|
||||
- Создается из snapshot текущих цен компонентов (`qt_lot_metadata`)
|
||||
- Сохраняет все настройки ценообразования: `price_period_days`, `price_coefficient`, `manual_price`, `meta_prices`
|
||||
- Это основной прайслист для расчетов и оценок
|
||||
|
||||
### Warehouse
|
||||
- Создается из складских справок (`stock_log`)
|
||||
- **ВАЖНО**: НЕ должен загружать настройки цен из `lot_metadata`
|
||||
- **КРИТИЧНО**: В warehouse прайслист попадают ТОЛЬКО сопоставленные partnumbers (есть в `lot_partnumbers`)
|
||||
- Несопоставленные partnumbers НЕ должны попадать в прайслист ни в каком виде
|
||||
- Использует только weighted median (взвешенная медиана по количеству) из stock_log
|
||||
- Поле `price_method` всегда содержит "weighted_median"
|
||||
- Не содержит `price_period_days`, `price_coefficient`, `manual_price`, `meta_prices`
|
||||
- Это фактические складские цены без дополнительной обработки
|
||||
|
||||
### Competitor
|
||||
- Зарезервировано для будущих интеграций с API B2B площадок
|
||||
|
||||
## Background Tasks (Фоновые задачи)
|
||||
|
||||
**ВАЖНО**: Все долгие операции выполняются через Task Manager, НЕ через SSE (Server-Sent Events).
|
||||
|
||||
### Принцип работы:
|
||||
1. **Backend**: Хендлер создает фоновую задачу через `taskManager.Submit()` и возвращает `task_id`
|
||||
2. **Frontend**: Получает `task_id` и делает polling через `/api/tasks/:id` каждые 500ms
|
||||
3. **Уведомления**: Система автоматически показывает toast-уведомления при завершении задачи
|
||||
|
||||
### Типы задач:
|
||||
- `TaskTypeRecalculate` - пересчет всех цен компонентов
|
||||
- `TaskTypeStockImport` - импорт складской справки из .mxl/.xlsx
|
||||
- `TaskTypePricelistCreate` - создание прайслиста (estimate/warehouse/competitor)
|
||||
|
||||
### Структура задачи:
|
||||
```go
|
||||
type Task struct {
|
||||
ID string // UUID задачи
|
||||
Type TaskType // Тип задачи
|
||||
Status TaskStatus // running | completed | error
|
||||
Progress int // 0-100
|
||||
Message string // Текущее сообщение для UI
|
||||
Result map[string]interface{} // Результат при completed
|
||||
Error string // Текст ошибки при error
|
||||
CreatedAt time.Time
|
||||
DoneAt *time.Time
|
||||
}
|
||||
```
|
||||
|
||||
### Пример использования:
|
||||
```go
|
||||
taskID := h.taskManager.Submit(tasks.TaskTypeStockImport, func(ctx context.Context, progressCb func(int, string)) (map[string]interface{}, error) {
|
||||
// Выполнение задачи
|
||||
progressCb(50, "Половина выполнена")
|
||||
|
||||
// Возврат результата
|
||||
return map[string]interface{}{
|
||||
"rows_total": 100,
|
||||
"inserted": 95,
|
||||
}, nil
|
||||
})
|
||||
c.JSON(http.StatusOK, gin.H{"task_id": taskID})
|
||||
```
|
||||
|
||||
## Категории товаров (lot_category)
|
||||
|
||||
### Источник данных:
|
||||
Категории берутся из таблицы `lot` (поле `lot_category`), НЕ из `qt_lot_metadata`.
|
||||
|
||||
### При создании прайслистов:
|
||||
1. **Estimate**: Загружаем категории из `lot.lot_category` для всех компонентов
|
||||
2. **Warehouse**: Загружаем категории из `lot.lot_category` для всех позиций
|
||||
3. Сохраняем в поле `lot_category` таблицы `qt_pricelist_items`
|
||||
|
||||
### В модели:
|
||||
```go
|
||||
type PricelistItem struct {
|
||||
LotCategory *string `gorm:"column:lot_category;size:50" json:"category,omitempty"`
|
||||
// JSON поле называется "category" для фронтенда
|
||||
}
|
||||
```
|
||||
|
||||
### ВАЖНО:
|
||||
- Категория НЕ виртуальное поле - она сохраняется в БД при создании прайслиста
|
||||
- JOIN с таблицей `lot` нужен только для `lot_description`, категория уже есть в `qt_pricelist_items`
|
||||
- Никогда не получать категорию из имени элемента (lot name/названия). Всегда использовать категорию из `PricelistItem` (поле `lot_category` / JSON `category`).
|
||||
|
||||
## Notes
|
||||
- Главная страница должна вести на `/admin/pricing`.
|
||||
- В UI не должно быть ссылок на конфигуратор, проекты и экспорт.
|
||||
- **НЕ использовать SSE** - только фоновые задачи через Task Manager.
|
||||
|
||||
## Vendor Mapping Rules (2026-02-18)
|
||||
|
||||
- `lot_partnumbers` is the canonical mapping contract for external configurator.
|
||||
- Do not reintroduce multi-row mapping for one key: one `(vendor, partnumber)` -> one `lot_name`.
|
||||
- Resolver behavior is fixed:
|
||||
- first exact `vendor + partnumber`,
|
||||
- then fallback `vendor='' + partnumber`.
|
||||
- Composite vendor partnumbers must be implemented via internal bundle tables:
|
||||
- `qt_lot_bundles`
|
||||
- `qt_lot_bundle_items`
|
||||
- Bundle LOT is internal and hidden in regular LOT UI by default.
|
||||
- Ignore behavior:
|
||||
- do not use `stock_ignore_rules` for new logic,
|
||||
- use `qt_vendor_partnumber_seen.is_ignored` (cross-source).
|
||||
- Client compatibility requirement:
|
||||
- client still consumes LOT-based pricelists,
|
||||
- bundle expansion/allocation happens only inside PriceForge warehouse calculations.
|
||||
|
||||
### Related modules
|
||||
- Migration: `migrations/023_vendor_partnumber_global_mapping.sql`
|
||||
- Service/API: `internal/services/vendor_mapping.go`, `internal/handlers/pricing.go`
|
||||
- Resolver: `internal/lotmatch/matcher.go`
|
||||
- Warehouse calc: `internal/warehouse/snapshot.go`
|
||||
- Stock import seen+ignore: `internal/services/stock_import.go`
|
||||
Read [`bible/BIBLE.md`](bible/BIBLE.md) and all files it references before making any changes. The bible is the source of truth. Follow its rules without exception.
|
||||
|
||||
Reference in New Issue
Block a user