Files
QuoteForge/CLAUDE.md
Mikhail Chusavitin 9bd2acd4f7 Add offline RefreshPrices, fix sync bugs, implement auto-restart
- Implement RefreshPrices for local-first mode
  - Update prices from local_components.current_price cache
  - Graceful degradation when component not found
  - Add PriceUpdatedAt timestamp to LocalConfiguration model
  - Support both authenticated and no-auth price refresh

- Fix sync duplicate entry bug
  - pushConfigurationUpdate now ensures server_id exists before update
  - Fetch from LocalConfiguration.ServerID or search on server if missing
  - Update local config with server_id after finding

- Add application auto-restart after settings save
  - Implement restartProcess() using syscall.Exec
  - Setup handler signals restart via channel
  - Setup page polls /health endpoint and redirects when ready
  - Add "Back" button on setup page when settings exist

- Fix setup handler password handling
  - Use PasswordEncrypted field consistently
  - Support empty password by using saved value

- Improve sync status handling
  - Add fallback for is_offline check in SyncStatusPartial
  - Enhance background sync logging with prefixes

- Update CLAUDE.md documentation
  - Mark Phase 2.5 tasks as complete
  - Add UI Improvements section with future tasks
  - Update SQLite tables documentation

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-02 11:03:41 +03:00

7.7 KiB
Raw Blame History

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.Passwordsettings.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

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