214 lines
8.0 KiB
Go
214 lines
8.0 KiB
Go
package models
|
|
|
|
import (
|
|
"time"
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
// User represents a user account
|
|
type User struct {
|
|
ID uint `json:"id" gorm:"primaryKey;autoIncrement"`
|
|
Username string `json:"username" gorm:"uniqueIndex;not null;size:50"`
|
|
Email string `json:"email" gorm:"uniqueIndex;not null;size:255"`
|
|
Password string `json:"-" gorm:"-"` // Only used for input, never stored
|
|
PasswordHash string `json:"-" gorm:"column:password_hash;not null"`
|
|
BaseCurrency string `json:"base_currency" gorm:"not null;default:EUR;size:3"`
|
|
FuelStops []FuelStop `json:"fuel_stops,omitempty" gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE"`
|
|
Vehicles []Vehicle `json:"vehicles,omitempty" gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE"`
|
|
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime"`
|
|
UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime"`
|
|
}
|
|
|
|
// CheckPassword verifies a password against the stored hash
|
|
func (u *User) CheckPassword(password string) bool {
|
|
// Defensive programming - check for nil user
|
|
if u == nil {
|
|
return false
|
|
}
|
|
|
|
// Check for empty password or password hash
|
|
if password == "" || u.PasswordHash == "" {
|
|
return false
|
|
}
|
|
|
|
err := bcrypt.CompareHashAndPassword([]byte(u.PasswordHash), []byte(password))
|
|
return err == nil
|
|
}
|
|
|
|
// Vehicle represents a user's vehicle
|
|
type Vehicle struct {
|
|
ID uint `json:"id" gorm:"primaryKey;autoIncrement"`
|
|
UserID uint `json:"user_id" gorm:"not null;index"`
|
|
User User `json:"-" gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE"`
|
|
Name string `json:"name" gorm:"not null;size:100"`
|
|
Make string `json:"make" gorm:"size:50"`
|
|
Model string `json:"model" gorm:"size:50"`
|
|
Year int `json:"year" gorm:"default:0"`
|
|
LicensePlate string `json:"license_plate" gorm:"size:20"`
|
|
FuelType string `json:"fuel_type" gorm:"size:50"`
|
|
Notes string `json:"notes" gorm:"type:text"`
|
|
IsActive bool `json:"is_active" gorm:"default:true"`
|
|
FuelStops []FuelStop `json:"fuel_stops,omitempty" gorm:"foreignKey:VehicleID;constraint:OnDelete:CASCADE"`
|
|
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime"`
|
|
UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime"`
|
|
}
|
|
|
|
// FuelStop represents a fuel station stop record
|
|
type FuelStop struct {
|
|
ID uint `json:"id" gorm:"primaryKey;autoIncrement"`
|
|
UserID uint `json:"user_id" gorm:"not null;index"`
|
|
User User `json:"-" gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE"`
|
|
VehicleID uint `json:"vehicle_id" gorm:"not null;index"`
|
|
Vehicle Vehicle `json:"vehicle,omitempty" gorm:"foreignKey:VehicleID;constraint:OnDelete:CASCADE"`
|
|
Date time.Time `json:"date" gorm:"not null;type:date"`
|
|
StationName string `json:"station_name" gorm:"not null;size:100"`
|
|
Location string `json:"location" gorm:"not null;size:255"`
|
|
FuelType string `json:"fuel_type" gorm:"not null;size:50"`
|
|
Liters float64 `json:"liters" gorm:"not null;type:decimal(10,3)"`
|
|
PricePerL float64 `json:"price_per_l" gorm:"not null;type:decimal(10,4)"`
|
|
TotalPrice float64 `json:"total_price" gorm:"not null;type:decimal(10,2)"`
|
|
Currency string `json:"currency" gorm:"not null;default:EUR;size:3"`
|
|
Odometer int `json:"odometer" gorm:"default:0"`
|
|
TripLength float64 `json:"trip_length" gorm:"default:0;type:decimal(8,2);comment:Distance traveled since last fillup in km"`
|
|
Notes string `json:"notes" gorm:"type:text"`
|
|
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime"`
|
|
UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime"`
|
|
}
|
|
|
|
// FuelStopStats represents statistics for fuel consumption
|
|
type FuelStopStats struct {
|
|
TotalStops int `json:"total_stops"`
|
|
TotalLiters float64 `json:"total_liters"`
|
|
TotalSpent float64 `json:"total_spent"`
|
|
AveragePrice float64 `json:"average_price"`
|
|
AverageConsumption float64 `json:"average_consumption"`
|
|
LastFillup *FuelStop `json:"last_fillup"`
|
|
}
|
|
|
|
// FuelStopForm represents form data for creating/updating fuel stops
|
|
type FuelStopForm struct {
|
|
Date string `json:"date" form:"date"`
|
|
VehicleID uint `json:"vehicle_id" form:"vehicle_id"`
|
|
StationName string `json:"station_name" form:"station_name"`
|
|
Location string `json:"location" form:"location"`
|
|
FuelType string `json:"fuel_type" form:"fuel_type"`
|
|
Liters float64 `json:"liters" form:"liters"`
|
|
PricePerL float64 `json:"price_per_l" form:"price_per_l"`
|
|
TotalPrice float64 `json:"total_price" form:"total_price"`
|
|
Currency string `json:"currency" form:"currency"`
|
|
Odometer int `json:"odometer" form:"odometer"`
|
|
TripLength float64 `json:"trip_length" form:"trip_length"`
|
|
Notes string `json:"notes" form:"notes"`
|
|
}
|
|
|
|
// UserRegistrationForm represents form data for user registration
|
|
type UserRegistrationForm struct {
|
|
Username string `json:"username" form:"username"`
|
|
Email string `json:"email" form:"email"`
|
|
Password string `json:"password" form:"password"`
|
|
ConfirmPassword string `json:"confirm_password" form:"confirm_password"`
|
|
BaseCurrency string `json:"base_currency" form:"base_currency"`
|
|
}
|
|
|
|
// UserLoginForm represents form data for user login
|
|
type UserLoginForm struct {
|
|
Username string `json:"username" form:"username"`
|
|
Password string `json:"password" form:"password"`
|
|
}
|
|
|
|
// UserSettingsForm represents form data for user settings
|
|
type UserSettingsForm struct {
|
|
Email string `json:"email" form:"email"`
|
|
BaseCurrency string `json:"base_currency" form:"base_currency"`
|
|
}
|
|
|
|
// MonthlyStats represents monthly statistics for fuel consumption
|
|
type MonthlyStats struct {
|
|
Month string `json:"month"`
|
|
Year string `json:"year"`
|
|
TotalStops int `json:"total_stops"`
|
|
TotalLiters float64 `json:"total_liters"`
|
|
TotalSpent float64 `json:"total_spent"`
|
|
AveragePrice float64 `json:"average_price"`
|
|
}
|
|
|
|
// VehicleForm represents form data for creating/updating vehicles
|
|
type VehicleForm struct {
|
|
Name string `json:"name" form:"name"`
|
|
Make string `json:"make" form:"make"`
|
|
Model string `json:"model" form:"model"`
|
|
Year int `json:"year" form:"year"`
|
|
LicensePlate string `json:"license_plate" form:"license_plate"`
|
|
FuelType string `json:"fuel_type" form:"fuel_type"`
|
|
Notes string `json:"notes" form:"notes"`
|
|
IsActive bool `json:"is_active" form:"is_active"`
|
|
}
|
|
|
|
// ToFuelStop converts a FuelStopForm to a FuelStop
|
|
func (f *FuelStopForm) ToFuelStop(userID uint) (*FuelStop, error) {
|
|
date, err := time.Parse("2006-01-02", f.Date)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Use EUR as default currency if not provided
|
|
currency := f.Currency
|
|
if currency == "" {
|
|
currency = "EUR"
|
|
}
|
|
|
|
return &FuelStop{
|
|
UserID: userID,
|
|
VehicleID: f.VehicleID,
|
|
Date: date,
|
|
StationName: f.StationName,
|
|
Location: f.Location,
|
|
FuelType: f.FuelType,
|
|
Liters: f.Liters,
|
|
PricePerL: f.PricePerL,
|
|
TotalPrice: f.TotalPrice,
|
|
Currency: currency,
|
|
Odometer: f.Odometer,
|
|
TripLength: f.TripLength,
|
|
Notes: f.Notes,
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
}, nil
|
|
}
|
|
|
|
// ToVehicle converts a VehicleForm to a Vehicle
|
|
func (f *VehicleForm) ToVehicle(userID uint) *Vehicle {
|
|
return &Vehicle{
|
|
UserID: userID,
|
|
Name: f.Name,
|
|
Make: f.Make,
|
|
Model: f.Model,
|
|
Year: f.Year,
|
|
LicensePlate: f.LicensePlate,
|
|
FuelType: f.FuelType,
|
|
Notes: f.Notes,
|
|
IsActive: f.IsActive,
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
}
|
|
}
|
|
|
|
// ToUser converts a UserRegistrationForm to a User
|
|
func (f *UserRegistrationForm) ToUser() (*User, error) {
|
|
// Use EUR as default currency if not provided
|
|
currency := f.BaseCurrency
|
|
if currency == "" {
|
|
currency = "EUR"
|
|
}
|
|
|
|
return &User{
|
|
Username: f.Username,
|
|
Email: f.Email,
|
|
Password: f.Password,
|
|
BaseCurrency: currency,
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
}, nil
|
|
}
|