Implements complete offline-first architecture with SQLite caching and MariaDB synchronization. Key features: - Local SQLite database for offline operation (data/quoteforge.db) - Connection settings with encrypted credentials - Component and pricelist caching with auto-sync - Sync API endpoints (/api/sync/status, /components, /pricelists, /all) - Real-time sync status indicator in UI with auto-refresh - Offline mode detection middleware - Migration tool for database initialization - Setup wizard for initial configuration New components: - internal/localdb: SQLite repository layer (components, pricelists, sync) - internal/services/sync: Synchronization service - internal/handlers/sync: Sync API handlers - internal/handlers/setup: Setup wizard handlers - internal/middleware/offline: Offline detection - cmd/migrate: Database migration tool UI improvements: - Setup page for database configuration - Sync status indicator with online/offline detection - Warning icons for pending synchronization - Auto-refresh every 30 seconds Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
59 lines
2.3 KiB
Go
59 lines
2.3 KiB
Go
package models
|
|
|
|
import (
|
|
"time"
|
|
)
|
|
|
|
// Pricelist represents a versioned snapshot of prices
|
|
type Pricelist struct {
|
|
ID uint `gorm:"primaryKey" json:"id"`
|
|
Version string `gorm:"size:20;uniqueIndex;not null" json:"version"` // Format: YYYY-MM-DD-NNN
|
|
Notification string `gorm:"size:500" json:"notification"` // Notification shown in configurator
|
|
CreatedAt time.Time `json:"created_at"`
|
|
CreatedBy string `gorm:"size:100" json:"created_by"`
|
|
IsActive bool `gorm:"default:true" json:"is_active"`
|
|
UsageCount int `gorm:"default:0" json:"usage_count"`
|
|
ExpiresAt *time.Time `json:"expires_at"`
|
|
ItemCount int `gorm:"-" json:"item_count,omitempty"` // Virtual field for display
|
|
}
|
|
|
|
func (Pricelist) TableName() string {
|
|
return "qt_pricelists"
|
|
}
|
|
|
|
// PricelistItem represents a single item in a pricelist
|
|
type PricelistItem struct {
|
|
ID uint `gorm:"primaryKey" json:"id"`
|
|
PricelistID uint `gorm:"not null;index:idx_pricelist_lot" json:"pricelist_id"`
|
|
LotName string `gorm:"size:255;not null;index:idx_pricelist_lot" json:"lot_name"`
|
|
Price float64 `gorm:"type:decimal(12,2);not null" json:"price"`
|
|
PriceMethod string `gorm:"size:20" json:"price_method"`
|
|
|
|
// Price calculation settings (snapshot from qt_lot_metadata)
|
|
PricePeriodDays int `gorm:"default:90" json:"price_period_days"`
|
|
PriceCoefficient float64 `gorm:"type:decimal(5,2);default:0" json:"price_coefficient"`
|
|
ManualPrice *float64 `gorm:"type:decimal(12,2)" json:"manual_price,omitempty"`
|
|
MetaPrices string `gorm:"size:1000" json:"meta_prices,omitempty"`
|
|
|
|
// Virtual fields for display
|
|
LotDescription string `gorm:"-" json:"lot_description,omitempty"`
|
|
Category string `gorm:"-" json:"category,omitempty"`
|
|
}
|
|
|
|
func (PricelistItem) TableName() string {
|
|
return "qt_pricelist_items"
|
|
}
|
|
|
|
// PricelistSummary is used for list views
|
|
type PricelistSummary struct {
|
|
ID uint `json:"id"`
|
|
Version string `json:"version"`
|
|
Notification string `json:"notification"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
CreatedBy string `json:"created_by"`
|
|
IsActive bool `json:"is_active"`
|
|
UsageCount int `json:"usage_count"`
|
|
ExpiresAt *time.Time `json:"expires_at"`
|
|
ItemCount int64 `json:"item_count"`
|
|
}
|