Compare commits

..

7 Commits

Author SHA1 Message Date
Alex
dbf7aca078 add: Tests for driverslicence 2024-09-29 21:18:02 +02:00
Alex
b99a5010a7 add default for userCreation 2024-09-29 21:17:25 +02:00
Alex
8a581da1d8 add driversLicence model 2024-09-29 21:16:58 +02:00
Alex
41738753f0 add subscriptions to currentUser JSON for frontend 2024-09-29 21:14:03 +02:00
Alex
33561692b6 membership status now int; add auto SEPA mandateref upon user creation 2024-09-29 21:12:49 +02:00
Alex
31cfe21695 clean up of bank-service 2024-09-29 21:08:59 +02:00
Alex
36bd75bbeb add where clause default to membershipservice 2024-09-29 21:07:22 +02:00
14 changed files with 376 additions and 117 deletions

View File

@@ -7,6 +7,25 @@ type roles struct {
Admin int8
}
type licences struct {
AM string
A1 string
A2 string
A string
B string
C1 string
C string
D1 string
D string
BE string
C1E string
CE string
D1E string
DE string
L string
T string
}
const (
UnverifiedStatus = iota + 1
VerifiedStatus
@@ -29,6 +48,25 @@ var Roles = roles{
Admin: 8,
}
var Licences = licences{
AM: "AM",
A1: "A1",
A2: "A2",
A: "A",
B: "B",
C1: "C1",
C: "C",
D1: "D1",
D: "D",
BE: "BE",
C1E: "C1E",
CE: "CE",
D1E: "D1E",
DE: "DE",
L: "L",
T: "T",
}
const PRIV_VIEW = 1
const PRIV_ADD = 2
const PRIV_EDIT = 4

View File

@@ -98,8 +98,9 @@ func TestSuite(t *testing.T) {
var subscriptionRepo repositories.SubscriptionModelsRepositoryInterface = &repositories.SubscriptionModelsRepository{}
membershipService := &services.MembershipService{Repo: membershipRepo, SubscriptionRepo: subscriptionRepo}
var licenceRepo repositories.DriversLicenceInterface = &repositories.DriversLicenceRepository{}
var userRepo repositories.UserRepositoryInterface = &repositories.UserRepository{}
userService := &services.UserService{Repo: userRepo}
userService := &services.UserService{Repo: userRepo, Licences: licenceRepo}
Uc = &UserController{Service: userService, EmailService: emailService, ConsentService: consentService, BankAccountService: bankAccountService, MembershipService: membershipService}
Mc = &MembershipController{Service: *membershipService}
@@ -133,9 +134,9 @@ func TestSuite(t *testing.T) {
log.Fatalf("Failed to stop SMTP Mockup Server: %#v", err)
}
if err := deleteTestDB("test.db"); err != nil {
log.Fatalf("Failed to tear down DB: %#v", err)
}
// if err := deleteTestDB("test.db"); err != nil {
// log.Fatalf("Failed to tear down DB: %#v", err)
// }
}
func initSubscriptionPlans() error {
@@ -221,6 +222,7 @@ func getBaseUser() models.User {
Phone: "01738484993",
BankAccount: models.BankAccount{IBAN: "DE89370400440532013000"},
Membership: models.Membership{SubscriptionModel: models.SubscriptionModel{Name: "Basic"}},
DriversLicence: models.DriversLicence{},
ProfilePicture: "",
Password: "password123",
Company: "",

View File

@@ -61,6 +61,18 @@ func (uc *UserController) UpdateHandler(c *gin.Context) {
c.JSON(http.StatusForbidden, gin.H{"error": "You are not authorized to update this user"})
return
}
if user.Membership.SubscriptionModel.Name == "" {
logger.Error.Printf("No subscription model provided: %v", user.Email)
c.JSON(http.StatusNotAcceptable, gin.H{"error": "No subscription model provided"})
return
}
selectedModel, err := uc.MembershipService.GetModelByName(&user.Membership.SubscriptionModel.Name)
if err != nil {
logger.Error.Printf("%v:No subscription model found: %#v", user.Email, err)
c.JSON(http.StatusNotFound, gin.H{"error": "Not a valid subscription model"})
return
}
user.Membership.SubscriptionModel = *selectedModel
// TODO: If it's not an admin, prevent changes to critical fields
// if userRole != constants.Roles.Admin {
// existingUser, err := uc.Service.GetUserByID(jwtUserID)
@@ -111,7 +123,17 @@ func (uc *UserController) CurrentUserHandler(c *gin.Context) {
return
}
c.JSON(http.StatusOK, user.Safe())
subscriptions, err := uc.MembershipService.GetSubscriptions(nil)
if err != nil {
logger.Error.Printf("Error retrieving subscriptions: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error retrieving subscriptions."})
return
}
c.JSON(http.StatusOK, gin.H{
"user": user.Safe(),
"subscriptions": subscriptions,
})
}
func (uc *UserController) LogoutHandler(c *gin.Context) {
@@ -187,7 +209,6 @@ func (uc *UserController) RegisterUser(c *gin.Context) {
c.JSON(http.StatusNotAcceptable, gin.H{"error": "No subscription model provided"})
return
}
logger.Error.Printf("user.membership: %#v", regData.User.Membership)
selectedModel, err := uc.MembershipService.GetModelByName(&regData.User.Membership.SubscriptionModel.Name)
if err != nil {
logger.Error.Printf("%v:No subscription model found: %#v", regData.User.Email, err)
@@ -195,7 +216,6 @@ func (uc *UserController) RegisterUser(c *gin.Context) {
return
}
regData.User.Membership.SubscriptionModel = *selectedModel
// logger.Info.Printf("REGISTERING user: %#v", regData.User)
regData.User.RoleID = constants.Roles.Member

View File

@@ -22,6 +22,7 @@ import (
"GoMembership/internal/constants"
"GoMembership/internal/middlewares"
"GoMembership/internal/models"
"GoMembership/internal/repositories"
"GoMembership/internal/utils"
"GoMembership/pkg/logger"
@@ -285,7 +286,7 @@ func testCurrentUserHandler(t *testing.T, loginEmail string, loginCookie http.Co
for _, tt := range tests {
logger.Error.Print("==============================================================")
logger.Error.Printf("Testing : %v", tt.name)
logger.Error.Printf("CurrentUser Testing : %v", tt.name)
logger.Error.Print("==============================================================")
t.Run(tt.name, func(t *testing.T) {
@@ -303,11 +304,14 @@ func testCurrentUserHandler(t *testing.T, loginEmail string, loginCookie http.Co
assert.Equal(t, tt.expectedStatus, w.Code)
if tt.expectedStatus == http.StatusOK {
var response models.User
var response struct {
User models.User `json:"user"`
Subscriptions []models.SubscriptionModel `json:"subscriptions"`
}
err := json.Unmarshal(w.Body.Bytes(), &response)
assert.NoError(t, err)
assert.Equal(t, tt.expectedUserMail, response.Email)
// logger.Error.Printf("response: %#v", response)
assert.Equal(t, tt.expectedUserMail, response.User.Email)
var newCookie *http.Cookie
for _, cookie := range w.Result().Cookies() {
if cookie.Name == "jwt" {
@@ -346,6 +350,18 @@ func validateUser(assert bool, wantDBData map[string]interface{}) error {
return fmt.Errorf("User entry query didn't met expectation: %v != %#v", assert, *users)
}
if assert {
user := (*users)[0]
// Check for mandate reference
if user.BankAccount.MandateReference == "" {
return fmt.Errorf("Mandate reference not generated for user: %s", user.Email)
}
// Validate mandate reference format
expected := user.GenerateMandateReference()
if !strings.HasPrefix(user.BankAccount.MandateReference, expected) {
return fmt.Errorf("Mandate reference is invalid. Expected: %s, Got: %s", expected, user.BankAccount.MandateReference)
}
//check for email delivery
messages := utils.SMTPGetMessages()
for _, message := range messages {
@@ -355,12 +371,12 @@ func validateUser(assert bool, wantDBData map[string]interface{}) error {
return err
}
if strings.Contains(mail.Subject, constants.MailRegistrationSubject) {
if err := checkRegistrationMail(mail, &(*users)[0]); err != nil {
if err := checkRegistrationMail(mail, &user); err != nil {
logger.Error.Printf("Error in checkRegistrationMail: %#v", err)
return err
}
} else if strings.Contains(mail.Subject, constants.MailVerificationSubject) {
if err := checkVerificationMail(mail, &(*users)[0]); err != nil {
if err := checkVerificationMail(mail, &user); err != nil {
logger.Error.Printf("Error in checkVerificationMail: %#v", err)
return err
}
@@ -413,16 +429,6 @@ func testUpdateUser(t *testing.T, loginEmail string, loginCookie http.Cookie) {
},
expectedStatus: http.StatusAccepted,
},
{
name: "Password Update",
setupCookie: func(req *http.Request) {
req.AddCookie(&loginCookie)
},
updateFunc: func(u *models.User) {
u.Password = "NewPassword"
},
expectedStatus: http.StatusAccepted,
},
{
name: "Valid Update, invalid cookie",
setupCookie: func(req *http.Request) {
@@ -444,11 +450,80 @@ func testUpdateUser(t *testing.T, loginEmail string, loginCookie http.Cookie) {
},
updateFunc: func(u *models.User) {
u.Password = ""
u.FirstName = "John Updated"
u.LastName = "Doe Updated"
u.Phone = "01738484994"
u.Email = "invalid-email"
},
expectedStatus: http.StatusBadRequest,
expectedError: "Invalid user data",
},
{
name: "Change LicenceNumber",
setupCookie: func(req *http.Request) {
req.AddCookie(&loginCookie)
},
updateFunc: func(u *models.User) {
u.Password = ""
u.FirstName = "John Updated"
u.LastName = "Doe Updated"
u.Phone = "01738484994"
u.DriversLicence.LicenceNumber = "NEWNUMBER"
},
expectedStatus: http.StatusAccepted,
},
{
name: "Add category",
setupCookie: func(req *http.Request) {
req.AddCookie(&loginCookie)
},
updateFunc: func(u *models.User) {
u.Password = ""
u.FirstName = "John Updated"
u.LastName = "Doe Updated"
u.Phone = "01738484994"
u.DriversLicence.LicenceNumber = "NEWNUMBER"
var licenceRepo repositories.DriversLicenceInterface = &repositories.DriversLicenceRepository{}
category, err := licenceRepo.FindCategoryByName("B")
assert.NoError(t, err)
u.DriversLicence.LicenceCategories = []models.LicenceCategory{category}
},
expectedStatus: http.StatusAccepted,
},
{
name: "Add 2 categories",
setupCookie: func(req *http.Request) {
req.AddCookie(&loginCookie)
},
updateFunc: func(u *models.User) {
u.Password = ""
u.FirstName = "John Updated"
u.LastName = "Doe Updated"
u.Phone = "01738484994"
u.DriversLicence.LicenceNumber = "NEWNUMBER"
var licenceRepo repositories.DriversLicenceInterface = &repositories.DriversLicenceRepository{}
category, err := licenceRepo.FindCategoryByName("B")
category2, err := licenceRepo.FindCategoryByName("BE")
assert.NoError(t, err)
u.DriversLicence.LicenceCategories = []models.LicenceCategory{category, category2}
},
expectedStatus: http.StatusAccepted,
},
{
name: "Delete all categories",
setupCookie: func(req *http.Request) {
req.AddCookie(&loginCookie)
},
updateFunc: func(u *models.User) {
u.Password = ""
u.FirstName = "John Updated"
u.LastName = "Doe Updated"
u.Phone = "01738484994"
u.DriversLicence.LicenceNumber = "NEWNUMBER"
u.DriversLicence.LicenceCategories = []models.LicenceCategory{}
},
expectedStatus: http.StatusAccepted,
},
{
name: "User ID mismatch while not admin",
setupCookie: func(req *http.Request) {
@@ -457,11 +532,28 @@ func testUpdateUser(t *testing.T, loginEmail string, loginCookie http.Cookie) {
updateFunc: func(u *models.User) {
u.Password = ""
u.ID = 1
u.LastName = "Doe Updated"
u.Phone = "01738484994"
u.DriversLicence.LicenceNumber = "NEWNUMBER"
u.FirstName = "John Missing ID"
},
expectedStatus: http.StatusForbidden,
expectedError: "You are not authorized to update this user",
},
{
name: "Password Update",
setupCookie: func(req *http.Request) {
req.AddCookie(&loginCookie)
},
updateFunc: func(u *models.User) {
u.Password = ""
u.LastName = "Doe Updated"
u.Phone = "01738484994"
u.DriversLicence.LicenceNumber = "NEWNUMBER"
u.Password = "NewPassword"
},
expectedStatus: http.StatusAccepted,
},
// {
// name: "Non-existent User",
// setupCookie: func(req *http.Request) {
@@ -476,7 +568,6 @@ func testUpdateUser(t *testing.T, loginEmail string, loginCookie http.Cookie) {
// expectedError: "User not found",
// },
}
for _, tt := range tests {
logger.Error.Print("==============================================================")
logger.Error.Printf("Update Testing : %v", tt.name)
@@ -485,13 +576,13 @@ func testUpdateUser(t *testing.T, loginEmail string, loginCookie http.Cookie) {
// Create a copy of the user and apply the updates
updatedUser := user
tt.updateFunc(&updatedUser)
// Convert user to JSON
jsonData, err := json.Marshal(updatedUser)
if err != nil {
t.Fatalf("Failed to marshal user data: %v", err)
}
logger.Error.Printf("Updated User: %#v", updatedUser)
// Create request
req, _ := http.NewRequest("PUT", "/users/"+strconv.FormatUint(uint64(user.ID), 10), bytes.NewBuffer(jsonData))
req.Header.Set("Content-Type", "application/json")
@@ -523,20 +614,59 @@ func testUpdateUser(t *testing.T, loginEmail string, loginCookie http.Cookie) {
// Verify the update in the database
updatedUserFromDB, err := Uc.Service.GetUserByID(user.ID)
updatedUserFromDB.UpdatedAt = updatedUser.UpdatedAt
updatedUserFromDB.Membership.UpdatedAt = updatedUser.Membership.UpdatedAt
updatedUserFromDB.BankAccount.UpdatedAt = updatedUser.BankAccount.UpdatedAt
updatedUserFromDB.Verification.UpdatedAt = updatedUser.Verification.UpdatedAt
updatedUserFromDB.Membership.SubscriptionModel.UpdatedAt = updatedUser.Membership.SubscriptionModel.UpdatedAt
assert.NoError(t, err)
if updatedUser.Password == "" {
assert.Equal(t, user.Password, (*updatedUserFromDB).Password)
} else {
assert.NotEqual(t, user.Password, (*updatedUserFromDB).Password)
updatedUser.Password = ""
}
updatedUserFromDB.Password = ""
assert.NoError(t, err)
assert.Equal(t, updatedUser, *updatedUserFromDB, "Updated user in DB does not match expected user")
updatedUser.Password = ""
assert.Equal(t, updatedUser.FirstName, updatedUserFromDB.FirstName, "FirstName mismatch")
assert.Equal(t, updatedUser.LastName, updatedUserFromDB.LastName, "LastName mismatch")
assert.Equal(t, updatedUser.Email, updatedUserFromDB.Email, "Email mismatch")
assert.Equal(t, updatedUser.DateOfBirth, updatedUserFromDB.DateOfBirth, "DateOfBirth mismatch")
assert.Equal(t, updatedUser.Company, updatedUserFromDB.Company, "Company mismatch")
assert.Equal(t, updatedUser.Phone, updatedUserFromDB.Phone, "Phone mismatch")
assert.Equal(t, updatedUser.Notes, updatedUserFromDB.Notes, "Notes mismatch")
assert.Equal(t, updatedUser.ProfilePicture, updatedUserFromDB.ProfilePicture, "ProfilePicture mismatch")
assert.Equal(t, updatedUser.Address, updatedUserFromDB.Address, "Address mismatch")
assert.Equal(t, updatedUser.ZipCode, updatedUserFromDB.ZipCode, "ZipCode mismatch")
assert.Equal(t, updatedUser.City, updatedUserFromDB.City, "City mismatch")
assert.Equal(t, updatedUser.PaymentStatus, updatedUserFromDB.PaymentStatus, "PaymentStatus mismatch")
assert.Equal(t, updatedUser.Status, updatedUserFromDB.Status, "Status mismatch")
assert.Equal(t, updatedUser.RoleID, updatedUserFromDB.RoleID, "RoleID mismatch")
// For nested structs, you might want to compare individual fields
assert.Equal(t, updatedUser.BankAccount.Bank, updatedUserFromDB.BankAccount.Bank, "BankAccount.Bank mismatch")
assert.Equal(t, updatedUser.BankAccount.AccountHolderName, updatedUserFromDB.BankAccount.AccountHolderName, "BankAccount.AccountHolderName mismatch")
assert.Equal(t, updatedUser.BankAccount.IBAN, updatedUserFromDB.BankAccount.IBAN, "BankAccount.IBAN mismatch")
assert.Equal(t, updatedUser.BankAccount.BIC, updatedUserFromDB.BankAccount.BIC, "BankAccount.BIC mismatch")
assert.Equal(t, updatedUser.BankAccount.MandateReference, updatedUserFromDB.BankAccount.MandateReference, "BankAccount.MandateReference mismatch")
assert.Equal(t, updatedUser.Membership.StartDate, updatedUserFromDB.Membership.StartDate, "Membership.StartDate mismatch")
assert.Equal(t, updatedUser.Membership.EndDate, updatedUserFromDB.Membership.EndDate, "Membership.EndDate mismatch")
assert.Equal(t, updatedUser.Membership.Status, updatedUserFromDB.Membership.Status, "Membership.Status mismatch")
assert.Equal(t, updatedUser.Membership.SubscriptionModelID, updatedUserFromDB.Membership.SubscriptionModelID, "Membership.SubscriptionModelID mismatch")
assert.Equal(t, updatedUser.Membership.ParentMembershipID, updatedUserFromDB.Membership.ParentMembershipID, "Membership.ParentMembershipID mismatch")
assert.Equal(t, updatedUser.DriversLicence.Status, updatedUserFromDB.DriversLicence.Status, "DriversLicence.Status mismatch")
assert.Equal(t, updatedUser.DriversLicence.LicenceNumber, updatedUserFromDB.DriversLicence.LicenceNumber, "DriversLicence.LicenceNumber mismatch")
assert.Equal(t, updatedUser.DriversLicence.IssuedDate, updatedUserFromDB.DriversLicence.IssuedDate, "DriversLicence.IssuedDate mismatch")
assert.Equal(t, updatedUser.DriversLicence.ExpirationDate, updatedUserFromDB.DriversLicence.ExpirationDate, "DriversLicence.ExpirationDate mismatch")
assert.Equal(t, updatedUser.DriversLicence.IssuingCountry, updatedUserFromDB.DriversLicence.IssuingCountry, "DriversLicence.IssuingCountry mismatch")
// For slices or more complex nested structures, you might want to use deep equality checks
assert.ElementsMatch(t, updatedUser.Consents, updatedUserFromDB.Consents, "Consents mismatch")
if len(updatedUser.DriversLicence.LicenceCategories) > 0 {
for i := range updatedUser.DriversLicence.LicenceCategories {
assert.Equal(t, updatedUser.DriversLicence.LicenceCategories[i].Category, updatedUserFromDB.DriversLicence.LicenceCategories[i].Category, "LicenceCategory Category mismatch at index %d", i)
}
} else {
assert.Emptyf(t, updatedUserFromDB.DriversLicence.LicenceCategories, "Categories aren't empty when they should")
}
}
})
}
@@ -822,7 +952,7 @@ func getTestUsers() []RegisterUserTest {
{
Name: "Correct Entry should pass",
WantResponse: http.StatusCreated,
WantDBData: map[string]interface{}{"Email": "john.doe@example.com"},
WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
Assert: true,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User { return user })),
},
@@ -839,7 +969,7 @@ func getTestUsers() []RegisterUserTest {
{
Name: "Company present should pass",
WantResponse: http.StatusCreated,
WantDBData: map[string]interface{}{"Email": "john.doe2@example.com"},
WantDBData: map[string]interface{}{"email": "john.doe2@example.com"},
Assert: true,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
user.Email = "john.doe2@example.com"
@@ -850,7 +980,7 @@ func getTestUsers() []RegisterUserTest {
{
Name: "Subscription constraints not entered; should fail",
WantResponse: http.StatusNotAcceptable,
WantDBData: map[string]interface{}{"Email": "john.junior.doe@example.com"},
WantDBData: map[string]interface{}{"email": "john.junior.doe@example.com"},
Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
user.Email = "john.junior.doe@example.com"
@@ -861,7 +991,7 @@ func getTestUsers() []RegisterUserTest {
{
Name: "Subscription constraints wrong; should fail",
WantResponse: http.StatusNotAcceptable,
WantDBData: map[string]interface{}{"Email": "john.junior.doe@example.com"},
WantDBData: map[string]interface{}{"email": "john.junior.doe@example.com"},
Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
user.Email = "john.junior.doe@example.com"
@@ -873,7 +1003,7 @@ func getTestUsers() []RegisterUserTest {
{
Name: "Subscription constraints correct, should pass",
WantResponse: http.StatusCreated,
WantDBData: map[string]interface{}{"Email": "john.junior.doe@example.com"},
WantDBData: map[string]interface{}{"email": "john.junior.doe@example.com"},
Assert: true,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
user.Email = "john.junior.doe@example.com"
@@ -882,5 +1012,16 @@ func getTestUsers() []RegisterUserTest {
return user
})),
},
{
Name: "Correct Entry with Mandate Reference",
WantResponse: http.StatusCreated,
WantDBData: map[string]interface{}{"email": "john.mandate@example.com"},
Assert: true,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
user.Email = "john.mandate@example.com"
user.BankAccount.IBAN = "DE89370400440532013000"
return user
})),
},
}
}

View File

@@ -27,6 +27,8 @@ func Open(dbPath string, adminMail string) error {
&models.Membership{},
&models.Consent{},
&models.Verification{},
&models.DriversLicence{},
&models.LicenceCategory{},
&models.BankAccount{}); err != nil {
logger.Error.Fatalf("Couldn't create database: %v", err)
return err
@@ -51,6 +53,13 @@ func Open(dbPath string, adminMail string) error {
return err
}
categories := createLicenceCategories()
for _, model := range categories {
result := db.Create(&model)
if result.Error != nil {
return result.Error
}
}
admin, err := createAdmin(adminMail, createdModel.ID)
if err != nil {
return err
@@ -73,6 +82,28 @@ func createSubscriptionModels() []models.SubscriptionModel {
},
}
}
func createLicenceCategories() []models.LicenceCategory {
return []models.LicenceCategory{
{Category: "AM"},
{Category: "A1"},
{Category: "A2"},
{Category: "A"},
{Category: "B"},
{Category: "C1"},
{Category: "C"},
{Category: "D1"},
{Category: "D"},
{Category: "BE"},
{Category: "C1E"},
{Category: "CE"},
{Category: "D1E"},
{Category: "DE"},
{Category: "R"},
{Category: "L"},
}
}
// TODO: Landing page to create an admin
func createAdmin(userMail string, subscriptionModelID uint) (*models.User, error) {

View File

@@ -0,0 +1,27 @@
package models
import (
"time"
"gorm.io/gorm"
)
type DriversLicence struct {
gorm.Model
Status int8 `json:"licence_status" validate:"omitempty,number"`
LicenceNumber string `json:"licence_number" validate:"safe_content"`
IssuedDate time.Time `json:"licence_issued_date" validate:"omitempty,lte"`
ExpirationDate time.Time `json:"licence_expiration_date" validate:"omitempty,gt"`
IssuingCountry string `json:"licence_country" validate:"safe_content"`
LicenceCategories []LicenceCategory `json:"licence_categories" gorm:"many2many:licence_2_categories"`
}
type LicenceCategory struct {
gorm.Model
Category string `json:"licence_category" validate:"safe_content"`
}
// func (d *DriversLicence) BeforeCreate(tx *gorm.DB) (err error) {
// d.Status = constants.UnverifiedStatus
// return
// }

View File

@@ -7,7 +7,7 @@ type Membership struct {
UpdatedAt time.Time
StartDate time.Time `json:"start_date"`
EndDate time.Time `json:"end_date"`
Status string `json:"status" validate:"safe_content"`
Status int8 `json:"status" validate:"safe_content"`
SubscriptionModel SubscriptionModel `gorm:"foreignKey:SubscriptionModelID" json:"subscription_model"`
SubscriptionModelID uint `json:"subsription_model_id"`
ParentMembershipID uint `json:"parent_member_id" validate:"omitempty,omitnil,number"`

View File

@@ -1,7 +1,7 @@
package models
import (
"GoMembership/internal/constants"
"fmt"
"time"
"github.com/alexedwards/argon2id"
@@ -9,10 +9,11 @@ import (
)
type User struct {
gorm.Model
DateOfBirth time.Time `gorm:"not null" json:"date_of_birth" validate:"required,age"`
Company string `json:"company" validate:"omitempty,omitnil,safe_content"`
Phone string `json:"phone" validate:"omitempty,omitnil,safe_content"`
Notes *string `json:"notes,safe_content"`
Notes string `json:"notes" validate:"safe_content"`
FirstName string `gorm:"not null" json:"first_name" validate:"required,safe_content"`
Password string `json:"password" validate:"required_unless=RoleID 0,safe_content"`
Email string `gorm:"unique;not null" json:"email" validate:"required,email,safe_content"`
@@ -23,22 +24,30 @@ type User struct {
City string `form:"not null" json:"city" validate:"required,alphaunicode,safe_content"`
Consents []Consent `gorm:"constraint:OnUpdate:CASCADE"`
BankAccount BankAccount `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"bank_account"`
BankAccountID uint
Verification Verification `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
VerificationID uint
Membership Membership `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"membership"`
ID int64 `gorm:"primaryKey" json:"id"`
MembershipID uint
DriversLicence DriversLicence `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"drivers_licence"`
DriversLicenceID uint
ID uint `json:"id"`
PaymentStatus int8 `json:"payment_status"`
Status int8 `json:"status"`
RoleID int8 `json:"role_id"`
gorm.Model
}
// BeforeCreate sets the default value for Status
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
if u.Status == 0 { // Assuming 0 is an unset value
u.Status = constants.UnverifiedStatus
u.PaymentStatus = constants.AwaitingPaymentStatus
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
}
return
return nil
}
func (u *User) GenerateMandateReference() string {
return fmt.Sprintf("%s%d%s", time.Now().Format("20060102"), u.ID, u.BankAccount.IBAN)
}
func (u *User) PasswordMatches(plaintextPassword string) (bool, error) {

View File

@@ -0,0 +1,25 @@
package repositories
import (
"GoMembership/internal/database"
"GoMembership/internal/models"
)
type DriversLicenceInterface interface {
FindCategoryByName(categoryName string) (models.LicenceCategory, error)
FindCategoriesByIDs(ids []uint) ([]models.LicenceCategory, error)
}
type DriversLicenceRepository struct{}
func (r *DriversLicenceRepository) FindCategoriesByIDs(ids []uint) ([]models.LicenceCategory, error) {
var categories []models.LicenceCategory
err := database.DB.Where("id IN ?", ids).Find(&categories).Error
return categories, err
}
func (r *DriversLicenceRepository) FindCategoryByName(categoryName string) (models.LicenceCategory, error) {
var category models.LicenceCategory
err := database.DB.Where("category = ?", categoryName).First(&category).Error
return category, err
}

View File

@@ -68,12 +68,10 @@ func (ur *UserRepository) UpdateUser(user *models.User) (*models.User, error) {
func (ur *UserRepository) GetUsers(where map[string]interface{}) (*[]models.User, error) {
var users []models.User
result := database.DB.
Preload("Consents").
Preload("BankAccount").
Preload("Verification").
Preload("Membership", func(db *gorm.DB) *gorm.DB {
return db.Preload("SubscriptionModel")
}).Where(where).Find(&users)
Preload(clause.Associations).
Preload("Membership.SubscriptionModel").
Preload("DriversLicence.LicenceCategories").
Where(where).Find(&users)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
return nil, gorm.ErrRecordNotFound
@@ -86,12 +84,10 @@ func (ur *UserRepository) GetUsers(where map[string]interface{}) (*[]models.User
func (ur *UserRepository) GetUserByID(userID *uint) (*models.User, error) {
var user models.User
result := database.DB.
Preload("Consents").
Preload("BankAccount").
Preload("Verification").
Preload("Membership", func(db *gorm.DB) *gorm.DB {
return db.Preload("SubscriptionModel")
}).First(&user, userID)
Preload(clause.Associations).
Preload("Membership.SubscriptionModel").
Preload("DriversLicence.LicenceCategories").
First(&user, userID)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
return nil, gorm.ErrRecordNotFound

View File

@@ -39,7 +39,8 @@ func Run() {
membershipService := &services.MembershipService{Repo: membershipRepo, SubscriptionRepo: subscriptionRepo}
var userRepo repositories.UserRepositoryInterface = &repositories.UserRepository{}
userService := &services.UserService{Repo: userRepo}
var licenceRepo repositories.DriversLicenceInterface = &repositories.DriversLicenceRepository{}
userService := &services.UserService{Repo: userRepo, Licences: licenceRepo}
userController := &controllers.UserController{Service: userService, EmailService: emailService, ConsentService: consentService, BankAccountService: bankAccountService, MembershipService: membershipService}
membershipController := &controllers.MembershipController{Service: *membershipService}

View File

@@ -1,49 +1,12 @@
package services
import (
"GoMembership/internal/models"
"GoMembership/internal/repositories"
"crypto/rand"
"encoding/hex"
"fmt"
"strconv"
"time"
)
type BankAccountServiceInterface interface {
RegisterBankAccount(bankAccount *models.BankAccount) (int64, error)
}
type BankAccountService struct {
Repo repositories.BankAccountRepositoryInterface
}
func (service *BankAccountService) RegisterBankAccount(bankAccount *models.BankAccount) (int64, error) {
bankAccount.MandateDateSigned = time.Now()
ref, err := generateSEPAMandateReference(strconv.FormatInt(bankAccount.UserID, 10))
if err != nil {
return -1, err
}
bankAccount.MandateReference = ref
return service.Repo.CreateBankAccount(bankAccount)
}
func generateUniqueID(length int) (string, error) {
bytes := make([]byte, length)
_, err := rand.Read(bytes)
if err != nil {
return "", err
}
return hex.EncodeToString(bytes), nil
}
func generateSEPAMandateReference(userID string) (string, error) {
today := time.Now().Format("20060102") // Format YYYYMMDD
uniqueID, err := generateUniqueID(4) // 4 Bytes = 8 Hex-Zeichen
if err != nil {
return "", err
}
mandateReference := fmt.Sprintf("%s-%s-%s", userID, today, uniqueID)
return mandateReference, nil
}

View File

@@ -60,6 +60,9 @@ func (service *MembershipService) GetModelByName(modelname *string) (*models.Sub
}
func (service *MembershipService) GetSubscriptions(where map[string]interface{}) (*[]models.SubscriptionModel, error) {
if where == nil {
where = map[string]interface{}{}
}
return service.SubscriptionRepo.GetSubscriptions(where)
}

View File

@@ -29,6 +29,7 @@ type UserServiceInterface interface {
type UserService struct {
Repo repositories.UserRepositoryInterface
Licences repositories.DriversLicenceInterface
}
func (service *UserService) UpdateUser(user *models.User, userRole int8) (*models.User, error) {
@@ -69,6 +70,8 @@ func (service *UserService) RegisterUser(user *models.User) (uint, string, error
user.Status = constants.UnverifiedStatus
user.CreatedAt = time.Now()
user.UpdatedAt = time.Now()
user.PaymentStatus = constants.AwaitingPaymentStatus
user.BankAccount.MandateDateSigned = time.Now()
id, err := service.Repo.CreateUser(user)
if err != nil && strings.Contains(err.Error(), "UNIQUE constraint failed") {