# 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 🔶 IN PROGRESS **1. Sync icon + pricelist badge в header (tasks 4+2):** - ❌ `sync_status.html`: заменить текст Online/Offline на SVG иконку - ❌ Кнопка sync → иконка (circular arrows) вместо текста - ❌ Dropdown при клике: Push changes, Full sync, статус последней синхронизации - ❌ `configs.html`: рядом с кнопкой "Создать" показать badge с версией активного прайслиста - ❌ Загружать через `/api/pricelists/latest` при DOMContentLoaded **2. Прайслисты → вкладка в "Администратор цен" (task 1):** - ❌ `base.html`: убрать отдельную ссылку "Прайслисты" из навигации - ❌ `admin_pricing.html`: добавить 4-ю вкладку "Прайслисты" - ❌ Перенести логику из `pricelists.html` (table, create modal, CRUD) в эту вкладку - ❌ Route `/pricelists` → редирект на `/admin/pricing?tab=pricelists` или удалить **3. Страница настроек: расширить + синхронизация (task 3):** - ❌ `setup.html`: переделать на `{{template "base" .}}` структуру - ❌ Увеличить до `max-w-4xl`, разделить на 2 секции - ❌ Секция A: Подключение к БД (текущая форма) - ❌ Секция B: Синхронизация данных: - Статус Online/Offline - Кнопки: "Синхронизировать всё", "Обновить компоненты", "Обновить прайслисты" - Журнал синхронизации (последние N операций) - ❌ Возможно: новый API endpoint для sync log ### 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