diff --git a/db/account.go b/db/account.go new file mode 100644 index 0000000..e6178f7 --- /dev/null +++ b/db/account.go @@ -0,0 +1,12 @@ +package db + +import "gorm.io/gorm" + +// Account can be either card or wallet +type Account struct { + gorm.Model + Name string + Value uint64 + HaveCreditLine bool + CreditLine uint64 +} diff --git a/db/category.go b/db/category.go new file mode 100644 index 0000000..cc8e4a4 --- /dev/null +++ b/db/category.go @@ -0,0 +1,11 @@ +package db + +import "gorm.io/gorm" + +type Category struct { + gorm.Model + Name string + // Parent is used as a infinite sub-category structure + ParentID uint + Parent *Category +} diff --git a/db/db.go b/db/db.go new file mode 100644 index 0000000..a55d06f --- /dev/null +++ b/db/db.go @@ -0,0 +1,54 @@ +package db + +import ( + "errors" + "log" + "os" + "sync" + "time" + + "gorm.io/driver/sqlite" + "gorm.io/gorm" + "gorm.io/gorm/logger" +) + +var udb *gorm.DB +var conMu sync.Mutex + +var ( + ERROR_DB_NOT_INIT = errors.New("Database connection is not initialized") +) + +func Connect() *gorm.DB { + conMu.Lock() + defer conMu.Unlock() + if udb != nil { + return udb + } + newLogger := logger.New( + log.New(os.Stdout, "\r\n", log.LstdFlags), + logger.Config{ + SlowThreshold: time.Second, + LogLevel: logger.Error, + IgnoreRecordNotFoundError: true, + ParameterizedQueries: true, + Colorful: false, + }, + ) + gormDB, err := gorm.Open(sqlite.Open("gonuts.db"), &gorm.Config{ + Logger: newLogger, + }) + if err != nil { + log.Panic(err) + } + newUDB := gormDB + gormDB.AutoMigrate(&Account{}) + gormDB.AutoMigrate(&Category{}) + gormDB.AutoMigrate(&Item{}) + gormDB.AutoMigrate(&ItemPrice{}) + gormDB.AutoMigrate(&Payment{}) + gormDB.AutoMigrate(&ItemBought{}) + gormDB.AutoMigrate(&Income{}) + gormDB.AutoMigrate(&Debt{}) + return newUDB +} diff --git a/db/debt.go b/db/debt.go new file mode 100644 index 0000000..0e7f50b --- /dev/null +++ b/db/debt.go @@ -0,0 +1,18 @@ +package db + +import ( + "time" + + "gorm.io/gorm" +) + +type Debt struct { + gorm.Model + AccountID uint + Account *Account + Value uint64 + IOwe bool + Date time.Time + DateEnd time.Time + Finished bool +} diff --git a/db/income.go b/db/income.go new file mode 100644 index 0000000..0a5003f --- /dev/null +++ b/db/income.go @@ -0,0 +1,15 @@ +package db + +import ( + "time" + + "gorm.io/gorm" +) + +type Income struct { + gorm.Model + AccountID uint + Account *Account + Value uint64 + Date time.Time +} diff --git a/db/item.go b/db/item.go new file mode 100644 index 0000000..6134955 --- /dev/null +++ b/db/item.go @@ -0,0 +1,51 @@ +package db + +import "gorm.io/gorm" + +type Item struct { + gorm.Model + Name string + MetricType string + MetricValue uint64 + // + CategoryID uint + Category *Category + // + Proteins uint64 + Carbs uint64 + Fats uint64 + // + Prices []ItemPrice + CurrentPriceID uint + CurrentPrice *ItemPrice +} + +func GetItem(id uint, preloadPrices bool) (*Item, error) { + if udb == nil { + return nil, ERROR_DB_NOT_INIT + } + db := udb + if preloadPrices { + db = db.Preload("Prices") + } + var item Item + err := db.Preload("Category").Preload("CurrentPrice").First(&item, id).Error + return &item, err +} + +func GetItemToRootCat(id uint, preloadPrices bool) (*Item, error) { + if udb == nil { + return nil, ERROR_DB_NOT_INIT + } + db := udb + if preloadPrices { + db = db.Preload("Prices") + } + var item Item + err := db.Preload("Category.Parent", func(db *gorm.DB) *gorm.DB { + return db.Preload("Parent", func(db *gorm.DB) *gorm.DB { + return db.Preload("Parent") + }) + }).Preload("CurrentPrice").First(&item, id).Error + return &item, err +} diff --git a/db/item_bought.go b/db/item_bought.go new file mode 100644 index 0000000..90777b7 --- /dev/null +++ b/db/item_bought.go @@ -0,0 +1,12 @@ +package db + +import "gorm.io/gorm" + +type ItemBought struct { + gorm.Model + ItemID uint + Item *Item + Quantity uint + PaymentID uint + Payment *Payment +} diff --git a/db/item_price.go b/db/item_price.go new file mode 100644 index 0000000..52ef988 --- /dev/null +++ b/db/item_price.go @@ -0,0 +1,28 @@ +package db + +import ( + "errors" + "time" + + "gorm.io/gorm" +) + +type ItemPrice struct { + gorm.Model + ItemID uint + Item *Item + Price uint64 + ValidFrom time.Time + IsValid bool `gorm:"default:true"` +} + +var ( + ERROR_ITEMPRICE_VALID_FROM_ERR = errors.New("ValidFrom shall be initiated when created") +) + +func (i *ItemPrice) BeforeCreate(tx *gorm.DB) error { + if i.ValidFrom.IsZero() { + i.ValidFrom = time.Now() + } + return nil +} diff --git a/db/payment.go b/db/payment.go new file mode 100644 index 0000000..cbd16c6 --- /dev/null +++ b/db/payment.go @@ -0,0 +1,21 @@ +package db + +import ( + "time" + + "gorm.io/gorm" +) + +// For grocery payment +type Payment struct { + gorm.Model + AccountID uint + Account *Account + CategoryID uint + Category *Category + Name string + Descr string + Note string + Items []ItemBought + Date time.Time +}