Files
PriceForge/CLAUDE.md

132 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`
## Notes
- Главная страница должна вести на `/admin/pricing`.
- В UI не должно быть ссылок на конфигуратор, проекты и экспорт.
- **НЕ использовать SSE** - только фоновые задачи через Task Manager.