package localdb import ( "database/sql/driver" "encoding/json" "errors" "time" ) // AppSetting stores application settings in local SQLite type AppSetting struct { Key string `gorm:"primaryKey" json:"key"` Value string `gorm:"not null" json:"value"` UpdatedAt time.Time `json:"updated_at"` } func (AppSetting) TableName() string { return "app_settings" } // LocalConfigItem represents an item in a configuration type LocalConfigItem struct { LotName string `json:"lot_name"` Quantity int `json:"quantity"` UnitPrice float64 `json:"unit_price"` } // LocalConfigItems is a slice of LocalConfigItem that can be stored as JSON type LocalConfigItems []LocalConfigItem func (c LocalConfigItems) Value() (driver.Value, error) { return json.Marshal(c) } func (c *LocalConfigItems) Scan(value interface{}) error { if value == nil { *c = make(LocalConfigItems, 0) return nil } var bytes []byte switch v := value.(type) { case []byte: bytes = v case string: bytes = []byte(v) default: return errors.New("type assertion failed for LocalConfigItems") } return json.Unmarshal(bytes, c) } func (c LocalConfigItems) Total() float64 { var total float64 for _, item := range c { total += item.UnitPrice * float64(item.Quantity) } return total } // LocalStringList is a JSON-encoded list of strings stored as TEXT in SQLite. type LocalStringList []string func (s LocalStringList) Value() (driver.Value, error) { return json.Marshal(s) } func (s *LocalStringList) Scan(value interface{}) error { if value == nil { *s = make(LocalStringList, 0) return nil } var bytes []byte switch v := value.(type) { case []byte: bytes = v case string: bytes = []byte(v) default: return errors.New("type assertion failed for LocalStringList") } return json.Unmarshal(bytes, s) } // LocalConfiguration stores configurations in local SQLite type LocalConfiguration struct { ID uint `gorm:"primaryKey;autoIncrement" json:"id"` UUID string `gorm:"uniqueIndex;not null" json:"uuid"` ServerID *uint `json:"server_id"` // ID on MariaDB server, NULL if local only ProjectUUID *string `gorm:"index" json:"project_uuid,omitempty"` CurrentVersionID *string `gorm:"index" json:"current_version_id,omitempty"` IsActive bool `gorm:"default:true;index" json:"is_active"` Name string `gorm:"not null" json:"name"` Items LocalConfigItems `gorm:"type:text" json:"items"` // JSON stored as text in SQLite TotalPrice *float64 `json:"total_price"` CustomPrice *float64 `json:"custom_price"` Notes string `json:"notes"` IsTemplate bool `gorm:"default:false" json:"is_template"` ServerCount int `gorm:"default:1" json:"server_count"` PricelistID *uint `gorm:"index" json:"pricelist_id,omitempty"` OnlyInStock bool `gorm:"default:false" json:"only_in_stock"` PriceUpdatedAt *time.Time `gorm:"type:timestamp" json:"price_updated_at,omitempty"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` SyncedAt *time.Time `json:"synced_at"` SyncStatus string `gorm:"default:'local'" json:"sync_status"` // 'local', 'synced', 'modified' OriginalUserID uint `json:"original_user_id"` // UserID from MariaDB for reference OriginalUsername string `gorm:"not null;default:'';index" json:"original_username"` CurrentVersion *LocalConfigurationVersion `gorm:"foreignKey:CurrentVersionID;references:ID" json:"current_version,omitempty"` Versions []LocalConfigurationVersion `gorm:"foreignKey:ConfigurationUUID;references:UUID" json:"versions,omitempty"` } func (LocalConfiguration) TableName() string { return "local_configurations" } type LocalProject struct { ID uint `gorm:"primaryKey;autoIncrement" json:"id"` UUID string `gorm:"uniqueIndex;not null" json:"uuid"` ServerID *uint `json:"server_id,omitempty"` OwnerUsername string `gorm:"not null;index" json:"owner_username"` Name string `gorm:"not null" json:"name"` TrackerURL string `json:"tracker_url"` IsActive bool `gorm:"default:true;index" json:"is_active"` IsSystem bool `gorm:"default:false;index" json:"is_system"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` SyncedAt *time.Time `json:"synced_at,omitempty"` SyncStatus string `gorm:"default:'local'" json:"sync_status"` // local/synced/pending } func (LocalProject) TableName() string { return "local_projects" } // LocalConfigurationVersion stores immutable full snapshots for each configuration version type LocalConfigurationVersion struct { ID string `gorm:"primaryKey" json:"id"` ConfigurationUUID string `gorm:"not null;index:idx_lcv_config_created,priority:1;index:idx_lcv_config_version,priority:1;uniqueIndex:idx_lcv_config_version_unique,priority:1" json:"configuration_uuid"` VersionNo int `gorm:"not null;index:idx_lcv_config_version,sort:desc,priority:2;uniqueIndex:idx_lcv_config_version_unique,priority:2" json:"version_no"` Data string `gorm:"type:text;not null" json:"data"` ChangeNote *string `json:"change_note,omitempty"` CreatedBy *string `json:"created_by,omitempty"` AppVersion string `gorm:"size:64" json:"app_version,omitempty"` CreatedAt time.Time `gorm:"not null;autoCreateTime;index:idx_lcv_config_created,sort:desc,priority:2" json:"created_at"` Configuration *LocalConfiguration `gorm:"foreignKey:ConfigurationUUID;references:UUID" json:"configuration,omitempty"` } func (LocalConfigurationVersion) TableName() string { return "local_configuration_versions" } // LocalPricelist stores cached pricelists from server type LocalPricelist struct { ID uint `gorm:"primaryKey;autoIncrement" json:"id"` ServerID uint `gorm:"not null;uniqueIndex" json:"server_id"` // ID on MariaDB server Source string `gorm:"not null;default:'estimate';index:idx_local_pricelists_source_created_at,priority:1" json:"source"` Version string `gorm:"not null;index" json:"version"` Name string `json:"name"` CreatedAt time.Time `gorm:"index:idx_local_pricelists_source_created_at,priority:2,sort:desc" json:"created_at"` SyncedAt time.Time `json:"synced_at"` IsUsed bool `gorm:"default:false" json:"is_used"` // Used by any local configuration } func (LocalPricelist) TableName() string { return "local_pricelists" } // LocalPricelistItem stores pricelist items type LocalPricelistItem struct { ID uint `gorm:"primaryKey;autoIncrement" json:"id"` PricelistID uint `gorm:"not null;index" json:"pricelist_id"` LotName string `gorm:"not null" json:"lot_name"` Price float64 `gorm:"not null" json:"price"` AvailableQty *float64 `json:"available_qty,omitempty"` Partnumbers LocalStringList `gorm:"type:text" json:"partnumbers,omitempty"` } func (LocalPricelistItem) TableName() string { return "local_pricelist_items" } // LocalComponent stores cached components for offline search type LocalComponent struct { LotName string `gorm:"primaryKey" json:"lot_name"` LotDescription string `json:"lot_description"` Category string `json:"category"` Model string `json:"model"` CurrentPrice *float64 `json:"current_price"` SyncedAt time.Time `json:"synced_at"` } func (LocalComponent) TableName() string { return "local_components" } // LocalRemoteMigrationApplied tracks remote SQLite migrations received from server and applied locally. type LocalRemoteMigrationApplied struct { ID string `gorm:"primaryKey;size:128" json:"id"` Checksum string `gorm:"size:128;not null" json:"checksum"` AppVersion string `gorm:"size:64" json:"app_version,omitempty"` AppliedAt time.Time `gorm:"not null" json:"applied_at"` } func (LocalRemoteMigrationApplied) TableName() string { return "local_remote_migrations_applied" } // LocalSyncGuardState stores latest sync readiness decision for UI and preflight checks. type LocalSyncGuardState struct { ID uint `gorm:"primaryKey;autoIncrement" json:"id"` Status string `gorm:"size:32;not null;index" json:"status"` // ready|blocked|unknown ReasonCode string `gorm:"size:128" json:"reason_code,omitempty"` ReasonText string `gorm:"type:text" json:"reason_text,omitempty"` RequiredMinAppVersion *string `gorm:"size:64" json:"required_min_app_version,omitempty"` LastCheckedAt *time.Time `json:"last_checked_at,omitempty"` UpdatedAt time.Time `json:"updated_at"` } func (LocalSyncGuardState) TableName() string { return "local_sync_guard_state" } // PendingChange stores changes that need to be synced to the server type PendingChange struct { ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` EntityType string `gorm:"not null;index" json:"entity_type"` // "configuration", "project", "specification" EntityUUID string `gorm:"not null;index" json:"entity_uuid"` Operation string `gorm:"not null" json:"operation"` // "create", "update", "rollback", "deactivate", "reactivate", "delete" Payload string `gorm:"type:text" json:"payload"` // JSON snapshot of the entity CreatedAt time.Time `gorm:"not null" json:"created_at"` Attempts int `gorm:"default:0" json:"attempts"` // Retry count for sync LastError string `gorm:"type:text" json:"last_error,omitempty"` } func (PendingChange) TableName() string { return "pending_changes" }