Files
PriceForge/CLAUDE.md

6.2 KiB
Raw Blame History

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

# 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)

Структура задачи:

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
}

Пример использования:

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

В модели:

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.