diff --git a/internal/entity/auth.go b/internal/entity/auth.go index 6448110..fbeaf3a 100644 --- a/internal/entity/auth.go +++ b/internal/entity/auth.go @@ -118,7 +118,6 @@ func (b *UserList) ToUserList() []*User { } func (u *UserDB) ToUpdatedUser(req User) error { - if req.Name != "" { u.Name = req.Name } @@ -127,8 +126,12 @@ func (u *UserDB) ToUpdatedUser(req User) error { u.Email = req.Email } - if *req.PartnerID > 0 { - u.PartnerID = req.PartnerID + if req.PhoneNumber != "" { + u.PhoneNumber = req.PhoneNumber + } + + if req.NIK != "" { + u.NIK = req.NIK } if req.RoleID > 0 { diff --git a/internal/entity/partner.go b/internal/entity/partner.go index d7c6c1f..6153f7f 100644 --- a/internal/entity/partner.go +++ b/internal/entity/partner.go @@ -2,13 +2,13 @@ package entity import ( "furtuna-be/internal/constants/role" + "furtuna-be/internal/constants/userstatus" "time" ) type CreatePartnerRequest struct { Name string `json:"name" validate:"required"` Address string `json:"address"` - Username string `json:"username" validate:"required"` FullName string `json:"full_name"` Email string `json:"email"` Password string `json:"password" validate:"required"` @@ -16,6 +16,7 @@ type CreatePartnerRequest struct { PhoneNumber string `json:"phone_number"` BankName string `json:"bank_name"` BankAccountNumber string `json:"bank_account_number"` + Status string `json:"status"` BankAccountHolderName string `json:"bank_account_holder_name"` } @@ -40,6 +41,35 @@ type Partner struct { AdminEmail string `gorm:"-"` } +type PartnerUpdate struct { + ID int64 + Email string + Name string + Status string + Address string + PhoneNumber string + BankName string + BankAccountHolderNumber string + BankAccountHolderName string + NIK string + AdminUserID int64 + AdminName string + Password string +} + +func (c *PartnerUpdate) ToUserAdmin(partnerID *int64) *User { + return &User{ + ID: c.AdminUserID, + Name: c.Name, + Password: c.Password, + Email: c.Email, + NIK: c.NIK, + PhoneNumber: c.PhoneNumber, + Status: userstatus.UserStatus(c.Status), + PartnerID: partnerID, + } +} + func (Partner) TableName() string { return "partners" } @@ -77,17 +107,20 @@ func (PartnerDB) TableName() string { func (e *PartnerDB) ToPartner() *Partner { return &Partner{ - ID: e.ID, - Name: e.Name, - Status: e.Status, - Address: e.Address, - CreatedAt: e.CreatedAt, - UpdatedAt: e.UpdatedAt, - CreatedBy: e.CreatedBy, - Balance: e.Balance, - AdminEmail: e.AdminEmail, - AdminPhoneNumber: e.AdminPhoneNumber, - AdminName: e.AdminName, + ID: e.ID, + Name: e.Name, + Status: e.Status, + Address: e.Address, + CreatedAt: e.CreatedAt, + UpdatedAt: e.UpdatedAt, + CreatedBy: e.CreatedBy, + Balance: e.Balance, + AdminEmail: e.AdminEmail, + AdminPhoneNumber: e.AdminPhoneNumber, + AdminName: e.AdminName, + BankAccountHolderName: e.BankAccountHolderName, + BankName: e.BankName, + BankAccountNumber: e.BankAccountNumber, } } @@ -113,6 +146,50 @@ func (o *PartnerDB) ToUpdatedPartner(updatedBy int64, req Partner) { if req.Address != "" { o.Address = req.Address } + + if req.BankAccountNumber != "" { + o.BankAccountNumber = req.BankAccountNumber + } + + if req.BankAccountHolderName != "" { + o.BankAccountHolderName = req.BankAccountHolderName + } + + if req.Status != "" { + o.Status = req.Status + } +} + +func (o *PartnerDB) ToUpdatedPartnerData(updatedBy int64, req PartnerUpdate) { + o.UpdatedBy = updatedBy + + if req.Name != "" { + o.Name = req.Name + } + + if req.Status != "" { + o.Status = req.Status + } + + if req.Address != "" { + o.Address = req.Address + } + + if req.BankName != "" { + o.BankName = req.BankName + } + + if req.BankAccountHolderNumber != "" { + o.BankAccountNumber = req.BankAccountHolderNumber + } + + if req.BankAccountHolderName != "" { + o.BankAccountHolderName = req.BankAccountHolderName + } + + if req.Status != "" { + o.Status = req.Status + } } func (o *PartnerDB) SetDeleted(updatedBy int64) { @@ -128,7 +205,7 @@ func (c *CreatePartnerRequest) ToUserAdmin(partnerID int64) *User { Email: c.Email, NIK: c.NIK, PhoneNumber: c.PhoneNumber, - Status: "active", + Status: "Active", RoleID: role.PartnerAdmin, PartnerID: &partnerID, } @@ -141,7 +218,7 @@ func (e *CreatePartnerRequest) ToPartnerDB(createdBy int64) *PartnerDB { return &PartnerDB{ Partner: Partner{ Name: e.Name, - Status: "inactive", + Status: e.Status, Address: e.Address, CreatedBy: createdBy, CreatedAt: time.Now(), diff --git a/internal/handlers/http/partner/partner.go b/internal/handlers/http/partner/partner.go index af1ab30..1afc6ee 100644 --- a/internal/handlers/http/partner/partner.go +++ b/internal/handlers/http/partner/partner.go @@ -107,13 +107,7 @@ func (h *Handler) Update(c *gin.Context) { return } - validate := validator.New() - if err := validate.Struct(req); err != nil { - response.ErrorWrapper(c, err) - return - } - - updatedPartner, err := h.service.Update(ctx, PartnerID, req.ToEntity()) + updatedPartner, err := h.service.Update(ctx, req.ToEntityUpdate(PartnerID)) if err != nil { response.ErrorWrapper(c, err) return @@ -242,15 +236,18 @@ func (h *Handler) GetByID(c *gin.Context) { func (h *Handler) toPartnerResponse(resp *entity.Partner) response.Partner { return response.Partner{ - ID: &resp.ID, - Name: resp.Name, - Status: resp.Status, - CreatedAt: resp.CreatedAt.Format(time.RFC3339), - UpdatedAt: resp.CreatedAt.Format(time.RFC3339), - Balance: resp.Balance, - AdminName: resp.AdminName, - AdminPhoneNumber: resp.AdminPhoneNumber, - AdminEmail: resp.AdminEmail, + ID: &resp.ID, + Name: resp.Name, + Status: resp.Status, + CreatedAt: resp.CreatedAt.Format(time.RFC3339), + UpdatedAt: resp.CreatedAt.Format(time.RFC3339), + Balance: resp.Balance, + AdminName: resp.AdminName, + AdminPhoneNumber: resp.AdminPhoneNumber, + AdminEmail: resp.AdminEmail, + BankAccountName: resp.BankName, + BankAccountHolderName: resp.BankAccountHolderName, + BankAccountHolderNumber: resp.BankAccountNumber, } } diff --git a/internal/handlers/request/partner.go b/internal/handlers/request/partner.go index 7c32215..fb70c3d 100644 --- a/internal/handlers/request/partner.go +++ b/internal/handlers/request/partner.go @@ -21,32 +21,39 @@ func (p *PartnerParam) ToEntity() entity.PartnerSearch { } type Partner struct { - Name string `json:"name"` - Address string `json:"address"` - Status string `json:"status"` - Email string `json:"email"` - PhoneNumber string `json:"phone_number"` + ID int64 `json:"id"` + Name string `json:"name"` + Address string `json:"address"` + Status string `json:"status"` + Email string `json:"email"` + PhoneNumber string `json:"phone_number"` + Password string `json:"password"` + AdminUserID int64 `json:"admin_user_id"` + AdminName string `json:"admin_name"` + BankName string `json:"bank_name"` + BankAccountHolderNumber string `json:"bank_account_holder_number"` + BankAccountHolderName string `json:"bank_account_holder_name"` + NIK string `json:"nik"` } type CreatePartnerRequest struct { Name string `json:"name" validate:"required"` - Address string `json:"address"` - Username string `json:"username" validate:"required"` - FullName string `json:"full_name"` - Email string `json:"email"` + Email string `json:"email" validate:"required"` + Address string `json:"address" validate:"required"` + FullName string `json:"full_name" validate:"required"` Password string `json:"password" validate:"required"` NIK string `json:"nik"` - PhoneNumber string `json:"phone_number"` - BankName string `json:"bank_name"` - BankAccountNumber string `json:"bank_account_number"` - BankAccountHolderName string `json:"bank_account_holder_name"` + PhoneNumber string `json:"phone_number" validate:"required"` + BankName string `json:"bank_name" validate:"required"` + BankAccountNumber string `json:"bank_account_number" validate:"required"` + BankAccountHolderName string `json:"bank_account_holder_name" validate:"required"` + Status string `json:"status"` } func (e *CreatePartnerRequest) ToEntity() *entity.CreatePartnerRequest { return &entity.CreatePartnerRequest{ Name: e.Name, Address: e.Address, - Username: e.Username, FullName: e.FullName, Email: e.Email, Password: e.Password, @@ -55,6 +62,7 @@ func (e *CreatePartnerRequest) ToEntity() *entity.CreatePartnerRequest { BankName: e.BankName, BankAccountNumber: e.BankAccountNumber, BankAccountHolderName: e.BankAccountHolderName, + Status: e.Status, } } @@ -65,3 +73,21 @@ func (e *Partner) ToEntity() *entity.Partner { Status: e.Status, } } + +func (e *Partner) ToEntityUpdate(partnerID int64) *entity.PartnerUpdate { + return &entity.PartnerUpdate{ + ID: partnerID, + Name: e.Name, + Email: e.Email, + Address: e.Address, + Status: e.Status, + PhoneNumber: e.PhoneNumber, + BankName: e.BankName, + BankAccountHolderNumber: e.BankAccountHolderNumber, + BankAccountHolderName: e.BankAccountHolderName, + NIK: e.NIK, + AdminName: e.AdminName, + Password: e.Password, + AdminUserID: e.AdminUserID, + } +} diff --git a/internal/handlers/response/partner.go b/internal/handlers/response/partner.go index 592f244..e1249f7 100644 --- a/internal/handlers/response/partner.go +++ b/internal/handlers/response/partner.go @@ -1,16 +1,19 @@ package response type Partner struct { - ID *int64 `json:"id"` - Name string `json:"name"` - Status string `json:"status"` - Address string `json:"address,omitempty"` - CreatedAt string `json:"created_at,omitempty"` - UpdatedAt string `json:"updated_at,omitempty"` - AdminName string `json:"admin_name"` - AdminPhoneNumber string `json:"admin_phone_number"` - AdminEmail string `json:"admin_email"` - Balance float64 `json:"balance"` + ID *int64 `json:"id"` + Name string `json:"name"` + Status string `json:"status"` + Address string `json:"address,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + AdminName string `json:"admin_name"` + AdminPhoneNumber string `json:"admin_phone_number"` + AdminEmail string `json:"admin_email"` + Balance float64 `json:"balance"` + BankAccountName string `json:"bank_account_name"` + BankAccountHolderName string `json:"bank_account_holder_name"` + BankAccountHolderNumber string `json:"bank_account_holder_number"` } type PartnerList struct { diff --git a/internal/repository/partners/partners.go b/internal/repository/partners/partners.go index d8ba183..b0dbfa2 100644 --- a/internal/repository/partners/partners.go +++ b/internal/repository/partners/partners.go @@ -54,12 +54,25 @@ func (b *PartnerRepository) UpdateWithTx(ctx context.Context, tx *gorm.DB, Partn } func (b *PartnerRepository) GetByID(ctx context.Context, id int64) (*entity.PartnerDB, error) { - Partner := new(entity.PartnerDB) - if err := b.db.First(Partner, id).Error; err != nil { + Partner := new(entity.PartnerDBSearch) + + query := b.db.Table("partners p"). + Select("p.*, w.balance as wallet_balance, u.name as admin_name, u.email as admin_email, u.phone_number as admin_phone_number"). + Joins("LEFT JOIN wallets w ON w.partner_id = p.id"). + Joins("LEFT JOIN users u ON p.admin_user_id = u.id"). + Where("p.id = ? AND p.deleted_at IS NULL", id) + + if err := query.First(&Partner).Error; err != nil { logger.ContextLogger(ctx).Error("error when get by id Partner", zap.Error(err)) return nil, err } - return Partner, nil + + Partner.Partner.Balance = Partner.WalletBalance + Partner.Partner.AdminName = Partner.AdminName + Partner.Partner.AdminPhoneNumber = Partner.AdminPhoneNumber + Partner.Partner.AdminEmail = Partner.AdminEmail + + return &entity.PartnerDB{Partner.Partner}, nil } func (b *PartnerRepository) GetAll(ctx context.Context, req entity.PartnerSearch) (entity.PartnerList, int, error) { diff --git a/internal/repository/repository.go b/internal/repository/repository.go index 8682edb..1d5bad8 100644 --- a/internal/repository/repository.go +++ b/internal/repository/repository.go @@ -103,6 +103,7 @@ type User interface { GetUserByID(ctx context.Context, id int64) (*entity.UserDB, error) GetUserByEmail(ctx context.Context, email string) (*entity.UserDB, error) UpdateUser(ctx context.Context, user *entity.UserDB) (*entity.UserDB, error) + UpdateUserWithTx(ctx context.Context, tx *gorm.DB, user *entity.UserDB) (*entity.UserDB, error) } type Branch interface { diff --git a/internal/repository/users/user.go b/internal/repository/users/user.go index 589b71e..5d7fd98 100644 --- a/internal/repository/users/user.go +++ b/internal/repository/users/user.go @@ -122,7 +122,7 @@ func (b *UserRepository) GetUserByID(ctx context.Context, id int64) (*entity.Use var user *entity.UserDB query := b.db.Table("users"). - Select("users.id, users.email, users.name, users.status, users.created_at, users.updated_at, ur.role_id, r.role_name, ur.partner_id, b.name as partner_name"). + Select("users.id, users.email, users.password , users.name, users.status, users.created_at, users.updated_at, ur.role_id, r.role_name, ur.partner_id, b.name as partner_name"). Joins("LEFT JOIN user_roles ur ON users.id = ur.user_id"). Joins("LEFT JOIN roles r ON ur.role_id = r.role_id"). Joins("LEFT JOIN partners b ON ur.partner_id = b.id"). @@ -184,6 +184,22 @@ func (r *UserRepository) UpdateUser(ctx context.Context, user *entity.UserDB) (* return user, nil } +func (r *UserRepository) UpdateUserWithTx(ctx context.Context, tx *gorm.DB, user *entity.UserDB) (*entity.UserDB, error) { + if err := tx.Select("name", "email", "nik", "phone_number", "password", "status", "deleted_at", "updated_by").Save(user).Error; err != nil { + logError(ctx, "update user", err) + return nil, err + } + + userRole := user.ToUserRoleDB() + if err := tx.Model(userRole).Where("user_id = ?", user.ID).Updates(userRole).Error; err != nil { + tx.Rollback() + logError(ctx, "update user role", err) + return nil, err + } + + return user, nil +} + func logError(ctx context.Context, action string, err error) { logger.ContextLogger(ctx).Error("error when "+action, zap.Error(err)) } diff --git a/internal/services/partner/partner.go b/internal/services/partner/partner.go index 80cf2aa..03daf44 100644 --- a/internal/services/partner/partner.go +++ b/internal/services/partner/partner.go @@ -76,20 +76,44 @@ func (s *PartnerService) Create(ctx mycontext.Context, partnerReq *entity.Create return partnerDB.ToPartner(), nil } -func (s *PartnerService) Update(ctx mycontext.Context, id int64, PartnerReq *entity.Partner) (*entity.Partner, error) { - existingPartner, err := s.repo.GetByID(ctx, id) +func (s *PartnerService) Update(ctx mycontext.Context, req *entity.PartnerUpdate) (*entity.Partner, error) { + existingPartner, err := s.repo.GetByID(ctx, req.ID) if err != nil { return nil, err } + existingPartner.ToUpdatedPartnerData(ctx.RequestedBy(), *req) - existingPartner.ToUpdatedPartner(ctx.RequestedBy(), *PartnerReq) + tx, err := s.trx.Begin(ctx) + if err != nil { + logger.ContextLogger(ctx).Error("error when starting transaction", zap.Error(err)) + return nil, err + } - updatedPartnerDB, err := s.repo.Update(ctx, existingPartner.ToPartnerDB()) + defer func() { + if r := recover(); r != nil { + s.trx.Rollback(tx) + panic(r) + } else if err != nil { + s.trx.Rollback(tx) + } else { + err = s.trx.Commit(tx).Error + } + }() + + updatedPartnerDB, err := s.repo.UpdateWithTx(ctx, tx, existingPartner.ToPartnerDB()) if err != nil { logger.ContextLogger(ctx).Error("error when update Partner", zap.Error(err)) return nil, err } + if req.AdminUserID != 0 { + adminUser := req.ToUserAdmin(&req.ID) + if adminUser, err = s.userSvc.UpdateWithTx(ctx, tx, adminUser); err != nil { + logger.ContextLogger(ctx).Error("error when creating admin user", zap.Error(err)) + return nil, err + } + } + return updatedPartnerDB.ToPartner(), nil } diff --git a/internal/services/service.go b/internal/services/service.go index 8cfe370..f300887 100644 --- a/internal/services/service.go +++ b/internal/services/service.go @@ -72,6 +72,7 @@ type User interface { CreateWithTx(ctx mycontext.Context, tx *gorm.DB, userReq *entity.User) (*entity.User, error) GetAll(ctx mycontext.Context, search entity.UserSearch) ([]*entity.User, int, error) Update(ctx mycontext.Context, id int64, userReq *entity.User) (*entity.User, error) + UpdateWithTx(ctx mycontext.Context, tx *gorm.DB, req *entity.User) (*entity.User, error) GetByID(ctx mycontext.Context, id int64) (*entity.User, error) Delete(ctx mycontext.Context, id int64) error } @@ -115,7 +116,7 @@ type OSSService interface { type Partner interface { Create(ctx mycontext.Context, branchReq *entity.CreatePartnerRequest) (*entity.Partner, error) - Update(ctx mycontext.Context, id int64, branchReq *entity.Partner) (*entity.Partner, error) + Update(ctx mycontext.Context, branchReq *entity.PartnerUpdate) (*entity.Partner, error) GetByID(ctx context.Context, id int64) (*entity.Partner, error) GetAll(ctx context.Context, search entity.PartnerSearch) ([]*entity.Partner, int, error) Delete(ctx mycontext.Context, id int64) error diff --git a/internal/services/users/users.go b/internal/services/users/users.go index a6b6ce4..f8f10a5 100644 --- a/internal/services/users/users.go +++ b/internal/services/users/users.go @@ -1,6 +1,7 @@ package users import ( + "errors" "fmt" "furtuna-be/internal/common/mycontext" "gorm.io/gorm" @@ -101,6 +102,10 @@ func (s *UserService) Update(ctx mycontext.Context, id int64, userReq *entity.Us return nil, err } + if existingUser == nil { + return nil, errors.New("user not found") + } + //if changed branch if *userReq.PartnerID > 0 { branch, err := s.branchRepo.GetBranchByID(ctx, *userReq.PartnerID) @@ -125,6 +130,34 @@ func (s *UserService) Update(ctx mycontext.Context, id int64, userReq *entity.Us return updatedUserDB.ToUser(), nil } +func (s *UserService) UpdateWithTx(ctx mycontext.Context, tx *gorm.DB, req *entity.User) (*entity.User, error) { + existingUser, err := s.repo.GetUserByID(ctx, req.ID) + if err != nil { + return nil, err + } + + if existingUser == nil { + return nil, errors.New("user not found") + } + + if *existingUser.PartnerID != *req.PartnerID { + return nil, errors.New("invalid request") + } + + err = existingUser.ToUpdatedUser(*req) + if err != nil { + return nil, err + } + + updatedUserDB, err := s.repo.UpdateUserWithTx(ctx, tx, existingUser) + if err != nil { + logger.ContextLogger(ctx).Error("error when update user", zap.Error(err)) + return nil, err + } + + return updatedUserDB.ToUser(), nil +} + func (s *UserService) Delete(ctx mycontext.Context, id int64) error { userDB, err := s.repo.GetUserByID(ctx, id) if err != nil {