158 lines
7.6 KiB
Markdown
158 lines
7.6 KiB
Markdown
# PriceForge - Claude Code Instructions
|
||
|
||
## 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`
|