You've already forked openaccounting-server
forked from cybercinch/openaccounting-server
feat: add GORM integration with repository pattern
- Add GORM models in models/ directory with proper column tags - Create repository interfaces and implementations in core/repository/ - Add database package with MySQL and SQLite support - Add UUID ID utility for GORM models - Implement complete repository layer replacing SQL-based data access - Add database migrations and index creation - Support both MySQL and SQLite drivers with auto-migration 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
18
models/account.go
Normal file
18
models/account.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package models
|
||||
|
||||
// Account represents a financial account
|
||||
type Account struct {
|
||||
ID []byte `gorm:"type:BINARY(16);primaryKey"`
|
||||
OrgID []byte `gorm:"column:orgId;type:BINARY(16);not null"`
|
||||
Inserted uint64 `gorm:"column:inserted;not null"`
|
||||
Updated uint64 `gorm:"column:updated;not null"`
|
||||
Name string `gorm:"column:name;size:100;not null"`
|
||||
Parent []byte `gorm:"column:parent;type:BINARY(16);not null"`
|
||||
Currency string `gorm:"column:currency;size:10;not null"`
|
||||
Precision int `gorm:"column:precision;not null"`
|
||||
DebitBalance bool `gorm:"column:debitBalance;not null"`
|
||||
|
||||
Org Org `gorm:"foreignKey:OrgID"`
|
||||
Splits []Split `gorm:"foreignKey:AccountID"`
|
||||
Balances []Balance `gorm:"foreignKey:AccountID"`
|
||||
}
|
||||
13
models/api_key.go
Normal file
13
models/api_key.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package models
|
||||
|
||||
// APIKey represents API keys for users
|
||||
type APIKey struct {
|
||||
ID []byte `gorm:"type:BINARY(16);primaryKey"`
|
||||
Inserted uint64 `gorm:"column:inserted;not null"`
|
||||
Updated uint64 `gorm:"column:updated;not null"`
|
||||
UserID []byte `gorm:"column:userId;type:BINARY(16);not null"`
|
||||
Label string `gorm:"column:label;size:300;not null"`
|
||||
Deleted uint64 `gorm:"column:deleted"`
|
||||
|
||||
User User `gorm:"foreignKey:UserID"`
|
||||
}
|
||||
11
models/balance.go
Normal file
11
models/balance.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package models
|
||||
|
||||
// Balance represents an account balance at a point in time
|
||||
type Balance struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement"`
|
||||
Date uint64 `gorm:"column:date;not null"`
|
||||
AccountID []byte `gorm:"column:accountId;type:BINARY(16);not null"`
|
||||
Amount int64 `gorm:"column:amount;not null"`
|
||||
|
||||
Account Account `gorm:"foreignKey:AccountID"`
|
||||
}
|
||||
45
models/base.go
Normal file
45
models/base.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
"github.com/openaccounting/oa-server/core/util/id"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Base struct {
|
||||
ID []byte `gorm:"type:BINARY(16);primaryKey"`
|
||||
}
|
||||
|
||||
// GetUUID converts binary ID to UUID
|
||||
func (b *Base) GetUUID() (uuid.UUID, error) {
|
||||
return id.ToUUID(b.ID)
|
||||
}
|
||||
|
||||
// GetIDString returns string representation of the ID
|
||||
func (b *Base) GetIDString() string {
|
||||
return id.String(b.ID)
|
||||
}
|
||||
|
||||
// SetIDFromString parses string UUID into binary ID
|
||||
func (b *Base) SetIDFromString(s string) error {
|
||||
binID, err := id.FromString(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.ID = binID
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateID checks if the ID is a valid UUID
|
||||
func (b *Base) ValidateID() error {
|
||||
_, err := uuid.FromBytes(b.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// BeforeCreate GORM hook to set ID if empty
|
||||
func (b *Base) BeforeCreate(tx *gorm.DB) error {
|
||||
if len(b.ID) == 0 {
|
||||
b.ID = id.New()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
13
models/budget_item.go
Normal file
13
models/budget_item.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package models
|
||||
|
||||
// BudgetItem represents budget items
|
||||
type BudgetItem struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement"`
|
||||
OrgID []byte `gorm:"column:orgId;type:BINARY(16);not null"`
|
||||
AccountID []byte `gorm:"column:accountId;type:BINARY(16);not null"`
|
||||
Inserted uint64 `gorm:"column:inserted;not null"`
|
||||
Amount int64 `gorm:"column:amount;not null"`
|
||||
|
||||
Org Org `gorm:"foreignKey:OrgID"`
|
||||
Account Account `gorm:"foreignKey:AccountID"`
|
||||
}
|
||||
13
models/invite.go
Normal file
13
models/invite.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package models
|
||||
|
||||
// Invite represents organization invitations
|
||||
type Invite struct {
|
||||
ID string `gorm:"size:32;primaryKey"`
|
||||
OrgID []byte `gorm:"column:orgId;type:BINARY(16);not null"`
|
||||
Inserted uint64 `gorm:"column:inserted;not null"`
|
||||
Updated uint64 `gorm:"column:updated;not null"`
|
||||
Email string `gorm:"column:email;size:100;not null"`
|
||||
Accepted bool `gorm:"column:accepted;not null"`
|
||||
|
||||
Org Org `gorm:"foreignKey:OrgID"`
|
||||
}
|
||||
15
models/org.go
Normal file
15
models/org.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package models
|
||||
|
||||
// Org represents an organization
|
||||
type Org struct {
|
||||
ID []byte `gorm:"type:BINARY(16);primaryKey"`
|
||||
Inserted uint64 `gorm:"column:inserted;not null"`
|
||||
Updated uint64 `gorm:"column:updated;not null"`
|
||||
Name string `gorm:"column:name;size:100;not null"`
|
||||
Currency string `gorm:"column:currency;size:10;not null"`
|
||||
Precision int `gorm:"column:precision;not null"`
|
||||
Timezone string `gorm:"column:timezone;size:100;not null"`
|
||||
|
||||
Accounts []Account `gorm:"foreignKey:OrgID"`
|
||||
UserOrgs []UserOrg `gorm:"foreignKey:OrgID"`
|
||||
}
|
||||
18
models/permission.go
Normal file
18
models/permission.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package models
|
||||
|
||||
// Permission represents access control rules
|
||||
type Permission struct {
|
||||
ID []byte `gorm:"type:BINARY(16);primaryKey"`
|
||||
UserID []byte `gorm:"column:userId;type:BINARY(16)"`
|
||||
TokenID []byte `gorm:"column:tokenId;type:BINARY(16)"`
|
||||
OrgID []byte `gorm:"column:orgId;type:BINARY(16);not null"`
|
||||
AccountID []byte `gorm:"column:accountId;type:BINARY(16);not null"`
|
||||
Type uint `gorm:"column:type;not null"`
|
||||
Inserted uint64 `gorm:"column:inserted;not null"`
|
||||
Updated uint64 `gorm:"column:updated;not null"`
|
||||
|
||||
User User `gorm:"foreignKey:UserID"`
|
||||
Token Token `gorm:"foreignKey:TokenID"`
|
||||
Org Org `gorm:"foreignKey:OrgID"`
|
||||
Account Account `gorm:"foreignKey:AccountID"`
|
||||
}
|
||||
14
models/price.go
Normal file
14
models/price.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package models
|
||||
|
||||
// Price represents currency exchange rates
|
||||
type Price struct {
|
||||
ID []byte `gorm:"type:BINARY(16);primaryKey"`
|
||||
OrgID []byte `gorm:"column:orgId;type:BINARY(16);not null"`
|
||||
Currency string `gorm:"column:currency;size:10;not null"`
|
||||
Date uint64 `gorm:"column:date;not null"`
|
||||
Inserted uint64 `gorm:"column:inserted;not null"`
|
||||
Updated uint64 `gorm:"column:updated;not null"`
|
||||
Price float64 `gorm:"column:price;not null"`
|
||||
|
||||
Org Org `gorm:"foreignKey:OrgID"`
|
||||
}
|
||||
12
models/session.go
Normal file
12
models/session.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package models
|
||||
|
||||
// Session represents user sessions
|
||||
type Session struct {
|
||||
ID []byte `gorm:"type:BINARY(16);primaryKey"`
|
||||
Inserted uint64 `gorm:"column:inserted;not null"`
|
||||
Updated uint64 `gorm:"column:updated;not null"`
|
||||
UserID []byte `gorm:"column:userId;type:BINARY(16);not null"`
|
||||
Terminated uint64 `gorm:"column:terminated"`
|
||||
|
||||
User User `gorm:"foreignKey:UserID"`
|
||||
}
|
||||
17
models/split.go
Normal file
17
models/split.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package models
|
||||
|
||||
// Split represents a single entry in a transaction
|
||||
type Split struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement"`
|
||||
TransactionID []byte `gorm:"column:transactionId;type:BINARY(16);not null"`
|
||||
AccountID []byte `gorm:"column:accountId;type:BINARY(16);not null"`
|
||||
Date uint64 `gorm:"column:date;not null"`
|
||||
Inserted uint64 `gorm:"column:inserted;not null"`
|
||||
Updated uint64 `gorm:"column:updated;not null"`
|
||||
Amount int64 `gorm:"column:amount;not null"`
|
||||
NativeAmount int64 `gorm:"column:nativeAmount;not null"`
|
||||
Deleted bool `gorm:"column:deleted;default:false"`
|
||||
|
||||
Transaction Transaction `gorm:"foreignKey:TransactionID"`
|
||||
Account Account `gorm:"foreignKey:AccountID"`
|
||||
}
|
||||
10
models/token.go
Normal file
10
models/token.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package models
|
||||
|
||||
// Token represents an API token
|
||||
type Token struct {
|
||||
ID []byte `gorm:"type:BINARY(16);primaryKey"`
|
||||
Name string `gorm:"column:name;size:100"`
|
||||
UserOrgID uint `gorm:"column:userOrgId;not null"`
|
||||
|
||||
UserOrg UserOrg `gorm:"foreignKey:UserOrgID"`
|
||||
}
|
||||
18
models/transaction.go
Normal file
18
models/transaction.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package models
|
||||
|
||||
// Transaction represents a financial transaction
|
||||
type Transaction struct {
|
||||
ID []byte `gorm:"type:BINARY(16);primaryKey"`
|
||||
OrgID []byte `gorm:"column:orgId;type:BINARY(16);not null"`
|
||||
UserID []byte `gorm:"column:userId;type:BINARY(16);not null"`
|
||||
Date uint64 `gorm:"column:date;not null"`
|
||||
Inserted uint64 `gorm:"column:inserted;not null"`
|
||||
Updated uint64 `gorm:"column:updated;not null"`
|
||||
Description string `gorm:"column:description;size:300;not null"`
|
||||
Data string `gorm:"column:data;type:TEXT;not null"`
|
||||
Deleted bool `gorm:"column:deleted;default:false"`
|
||||
|
||||
Org Org `gorm:"foreignKey:OrgID"`
|
||||
User User `gorm:"foreignKey:UserID"`
|
||||
Splits []Split `gorm:"foreignKey:TransactionID"`
|
||||
}
|
||||
21
models/user.go
Normal file
21
models/user.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package models
|
||||
|
||||
// User represents a user account
|
||||
type User struct {
|
||||
ID []byte `gorm:"type:BINARY(16);primaryKey"`
|
||||
Inserted uint64 `gorm:"column:inserted;not null"`
|
||||
Updated uint64 `gorm:"column:updated;not null"`
|
||||
FirstName string `gorm:"column:firstName;size:50;not null"`
|
||||
LastName string `gorm:"column:lastName;size:50;not null"`
|
||||
Email string `gorm:"column:email;size:100;not null;unique"`
|
||||
PasswordHash string `gorm:"column:passwordHash;size:100;not null"`
|
||||
AgreeToTerms bool `gorm:"column:agreeToTerms;not null"`
|
||||
PasswordReset string `gorm:"column:passwordReset;size:32;not null"`
|
||||
EmailVerified bool `gorm:"column:emailVerified;not null"`
|
||||
EmailVerifyCode string `gorm:"column:emailVerifyCode;size:32;not null"`
|
||||
SignupSource string `gorm:"column:signupSource;size:100;not null"`
|
||||
|
||||
UserOrgs []UserOrg `gorm:"foreignKey:UserID"`
|
||||
Sessions []Session `gorm:"foreignKey:UserID"`
|
||||
APIKeys []APIKey `gorm:"foreignKey:UserID"`
|
||||
}
|
||||
12
models/user_org.go
Normal file
12
models/user_org.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package models
|
||||
|
||||
// UserOrg represents the relationship between users and organizations
|
||||
type UserOrg struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement"`
|
||||
UserID []byte `gorm:"column:userId;type:BINARY(16);not null"`
|
||||
OrgID []byte `gorm:"column:orgId;type:BINARY(16);not null"`
|
||||
Admin bool `gorm:"column:admin;default:false"`
|
||||
|
||||
User User `gorm:"foreignKey:UserID"`
|
||||
Org Org `gorm:"foreignKey:OrgID"`
|
||||
}
|
||||
Reference in New Issue
Block a user