From 28dfe7ecdec45227ff0b4998a41185085052d8ab Mon Sep 17 00:00:00 2001 From: Alex <$(pass /github/email)> Date: Mon, 24 Mar 2025 17:45:33 +0100 Subject: [PATCH] refactoring --- go-backend/internal/models/bank_account.go | 41 ++- go-backend/internal/models/category.go | 48 +++ go-backend/internal/models/consents.go | 44 ++- go-backend/internal/models/drivers_licence.go | 61 ++- go-backend/internal/models/membership.go | 47 ++- .../internal/models/subscription_model.go | 39 ++ go-backend/internal/models/user.go | 346 +++++++++++------- go-backend/internal/models/verification.go | 39 +- .../subscription_model_repository.go | 2 +- go-backend/internal/services/user_service.go | 4 - go-backend/internal/validation/fields_test.go | 12 +- 11 files changed, 528 insertions(+), 155 deletions(-) create mode 100644 go-backend/internal/models/category.go diff --git a/go-backend/internal/models/bank_account.go b/go-backend/internal/models/bank_account.go index 3f6ab37..676769d 100644 --- a/go-backend/internal/models/bank_account.go +++ b/go-backend/internal/models/bank_account.go @@ -1,15 +1,50 @@ package models -import "time" +import ( + "GoMembership/pkg/logger" + "time" + + "gorm.io/gorm" +) type BankAccount struct { ID uint `gorm:"primaryKey"` CreatedAt time.Time UpdatedAt time.Time - MandateDateSigned time.Time `gorm:"not null" json:"mandate_date_signed"` + UserID uint `gorm:"index" json:"user_id"` + MandateDateSigned time.Time `json:"mandate_date_signed"` Bank string `json:"bank_name" binding:"safe_content"` AccountHolderName string `json:"account_holder_name" binding:"safe_content"` IBAN string `json:"iban" binding:"safe_content"` BIC string `json:"bic" binding:"safe_content"` - MandateReference string `gorm:"not null" json:"mandate_reference" binding:"safe_content"` + MandateReference string `json:"mandate_reference" binding:"safe_content"` +} + +func (b *BankAccount) Create(db *gorm.DB) error { + // b.ID = 0 + // only the children the belongs to association gets a reference id + if err := db.Create(b).Error; err != nil { + return err + } + logger.Info.Printf("BankAccount created: %#v", b) + return db.First(b, b.ID).Error // Refresh the object with all associations + +} + +func (b *BankAccount) Update(db *gorm.DB) error { + var existingBankAccount BankAccount + + logger.Info.Printf("updating BankAccount: %#v", b) + if err := db.First(&existingBankAccount, b.ID).Error; err != nil { + return err + } + + if err := db.Model(&existingBankAccount).Updates(b).Error; err != nil { + return err + } + return db.First(b, b.ID).Error +} + +func (b *BankAccount) Delete(db *gorm.DB) error { + return db.Delete(&b).Error } diff --git a/go-backend/internal/models/category.go b/go-backend/internal/models/category.go new file mode 100644 index 0000000..7639826 --- /dev/null +++ b/go-backend/internal/models/category.go @@ -0,0 +1,48 @@ +package models + +import ( + "GoMembership/pkg/logger" + + "gorm.io/gorm" +) + +type Category struct { + ID uint `json:"id" gorm:"primaryKey"` + Name string `json:"category" binding:"safe_content"` +} + +func (c *Category) Create(db *gorm.DB) error { + return db.Transaction(func(tx *gorm.DB) error { + // Create the base User record (omit associations to handle them separately) + if err := tx.Create(c).Error; err != nil { + return err + } + logger.Info.Printf("Category created: %#v", c) + // Preload all associations to return the fully populated User + return tx. + First(c, c.ID).Error // Refresh the user object with all associations + }) +} + +func (c *Category) Update(db *gorm.DB) error { + + return db.Transaction(func(tx *gorm.DB) error { + // Check if the user exists in the database + var existingCategory Category + + logger.Info.Printf("updating Category: %#v", c) + if err := tx.First(&existingCategory, c.ID).Error; err != nil { + return err + } + + if err := tx.Model(&existingCategory).Updates(c).Error; err != nil { + return err + } + return tx.First(c, c.ID).Error + }) + +} + +func (c *Category) Delete(db *gorm.DB) error { + return db.Delete(&c).Error +} diff --git a/go-backend/internal/models/consents.go b/go-backend/internal/models/consents.go index 0712ea6..965c167 100644 --- a/go-backend/internal/models/consents.go +++ b/go-backend/internal/models/consents.go @@ -1,17 +1,55 @@ package models import ( + "GoMembership/pkg/logger" + "strings" "time" + + "gorm.io/gorm" ) type Consent struct { + ID uint `gorm:"primaryKey" json:"id"` CreatedAt time.Time UpdatedAt time.Time FirstName string `gorm:"not null" json:"first_name" binding:"safe_content"` LastName string `gorm:"not null" json:"last_name" binding:"safe_content"` Email string `json:"email" binding:"email,safe_content"` ConsentType string `gorm:"not null" json:"consent_type" binding:"safe_content"` - ID uint `gorm:"primaryKey"` - User User - UserID uint + UserID uint `gorm:"not null" json:"user_id"` +} + +func (c *Consent) BeforeSave(tx *gorm.DB) (err error) { + c.Email = strings.ToLower(c.Email) + return nil +} +func (c *Consent) Create(db *gorm.DB) error { + if err := db.Create(c).Error; err != nil { + return err + } + logger.Info.Printf("Consent created: %#v", c) + return db.First(c, c.ID).Error // Refresh the user object with all associations +} + +func (c *Consent) Update(db *gorm.DB) error { + + return db.Transaction(func(tx *gorm.DB) error { + // Check if the user exists in the database + var existingConsent Consent + + logger.Info.Printf("updating Consent: %#v", c) + if err := tx.First(&existingConsent, c.ID).Error; err != nil { + return err + } + + if err := tx.Model(&existingConsent).Updates(c).Error; err != nil { + return err + } + return tx.First(c, c.ID).Error + }) + +} + +func (c *Consent) Delete(db *gorm.DB) error { + return db.Delete(&c).Error } diff --git a/go-backend/internal/models/drivers_licence.go b/go-backend/internal/models/drivers_licence.go index 095643a..c90a5e7 100644 --- a/go-backend/internal/models/drivers_licence.go +++ b/go-backend/internal/models/drivers_licence.go @@ -1,7 +1,11 @@ package models import ( + "GoMembership/pkg/logger" + "fmt" "time" + + "gorm.io/gorm" ) type Licence struct { @@ -9,15 +13,54 @@ type Licence struct { UserID uint `json:"user_id"` CreatedAt time.Time UpdatedAt time.Time - Status int8 `json:"status" binding:"omitempty,number"` - Number string `json:"number" binding:"omitempty,safe_content"` - IssuedDate time.Time `json:"issued_date" binding:"omitempty"` - ExpirationDate time.Time `json:"expiration_date" binding:"omitempty"` - IssuingCountry string `json:"country" binding:"safe_content"` - Categories []Category `json:"categories" gorm:"many2many:licence_2_categories"` + Status int8 `json:"status" binding:"omitempty,number"` + Number string `json:"number" binding:"omitempty,safe_content"` + IssuedDate time.Time `json:"issued_date" binding:"omitempty"` + ExpirationDate time.Time `json:"expiration_date" binding:"omitempty"` + IssuingCountry string `json:"country" binding:"safe_content"` + Categories []*Category `json:"categories" gorm:"many2many:licence_2_categories"` } -type Category struct { - ID uint `json:"id" gorm:"primaryKey"` - Name string `json:"category" binding:"safe_content"` +func (l *Licence) BeforeSafe(tx *gorm.DB) error { + if err := tx.Model(l).Association("Categories").Replace(l.Categories); err != nil { + return fmt.Errorf("failed to link categories: %w", err) + } + + return nil +} + +func (l *Licence) Create(db *gorm.DB) error { + if err := db.Omit("Categories").Create(l).Error; err != nil { + return err + } + + if err := db.Model(&l).Association("Categories").Replace(l.Categories); err != nil { + return err + } + + logger.Info.Printf("Licence created: %#v", l) + return db.Preload("Categories").First(l, l.ID).Error // Refresh the object with Categories +} + +func (l *Licence) Update(db *gorm.DB) error { + + return db.Transaction(func(tx *gorm.DB) error { + // Check if the user exists in the database + var existingLicence Licence + + logger.Info.Printf("updating Licence: %#v", l) + if err := tx.First(&existingLicence, l.ID).Error; err != nil { + return err + } + + if err := tx.Model(&existingLicence).Updates(l).Error; err != nil { + return err + } + return tx.First(l, l.ID).Error + }) + +} + +func (l *Licence) Delete(db *gorm.DB) error { + return db.Delete(&l).Error } diff --git a/go-backend/internal/models/membership.go b/go-backend/internal/models/membership.go index 2e02d2e..81f9cec 100644 --- a/go-backend/internal/models/membership.go +++ b/go-backend/internal/models/membership.go @@ -1,8 +1,15 @@ package models -import "time" +import ( + "GoMembership/pkg/logger" + "time" + + "gorm.io/gorm" +) type Membership struct { + ID uint `gorm:"primaryKey" json:"id"` + UserID uint `gorm:"index" json:"user_id"` CreatedAt time.Time UpdatedAt time.Time StartDate time.Time `json:"start_date"` @@ -11,5 +18,41 @@ type Membership struct { SubscriptionModel SubscriptionModel `gorm:"foreignKey:SubscriptionModelID" json:"subscription_model"` SubscriptionModelID uint `json:"subsription_model_id"` ParentMembershipID uint `json:"parent_member_id" binding:"omitempty,omitnil,number"` - ID uint `json:"id"` +} + +func (m *Membership) BeforeSave(tx *gorm.DB) error { + m.SubscriptionModelID = m.SubscriptionModel.ID + return nil +} + +func (m *Membership) Create(db *gorm.DB) error { + if err := db.Create(m).Error; err != nil { + return err + } + logger.Info.Printf("Membership created: %#v", m) + + return db.Preload("SubscriptionModel").First(m, m.ID).Error // Refresh the user object with SubscriptionModel +} + +func (m *Membership) Update(db *gorm.DB) error { + + return db.Transaction(func(tx *gorm.DB) error { + // Check if the user exists in the database + var existingMembership Membership + + logger.Info.Printf("updating Membership: %#v", m) + if err := tx.First(&existingMembership, m.ID).Error; err != nil { + return err + } + + if err := tx.Model(&existingMembership).Updates(m).Error; err != nil { + return err + } + return tx.First(m, m.ID).Error + }) + +} + +func (m *Membership) Delete(db *gorm.DB) error { + return db.Delete(&m).Error } diff --git a/go-backend/internal/models/subscription_model.go b/go-backend/internal/models/subscription_model.go index 751496a..46714e7 100644 --- a/go-backend/internal/models/subscription_model.go +++ b/go-backend/internal/models/subscription_model.go @@ -1,7 +1,10 @@ package models import ( + "GoMembership/pkg/logger" "time" + + "gorm.io/gorm" ) type SubscriptionModel struct { @@ -17,3 +20,39 @@ type SubscriptionModel struct { IncludedPerYear int16 `json:"included_hours_per_year"` IncludedPerMonth int16 `json:"included_hours_per_month"` } + +func (s *SubscriptionModel) Create(db *gorm.DB) error { + return db.Transaction(func(tx *gorm.DB) error { + // Create the base User record (omit associations to handle them separately) + if err := tx.Create(s).Error; err != nil { + return err + } + logger.Info.Printf("SubscriptionModel created: %#v", s) + // Preload all associations to retuvn the fully populated User + return tx. + First(s, s.ID).Error // Refresh the user object with all associations + }) +} + +func (s *SubscriptionModel) Update(db *gorm.DB) error { + + return db.Transaction(func(tx *gorm.DB) error { + // Check if the user exists in the database + var existingSubscriptionModel SubscriptionModel + + logger.Info.Printf("updating SubscriptionModel: %#v", s) + if err := tx.First(&existingSubscriptionModel, s.ID).Error; err != nil { + return err + } + + if err := tx.Model(&existingSubscriptionModel).Updates(s).Error; err != nil { + return err + } + return tx.First(s, s.ID).Error + }) + +} + +func (s *SubscriptionModel) Delete(db *gorm.DB) error { + return db.Delete(&s).Error +} diff --git a/go-backend/internal/models/user.go b/go-backend/internal/models/user.go index 49974c3..304de0d 100644 --- a/go-backend/internal/models/user.go +++ b/go-backend/internal/models/user.go @@ -8,6 +8,7 @@ import ( "GoMembership/pkg/logger" "fmt" "slices" + "strings" "time" "github.com/alexedwards/argon2id" @@ -18,44 +19,43 @@ import ( ) type User struct { - ID uint `gorm:"primarykey" json:"id"` - CreatedAt time.Time - UpdatedAt time.Time - DeletedAt *time.Time - DateOfBirth time.Time `gorm:"not null" json:"dateofbirth" binding:"required_unless=RoleID 0,safe_content"` - Company string `json:"company" binding:"omitempty,omitnil,safe_content"` - Phone string `json:"phone" binding:"omitempty,omitnil,safe_content"` - Notes string `json:"notes" binding:"safe_content"` - FirstName string `gorm:"not null" json:"first_name" binding:"required,safe_content"` - Password string `json:"password" binding:"safe_content"` - Email string `gorm:"uniqueIndex:idx_users_email,not null" json:"email" binding:"required,email,safe_content"` - LastName string `gorm:"not null" json:"last_name" binding:"required,safe_content"` - ProfilePicture string `json:"profile_picture" binding:"omitempty,omitnil,image,safe_content"` - Address string `gorm:"not null" json:"address" binding:"required,safe_content"` - ZipCode string `gorm:"not null" json:"zip_code" binding:"required,alphanum,safe_content"` - City string `form:"not null" json:"city" binding:"required,alphaunicode,safe_content"` - Consents []Consent `gorm:"constraint:OnUpdate:CASCADE"` - BankAccount BankAccount `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"bank_account"` - BankAccountID uint - Verifications *[]Verification `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE"` - Membership Membership `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"membership"` - MembershipID uint - Licence *Licence `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"licence"` - LicenceID uint - PaymentStatus int8 `json:"payment_status"` - Status int8 `json:"status"` - RoleID int8 `json:"role_id"` + ID uint `gorm:"primaryKey" json:"id"` + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt *time.Time + DateOfBirth time.Time `gorm:"not null" json:"dateofbirth" binding:"required_unless=RoleID 0,safe_content"` + Company string `json:"company" binding:"omitempty,omitnil,safe_content"` + Phone string `json:"phone" binding:"omitempty,omitnil,safe_content"` + Notes string `json:"notes" binding:"safe_content"` + FirstName string `gorm:"not null" json:"first_name" binding:"required,safe_content"` + Password string `json:"password" binding:"safe_content"` + Email string `gorm:"uniqueIndex:idx_users_email,not null" json:"email" binding:"required,email,safe_content"` + LastName string `gorm:"not null" json:"last_name" binding:"required,safe_content"` + Address string `gorm:"not null" json:"address" binding:"required,safe_content"` + ZipCode string `gorm:"not null" json:"zip_code" binding:"required,alphanum,safe_content"` + City string `form:"not null" json:"city" binding:"required,alphaunicode,safe_content"` + Consents []Consent `gorm:"constraint:OnUpdate:CASCADE"` + BankAccount *BankAccount `gorm:"foreignkey:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"bank_account"` + Verifications []Verification `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE"` + Membership *Membership `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"membership"` + Licence *Licence `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"licence"` + Status int8 `json:"status"` + RoleID int8 `json:"role_id"` } func (u *User) AfterCreate(tx *gorm.DB) (err error) { - if u.BankAccount.ID != 0 && u.BankAccount.MandateReference == "" { - mandateReference := u.GenerateMandateReference() - - return tx.Model(&u.BankAccount).Update("MandateReference", mandateReference).Error + if u.BankAccount != nil && u.BankAccount.MandateReference == "" { + u.BankAccount.MandateReference = u.GenerateMandateReference() + u.BankAccount.Update(tx) } return nil } +func (u *User) BeforeSave(tx *gorm.DB) (err error) { + u.Email = strings.ToLower(u.Email) + return nil +} + func (u *User) GenerateMandateReference() string { return fmt.Sprintf("%s%d%s", time.Now().Format("20060102"), u.ID, u.BankAccount.IBAN) } @@ -86,29 +86,128 @@ func (u *User) Delete(db *gorm.DB) error { } func (u *User) Create(db *gorm.DB) error { + return db.Transaction(func(tx *gorm.DB) error { - // Create the base User record (omit associations to handle them separately) - if err := tx.Create(u).Error; err != nil { + if err := tx.Preload(clause.Associations).Create(u).Error; err != nil { return err } - // Replace associated Categories (assumes Categories already exist) - if u.Licence != nil && len(u.Licence.Categories) > 0 { - if err := tx.Model(u.Licence).Association("Categories").Replace(u.Licence.Categories); err != nil { - return err - } - } - logger.Info.Printf("user created: %#v", u.Safe()) - // Preload all associations to return the fully populated User - return tx. - Preload("Membership"). - Preload("Membership.SubscriptionModel"). - Preload("Licence"). - Preload("Licence.Categories"). - First(u, u.ID).Error // Refresh the user object with all associations + return nil }) } +// return db.Transaction(func(tx *gorm.DB) error { + +// // Initialize slices/pointers if nil +// if u.Verifications == nil { +// u.Verifications = &[]Verification{} +// } + +// // Create base user first +// if err := tx.Omit(clause.Associations).Create(u).Error; err != nil { +// return fmt.Errorf("failed to create user: %w", err) +// } + +// // Handle BankAccount +// if u.BankAccount != (BankAccount{}) { +// u.BankAccount.MandateReference = u.GenerateMandateReference() +// if err := tx.Create(&u.BankAccount).Error; err != nil { +// return fmt.Errorf("failed to create bank account: %w", err) +// } +// if err := tx.Model(u).Update("bank_account_id", u.BankAccount.ID).Error; err != nil { +// return fmt.Errorf("failed to link bank account: %w", err) +// } +// } + +// // Handle Membership and SubscriptionModel +// if u.Membership != (Membership{}) { +// if err := tx.Create(&u.Membership).Error; err != nil { +// return fmt.Errorf("failed to create membership: %w", err) +// } +// if err := tx.Model(u).Update("membership_id", u.Membership.ID).Error; err != nil { +// return fmt.Errorf("failed to link membership: %w", err) +// } +// } + +// // Handle Licence and Categories +// if u.Licence != nil { +// u.Licence.UserID = u.ID +// if err := tx.Create(u.Licence).Error; err != nil { +// return fmt.Errorf("failed to create licence: %w", err) +// } + +// if len(u.Licence.Categories) > 0 { +// if err := tx.Model(u.Licence).Association("Categories").Replace(u.Licence.Categories); err != nil { +// return fmt.Errorf("failed to link categories: %w", err) +// } +// } + +// if err := tx.Model(u).Update("licence_id", u.Licence.ID).Error; err != nil { +// return fmt.Errorf("failed to link licence: %w", err) +// } +// } + +// // Handle Consents +// for i := range u.Consents { +// u.Consents[i].UserID = u.ID +// } +// if len(u.Consents) > 0 { +// if err := tx.Create(&u.Consents).Error; err != nil { +// return fmt.Errorf("failed to create consents: %w", err) +// } +// } + +// // Handle Verifications +// for i := range *u.Verifications { +// (*u.Verifications)[i].UserID = u.ID +// } +// if len(*u.Verifications) > 0 { +// if err := tx.Create(u.Verifications).Error; err != nil { +// return fmt.Errorf("failed to create verifications: %w", err) +// } +// } + +// // Reload the complete user with all associations +// return tx.Preload(clause.Associations). +// Preload("Membership.SubscriptionModel"). +// Preload("Licence.Categories"). +// First(u, u.ID).Error +// }) +// } + +// func (u *User) Create(db *gorm.DB) error { +// return db.Transaction(func(tx *gorm.DB) error { +// // Create the base User record (omit associations to handle them separately) +// if err := tx.Create(u).Error; err != nil { +// return err +// } +// for i := range u.Consents { +// u.Consents[i].UserID = u.ID +// } + +// for i := range *u.Verifications { +// (*u.Verifications)[i].UserID = u.ID +// } + +// if err := tx.Session(&gorm.Session{FullSaveAssociations: true}).Updates(u).Error; err != nil { +// return err +// } +// // Replace associated Categories (assumes Categories already exist) +// if u.Licence != nil && len(u.Licence.Categories) > 0 { +// if err := tx.Model(u.Licence).Association("Categories").Replace(u.Licence.Categories); err != nil { +// return err +// } +// } +// logger.Info.Printf("user created: %#v", u.Safe()) +// // Preload all associations to return the fully populated User +// return tx. +// Preload(clause.Associations). +// Preload("Membership.SubscriptionModel"). +// Preload("Licence.Categories"). +// First(u, u.ID).Error // Refresh the user object with all associations +// }) +// } + func (u *User) Update(db *gorm.DB) error { err := db.Transaction(func(tx *gorm.DB) error { @@ -117,16 +216,11 @@ func (u *User) Update(db *gorm.DB) error { logger.Info.Printf("updating user: %#v", u) if err := tx. - Preload("Membership"). - Preload("Membership.SubscriptionModel"). - Preload("Licence"). - Preload("Licence.Categories"). - Preload("Verifications"). First(&existingUser, u.ID).Error; err != nil { return err } // Update the user's main fields - result := tx.Session(&gorm.Session{FullSaveAssociations: true}).Omit("Password", "Membership", "Licence", "Verifications").Updates(u) + result := tx.Session(&gorm.Session{FullSaveAssociations: true}).Omit("Password", "Verifications", "Licence.Categories").Updates(u) if result.Error != nil { logger.Error.Printf("User update error in update user: %#v", result.Error) return result.Error @@ -143,28 +237,28 @@ func (u *User) Update(db *gorm.DB) error { } } - // Update the Membership if provided - if u.Membership.ID != 0 { - if err := tx.Model(&existingUser.Membership).Updates(u.Membership).Error; err != nil { - logger.Error.Printf("Membership update error in update user: %#v", err) - return err - } - } - if u.Licence != nil { - u.Licence.UserID = existingUser.ID + // // Update the Membership if provided + // if u.Membership.ID != 0 { + // if err := tx.Model(&existingUser.Membership).Updates(u.Membership).Where("id = ?", existingUser.Membership.ID).Error; err != nil { + // logger.Error.Printf("Membership update error in update user: %#v", err) + // return err + // } + // } + // if u.Licence != nil { + // u.Licence.UserID = existingUser.ID - if err := tx.Save(u.Licence).Error; err != nil { - return err - } + // if err := tx.Save(u.Licence).Error; err != nil { + // return err + // } - if err := tx.Model(&existingUser).Update("LicenceID", u.Licence.ID).Error; err != nil { - return err - } + // if err := tx.Model(&existingUser).Update("LicenceID", u.Licence.ID).Error; err != nil { + // return err + // } - if err := tx.Model(u.Licence).Association("Categories").Replace(u.Licence.Categories); err != nil { - return err - } - } + // if err := tx.Model(u.Licence).Association("Categories").Replace(u.Licence.Categories); err != nil { + // return err + // } + // } // if u.Licence != nil { // if existingUser.Licence == nil || existingUser.LicenceID == 0 { // u.Licence.UserID = existingUser.ID // Ensure Licence belongs to User @@ -187,7 +281,13 @@ func (u *User) Update(db *gorm.DB) error { // } if u.Verifications != nil { - if err := tx.Save(*u.Verifications).Error; err != nil { + if err := tx.Save(u.Verifications).Error; err != nil { + return err + } + } + + if u.Licence != nil { + if err := tx.Model(u.Licence).Association("Categories").Replace(u.Licence.Categories); err != nil { return err } } @@ -199,11 +299,9 @@ func (u *User) Update(db *gorm.DB) error { } return db. - Preload("Membership"). + Preload(clause.Associations). Preload("Membership.SubscriptionModel"). - Preload("Licence"). Preload("Licence.Categories"). - Preload("Verifications"). First(&u, u.ID).Error } @@ -211,11 +309,8 @@ func (u *User) FromID(db *gorm.DB, userID *uint) error { var user User result := db. Preload(clause.Associations). - Preload("Membership"). Preload("Membership.SubscriptionModel"). - Preload("Licence"). Preload("Licence.Categories"). - Preload("Verifications"). First(&user, userID) if result.Error != nil { if result.Error == gorm.ErrRecordNotFound { @@ -231,11 +326,8 @@ func (u *User) FromEmail(db *gorm.DB, email *string) error { var user User result := db. Preload(clause.Associations). - Preload("Membership"). Preload("Membership.SubscriptionModel"). - Preload("Licence"). Preload("Licence.Categories"). - Preload("Verifications"). Where("email = ?", email).First(&user) if result.Error != nil { if result.Error == gorm.ErrRecordNotFound { @@ -284,7 +376,7 @@ func (u *User) IsSupporter() bool { func (u *User) SetVerification(verificationType string) (*Verification, error) { if u.Verifications == nil { - u.Verifications = new([]Verification) + u.Verifications = []Verification{} } token, err := utils.GenerateVerificationToken() if err != nil { @@ -295,10 +387,10 @@ func (u *User) SetVerification(verificationType string) (*Verification, error) { VerificationToken: token, Type: verificationType, } - if vi := slices.IndexFunc(*u.Verifications, func(vsl Verification) bool { return vsl.Type == v.Type }); vi > -1 { - (*u.Verifications)[vi] = v + if vi := slices.IndexFunc(u.Verifications, func(vsl Verification) bool { return vsl.Type == v.Type }); vi > -1 { + u.Verifications[vi] = v } else { - *u.Verifications = append(*u.Verifications, v) + u.Verifications = append(u.Verifications, v) } return &v, nil } @@ -307,11 +399,11 @@ func (u *User) GetVerification(verificationType string) (*Verification, error) { if u.Verifications == nil { return nil, errors.ErrNoData } - vi := slices.IndexFunc(*u.Verifications, func(vsl Verification) bool { return vsl.Type == verificationType }) + vi := slices.IndexFunc(u.Verifications, func(vsl Verification) bool { return vsl.Type == verificationType }) if vi == -1 { return nil, errors.ErrNotFound } - return &(*u.Verifications)[vi], nil + return &u.Verifications[vi], nil } func (u *User) Verify(token string, verificationType string) bool { @@ -320,7 +412,7 @@ func (u *User) Verify(token string, verificationType string) bool { return false } - vi := slices.IndexFunc(*u.Verifications, func(vsl Verification) bool { + vi := slices.IndexFunc(u.Verifications, func(vsl Verification) bool { return vsl.Type == verificationType && vsl.VerificationToken == token }) @@ -329,32 +421,22 @@ func (u *User) Verify(token string, verificationType string) bool { return false } - if (*u.Verifications)[vi].VerifiedAt != nil { - logger.Error.Printf("VerifiedAt is not nil, already verified?: %#v", (*u.Verifications)[vi]) + if u.Verifications[vi].VerifiedAt != nil { + logger.Error.Printf("VerifiedAt is not nil, already verified?: %#v", u.Verifications[vi]) return false } t := time.Now() - (*u.Verifications)[vi].VerifiedAt = &t + u.Verifications[vi].VerifiedAt = &t return true } func (u *User) Safe() map[string]interface{} { - result := map[string]interface{}{ - "email": u.Email, - "first_name": u.FirstName, - "last_name": u.LastName, - "phone": u.Phone, - "notes": u.Notes, - "address": u.Address, - "zip_code": u.ZipCode, - "city": u.City, - "status": u.Status, - "id": u.ID, - "role_id": u.RoleID, - "company": u.Company, - "dateofbirth": u.DateOfBirth, - "membership": map[string]interface{}{ + var membership map[string]interface{} = nil + var licence map[string]interface{} = nil + var bankAccount map[string]interface{} = nil + if u.Membership != nil { + membership = map[string]interface{}{ "id": u.Membership.ID, "start_date": u.Membership.StartDate, "end_date": u.Membership.EndDate, @@ -369,23 +451,11 @@ func (u *User) Safe() map[string]interface{} { "included_per_year": u.Membership.SubscriptionModel.IncludedPerYear, "included_per_month": u.Membership.SubscriptionModel.IncludedPerMonth, }, - }, - "licence": map[string]interface{}{ - "id": 0, - }, - "bank_account": map[string]interface{}{ - "id": u.BankAccount.ID, - "mandate_date_signed": u.BankAccount.MandateDateSigned, - "bank": u.BankAccount.Bank, - "account_holder_name": u.BankAccount.AccountHolderName, - "iban": u.BankAccount.IBAN, - "bic": u.BankAccount.BIC, - "mandate_reference": u.BankAccount.MandateReference, - }, + } } if u.Licence != nil { - result["licence"] = map[string]interface{}{ + licence = map[string]interface{}{ "id": u.Licence.ID, "number": u.Licence.Number, "status": u.Licence.Status, @@ -396,6 +466,36 @@ func (u *User) Safe() map[string]interface{} { } } + if u.BankAccount != nil { + bankAccount = map[string]interface{}{ + "id": u.BankAccount.ID, + "mandate_date_signed": u.BankAccount.MandateDateSigned, + "bank": u.BankAccount.Bank, + "account_holder_name": u.BankAccount.AccountHolderName, + "iban": u.BankAccount.IBAN, + "bic": u.BankAccount.BIC, + "mandate_reference": u.BankAccount.MandateReference, + } + } + result := map[string]interface{}{ + "email": u.Email, + "first_name": u.FirstName, + "last_name": u.LastName, + "phone": u.Phone, + "notes": u.Notes, + "address": u.Address, + "zip_code": u.ZipCode, + "city": u.City, + "status": u.Status, + "id": u.ID, + "role_id": u.RoleID, + "company": u.Company, + "dateofbirth": u.DateOfBirth, + "membership": membership, + "licence": licence, + "bank_account": bankAccount, + } + return result } @@ -439,14 +539,12 @@ func extractUserIDFrom(tokenString string) (uint, error) { } func GetUsersWhere(db *gorm.DB, where map[string]interface{}) (*[]User, error) { + logger.Error.Printf("where: %#v", where) var users []User result := db. Preload(clause.Associations). - Preload("Membership"). Preload("Membership.SubscriptionModel"). - Preload("Licence"). Preload("Licence.Categories"). - Preload("Verifications"). Where(where).Find(&users) if result.Error != nil { if result.Error == gorm.ErrRecordNotFound { diff --git a/go-backend/internal/models/verification.go b/go-backend/internal/models/verification.go index 9cece5e..3ed215d 100644 --- a/go-backend/internal/models/verification.go +++ b/go-backend/internal/models/verification.go @@ -1,15 +1,48 @@ package models import ( + "GoMembership/pkg/logger" "time" + + "gorm.io/gorm" ) type Verification struct { - UpdatedAt time.Time - CreatedAt time.Time + gorm.Model VerifiedAt *time.Time `json:"verified_at"` VerificationToken string `json:"token"` - ID uint `gorm:"primaryKey"` UserID uint `json:"user_id"` Type string } + +func (v *Verification) Create(db *gorm.DB) error { + if err := db.Create(v).Error; err != nil { + return err + } + logger.Info.Printf("verification created: %#v", v) + // Preload all associations to return the fully populated object + return db.First(v, v.ID).Error // Refresh the verification object with all associations +} + +func (v *Verification) Update(db *gorm.DB) error { + + return db.Transaction(func(tx *gorm.DB) error { + // Check if the user exists in the database + var existingVerification Verification + + logger.Info.Printf("updating verification: %#v", v) + if err := tx.First(&existingVerification, v.ID).Error; err != nil { + return err + } + + if err := tx.Model(&existingVerification).Updates(v).Error; err != nil { + return err + } + return tx.First(v, v.ID).Error + }) + +} + +func (v *Verification) Delete(db *gorm.DB) error { + return db.Delete(&v).Error +} diff --git a/go-backend/internal/repositories/subscription_model_repository.go b/go-backend/internal/repositories/subscription_model_repository.go index ea956db..39f9d1a 100644 --- a/go-backend/internal/repositories/subscription_model_repository.go +++ b/go-backend/internal/repositories/subscription_model_repository.go @@ -83,7 +83,7 @@ func GetUsersBySubscription(subscriptionID uint) (*[]models.User, error) { Preload("BankAccount"). Preload("Licence"). Preload("Licence.Categories"). - Joins("JOIN memberships ON users.membership_id = memberships.id"). + Joins("JOIN memberships ON users.id = memberships.user_id"). Joins("JOIN subscription_models ON memberships.subscription_model_id = subscription_models.id"). Where("subscription_models.id = ?", subscriptionID). Find(&users).Error diff --git a/go-backend/internal/services/user_service.go b/go-backend/internal/services/user_service.go index c488932..f57abc7 100644 --- a/go-backend/internal/services/user_service.go +++ b/go-backend/internal/services/user_service.go @@ -68,14 +68,11 @@ func (s *UserService) Update(user *models.User) (*models.User, error) { if err := existingUser.FromID(s.DB, &user.ID); err != nil { return nil, err } - user.MembershipID = existingUser.MembershipID user.Membership.ID = existingUser.Membership.ID if existingUser.Licence != nil { user.Licence.ID = existingUser.Licence.ID - user.LicenceID = existingUser.LicenceID } user.BankAccount.ID = existingUser.BankAccount.ID - user.BankAccountID = existingUser.BankAccountID user.SetPassword(user.Password) @@ -109,7 +106,6 @@ func (s *UserService) Register(user *models.User) (id uint, token string, err er user.Membership.SubscriptionModel = *selectedModel user.Membership.SubscriptionModelID = selectedModel.ID user.Status = constants.UnverifiedStatus - user.PaymentStatus = constants.AwaitingPaymentStatus user.BankAccount.MandateDateSigned = time.Now() v, err := user.SetVerification(constants.VerificationTypes.Email) if err != nil { diff --git a/go-backend/internal/validation/fields_test.go b/go-backend/internal/validation/fields_test.go index de14638..e89997d 100644 --- a/go-backend/internal/validation/fields_test.go +++ b/go-backend/internal/validation/fields_test.go @@ -10,7 +10,7 @@ type User struct { Age int Address *Address Tags []string - License License + Licence Licence } type Address struct { @@ -18,7 +18,7 @@ type Address struct { Country string } -type License struct { +type Licence struct { ID string Categories []string } @@ -98,22 +98,22 @@ func TestFilterAllowedStructFields(t *testing.T) { { name: "Filter slice of structs", input: &User{ - License: License{ + Licence: Licence{ ID: "123", Categories: []string{"A", "B"}, }, }, existing: &User{ - License: License{ + Licence: Licence{ ID: "456", Categories: []string{"C"}, }, }, allowedFields: map[string]bool{ - "License.ID": true, + "Licence.ID": true, }, expectedResult: &User{ - License: License{ + Licence: Licence{ ID: "123", // Allowed field Categories: []string{"C"}, // Kept from existing },