151 lines
7.6 KiB
Markdown
151 lines
7.6 KiB
Markdown
# QuoteForge - Claude Code Instructions
|
||
|
||
## Overview
|
||
Корпоративный конфигуратор серверов и формирование КП. MariaDB (RFQ_LOG) + SQLite для оффлайн.
|
||
|
||
## Development Phases
|
||
|
||
### Phase 1: Pricelists in MariaDB ✅ DONE
|
||
### Phase 2: Local SQLite Database ✅ DONE
|
||
|
||
### Phase 2.5: Full Offline Mode 🔶 IN PROGRESS
|
||
**Local-first architecture:** приложение ВСЕГДА работает с SQLite, MariaDB только для синхронизации.
|
||
|
||
**Принцип работы:**
|
||
- ВСЕ операции (CRUD) выполняются в SQLite
|
||
- При создании конфигурации:
|
||
1. Если online → проверить новые прайслисты на сервере → скачать если есть
|
||
2. Далее работаем с local_pricelists (и online, и offline одинаково)
|
||
- Background sync: push pending_changes → pull updates
|
||
|
||
**DONE:**
|
||
- ✅ Sync queue table (pending_changes) - `internal/localdb/models.go`
|
||
- ✅ Model converters: MariaDB ↔ SQLite - `internal/localdb/converters.go`
|
||
- ✅ LocalConfigurationService: все CRUD через SQLite - `internal/services/local_configuration.go`
|
||
- ✅ Pre-create pricelist check: `SyncPricelistsIfNeeded()` - `internal/services/sync/service.go`
|
||
- ✅ Push pending changes: `PushPendingChanges()` - sync service + handlers
|
||
- ✅ Sync API endpoints: `/api/sync/push`, `/pending/count`, `/pending`
|
||
- ✅ Integrate LocalConfigurationService in main.go (replace ConfigurationService)
|
||
- ✅ Add routes for new sync endpoints (`/api/sync/push`, `/pending/count`, `/pending`)
|
||
- ✅ ConfigurationGetter interface for handler compatibility
|
||
- ✅ Background sync worker: auto-sync every 5min (push + pull) - `internal/services/sync/worker.go`
|
||
- ✅ UI: sync status indicator (pending badge + sync button + offline/online dot) - `web/templates/partials/sync_status.html`
|
||
- ✅ RefreshPrices for local mode:
|
||
- `RefreshPrices()` / `RefreshPricesNoAuth()` в `local_configuration.go`
|
||
- Берёт цены из `local_components.current_price`
|
||
- Graceful degradation при отсутствии компонента
|
||
- Добавлено поле `price_updated_at` в `LocalConfiguration` (models.go:72)
|
||
- Обновлены converters для PriceUpdatedAt
|
||
- UI кнопка "Пересчитать цену" работает offline/online
|
||
- ✅ Fixed sync bugs:
|
||
- Duplicate entry error при update конфигураций (`sync/service.go:334-365`)
|
||
- pushConfigurationUpdate теперь проверяет наличие server_id перед update
|
||
- Если нет ID → получает из LocalConfiguration.ServerID или ищет на сервере
|
||
- Fixed setup.go: `settings.Password` → `settings.PasswordEncrypted`
|
||
|
||
**TODO:**
|
||
- ❌ Conflict resolution (Phase 4, last-write-wins default)
|
||
|
||
### UI Improvements ✅ MOSTLY DONE
|
||
|
||
**1. Sync UI + pricelist badge: ✅ DONE**
|
||
- ✅ `sync_status.html`: SVG иконки Online/Offline (кликабельные → открывают модал)
|
||
- ✅ Кнопка sync → иконка circular arrows (только full sync)
|
||
- ✅ Модальное окно "Статус системы" в `base.html` (info о БД, ошибки синхронизации)
|
||
- ✅ `configs.html`: badge с версией активного прайслиста
|
||
- ✅ Загрузка через `/api/pricelists/latest` при DOMContentLoaded
|
||
- ✅ Удалён dropdown с Push changes (упрощение UI)
|
||
|
||
**2. Прайслисты → вкладка в "Администратор цен": ✅ DONE**
|
||
- ✅ `base.html`: убрана ссылка "Прайслисты" из навигации
|
||
- ✅ `admin_pricing.html`: добавлена вкладка "Прайслисты"
|
||
- ✅ Логика перенесена из `pricelists.html` (table, create modal, CRUD)
|
||
- ✅ Route `/pricelists` → редирект на `/admin/pricing?tab=pricelists`
|
||
- ✅ Поддержка URL param `?tab=pricelists`
|
||
|
||
**3. Модал "Настройка цены" - кол-во котировок с учётом периода: ❌ TODO**
|
||
- Текущее: показывает только общее кол-во котировок
|
||
- Новое: показывать `N (всего: M)` где N - за выбранный период, M - всего
|
||
- ❌ `admin_pricing.html`: обновить `#modal-quote-count`
|
||
- ❌ `admin_pricing_handler.go`: в `/api/admin/pricing/preview` возвращать `quote_count_period` + `quote_count_total`
|
||
|
||
**4. Страница настроек: ❌ ОТЛОЖЕНО**
|
||
- Перенесено в Phase 3 (после основных UI улучшений)
|
||
|
||
### Phase 3: Projects and Specifications
|
||
- qt_projects, qt_specifications tables (MariaDB)
|
||
- Replace qt_configurations → Project/Specification hierarchy
|
||
- Fields: opty, customer_requirement, variant, qty, rev
|
||
- Local projects/specs with server sync
|
||
|
||
### Phase 4: Price Versioning
|
||
- Bind specifications to pricelist versions
|
||
- Price diff comparison
|
||
- Auto-cleanup expired pricelists (>1 year, usage_count=0)
|
||
|
||
## Tech Stack
|
||
Go 1.22+ | Gin | GORM | MariaDB 11 | SQLite (glebarez/sqlite) | htmx + Tailwind CDN | excelize
|
||
|
||
## Key Tables
|
||
|
||
### READ-ONLY (external systems)
|
||
- `lot` (lot_name PK, lot_description)
|
||
- `lot_log` (lot, supplier, date, price, quality, comments)
|
||
- `supplier` (supplier_name PK)
|
||
|
||
### MariaDB (qt_* prefix)
|
||
- `qt_lot_metadata` - component prices, methods, popularity
|
||
- `qt_categories` - category codes and names
|
||
- `qt_pricelists` - version snapshots (YYYY-MM-DD-NNN format)
|
||
- `qt_pricelist_items` - prices per pricelist
|
||
- `qt_projects` - uuid, opty, customer_requirement, name (Phase 3)
|
||
- `qt_specifications` - project_id, pricelist_id, variant, rev, qty, items JSON (Phase 3)
|
||
|
||
### SQLite (data/quoteforge.db)
|
||
- `connection_settings` - encrypted DB credentials (PasswordEncrypted field)
|
||
- `local_pricelists/items` - cached from server
|
||
- `local_components` - lot cache for offline search (with current_price)
|
||
- `local_configurations` - UUID, items, price_updated_at, sync_status (pending/synced/conflict), server_id
|
||
- `local_projects/specifications` - Phase 3
|
||
- `pending_changes` - sync queue (entity_type, uuid, op, payload, created_at, attempts, last_error)
|
||
|
||
## Business Logic
|
||
|
||
**Part number parsing:** `CPU_AMD_9654` → category=`CPU`, model=`AMD_9654`
|
||
|
||
**Price methods:** manual | median | average | weighted_median
|
||
|
||
**Price freshness:** fresh (<30d, ≥3 quotes) | normal (<60d) | stale (<90d) | critical
|
||
|
||
**Pricelist version:** `YYYY-MM-DD-NNN` (e.g., `2024-01-31-001`)
|
||
|
||
## API Endpoints
|
||
|
||
| Group | Endpoints |
|
||
|-------|-----------|
|
||
| Setup | GET/POST /setup, POST /setup/test |
|
||
| Components | GET /api/components, /api/categories |
|
||
| Pricelists | CRUD /api/pricelists, GET /latest, POST /compare |
|
||
| Projects | CRUD /api/projects/:uuid (Phase 3) |
|
||
| Specs | CRUD /api/specs/:uuid, POST /upgrade, GET /diff (Phase 3) |
|
||
| Configs | POST /:uuid/refresh-prices (обновить цены из local_components) |
|
||
| Sync | GET /status, POST /components, /pricelists, /push, /pull, /resolve-conflict |
|
||
| Export | GET /api/specs/:uuid/export, /api/projects/:uuid/export |
|
||
|
||
## Commands
|
||
```bash
|
||
go run ./cmd/server # Dev server
|
||
go run ./cmd/cron -job=X # cleanup-pricelists | update-prices | update-popularity
|
||
CGO_ENABLED=0 go build -ldflags="-s -w" -o bin/quoteforge ./cmd/server
|
||
```
|
||
|
||
## Code Style
|
||
- gofmt, structured logging (slog), wrap errors with context
|
||
- snake_case files, PascalCase types
|
||
- RBAC disabled: DB username = user_id via `models.EnsureDBUser()`
|
||
|
||
## UI Guidelines
|
||
- htmx (hx-get/post/target/swap), Tailwind CDN
|
||
- Freshness colors: green (fresh) → yellow → orange → red (critical)
|
||
- Sync status + offline indicator in header
|