From 8a23e72230904b69c6a0fdb4d1bad22aa3309ed1 Mon Sep 17 00:00:00 2001 From: "aditya.siregar" Date: Mon, 3 Jun 2024 14:40:50 +0700 Subject: [PATCH] Add User Partner and Product --- .gitlab-ci.yml | 3 - Makefile | 4 +- furtuna-frontend | 1 + ...elopment.yaml => furtuna.development.yaml} | 0 internal/common/errors/errors.go | 6 + internal/common/mycontext/kinoscontext.go | 12 +- internal/constants/role/role.go | 6 +- internal/constants/studio/studio.go | 4 +- internal/entity/auth.go | 72 +++-- internal/entity/jwt.go | 10 +- internal/entity/partner.go | 84 ++++- internal/entity/product.go | 45 ++- internal/entity/sites.go | 145 +++++++++ internal/entity/user.go | 41 +-- internal/entity/wallet.go | 17 + internal/handlers/http/auth/auth.go | 15 +- internal/handlers/http/partner/partner.go | 2 +- internal/handlers/http/product/product.go | 3 - internal/handlers/http/sites/sites.go | 299 ++++++++++++++++++ internal/handlers/http/user/user.go | 24 +- internal/handlers/request/partner.go | 30 ++ internal/handlers/request/product.go | 20 +- internal/handlers/request/site.go | 75 +++++ internal/handlers/response/auth.go | 8 +- internal/handlers/response/partner.go | 6 +- internal/handlers/response/product.go | 25 +- internal/handlers/response/site.go | 27 ++ internal/handlers/response/user.go | 20 +- internal/repository/auth/init.go | 7 +- internal/repository/crypto/init.go | 14 +- internal/repository/partners/partners.go | 9 + internal/repository/repository.go | 32 +- internal/repository/sites/sites.go | 131 ++++++++ internal/repository/trx/trx.go | 33 ++ internal/repository/users/user.go | 23 ++ internal/repository/wallet/wallet.go | 65 ++++ internal/routes/routes.go | 2 + internal/services/partner/partner.go | 57 +++- internal/services/service.go | 18 +- internal/services/sites/sites.go | 89 ++++++ internal/services/users/users.go | 28 +- migrations/000002_add-role-table.up.sql | 7 +- migrations/000004_partner-table.up.sql | 22 +- migrations/000006_add_table_products.up.sql | 27 +- migrations/000009_add_wallet.down.sql | 0 migrations/000009_add_wallet.up.sql | 11 + 46 files changed, 1366 insertions(+), 213 deletions(-) create mode 160000 furtuna-frontend rename infra/{fortuna.development.yaml => furtuna.development.yaml} (100%) create mode 100644 internal/entity/sites.go create mode 100644 internal/entity/wallet.go create mode 100644 internal/handlers/http/sites/sites.go create mode 100644 internal/handlers/request/site.go create mode 100644 internal/handlers/response/site.go create mode 100644 internal/repository/sites/sites.go create mode 100644 internal/repository/trx/trx.go create mode 100644 internal/repository/wallet/wallet.go create mode 100644 internal/services/sites/sites.go create mode 100644 migrations/000009_add_wallet.down.sql create mode 100644 migrations/000009_add_wallet.up.sql diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 80115ae..fe4e683 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -30,6 +30,3 @@ deploy_to_staging: - kubectl apply -f k8s/staging/ingress.yaml only: - main - -# tes bintang 4 - diff --git a/Makefile b/Makefile index 327abb9..ac65320 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,9 @@ PROJECT_NAME = "furtuna-backend" -DB_USERNAME := furtuna_admin +DB_USERNAME := fortuna_admin DB_PASSWORD := Z4G827t9428QFQ%5ESZXW%2343dB%25%214Bmh80 DB_HOST := 103.96.146.124 DB_PORT := 1960 -DB_NAME := furtuna-staging +DB_NAME := fortuna-staging DB_URL = postgres://$(DB_USERNAME):$(DB_PASSWORD)@$(DB_HOST):$(DB_PORT)/$(DB_NAME)?sslmode=disable diff --git a/furtuna-frontend b/furtuna-frontend new file mode 160000 index 0000000..19af799 --- /dev/null +++ b/furtuna-frontend @@ -0,0 +1 @@ +Subproject commit 19af799abdd523522fad579044d55f5d764f87a3 diff --git a/infra/fortuna.development.yaml b/infra/furtuna.development.yaml similarity index 100% rename from infra/fortuna.development.yaml rename to infra/furtuna.development.yaml diff --git a/internal/common/errors/errors.go b/internal/common/errors/errors.go index 31c3820..55da8b4 100644 --- a/internal/common/errors/errors.go +++ b/internal/common/errors/errors.go @@ -72,6 +72,9 @@ func (s *ServiceException) MapErrorsToHTTPCode() int { case errInvalidLogin: return http.StatusBadRequest + case errUserIsNotFound: + return http.StatusBadRequest + default: return http.StatusInternalServerError } @@ -89,6 +92,9 @@ func (s *ServiceException) MapErrorsToCode() Code { case errBadRequest: return BadRequest + case errUserIsNotFound: + return BadRequest + default: return ServerError } diff --git a/internal/common/mycontext/kinoscontext.go b/internal/common/mycontext/kinoscontext.go index 9c3e5eb..c3e9ca3 100644 --- a/internal/common/mycontext/kinoscontext.go +++ b/internal/common/mycontext/kinoscontext.go @@ -13,6 +13,7 @@ type Context interface { RequestedBy() int64 IsSuperAdmin() bool + GetPartnerID() *int64 } type MyContextImpl struct { @@ -20,7 +21,7 @@ type MyContextImpl struct { requestedBy int64 requestID string - branchID int64 + partnerID int64 roleID int } @@ -32,11 +33,18 @@ func (m *MyContextImpl) IsSuperAdmin() bool { return m.roleID == int(role.SuperAdmin) } +func (m *MyContextImpl) GetPartnerID() *int64 { + if m.partnerID != 0 { + return &m.partnerID + } + return nil +} + func NewMyContext(parent context.Context, claims *entity.JWTAuthClaims) (*MyContextImpl, error) { return &MyContextImpl{ Context: parent, requestedBy: claims.UserID, - branchID: claims.BranchID, + partnerID: claims.PartnerID, roleID: claims.Role, }, nil } diff --git a/internal/constants/role/role.go b/internal/constants/role/role.go index b21f368..defaefb 100644 --- a/internal/constants/role/role.go +++ b/internal/constants/role/role.go @@ -4,6 +4,8 @@ type Role int64 const ( SuperAdmin Role = 1 - BranchAdmin Role = 2 - CasheerAdmin Role = 3 + Admin Role = 2 + PartnerAdmin Role = 3 + SiteAdmin Role = 4 + Casheer Role = 5 ) diff --git a/internal/constants/studio/studio.go b/internal/constants/studio/studio.go index 02eed3c..070eca1 100644 --- a/internal/constants/studio/studio.go +++ b/internal/constants/studio/studio.go @@ -3,8 +3,8 @@ package studio type StudioStatus string const ( - Active StudioStatus = "Active" - Inactive StudioStatus = "Inactive" + Active StudioStatus = "active" + Inactive StudioStatus = "inactive" ) func (b StudioStatus) toString() string { diff --git a/internal/entity/auth.go b/internal/entity/auth.go index db5f4bb..716bf9c 100644 --- a/internal/entity/auth.go +++ b/internal/entity/auth.go @@ -14,23 +14,24 @@ type AuthData struct { } type UserDB struct { - ID int64 `gorm:"primary_key;column:id" json:"id"` - Name string `gorm:"column:name" json:"name"` - Email string `gorm:"column:email" json:"email"` - Password string `gorm:"column:password" json:"-"` - Status userstatus.UserStatus `gorm:"column:status" json:"status"` - UserType string `gorm:"column:user_type" json:"user_type"` - PhoneNumber string `gorm:"column:phone_number" json:"phone_number"` - NIK string `gorm:"column:nik" json:"nik"` - RoleID int64 `gorm:"column:role_id" json:"role_id"` - RoleName string `gorm:"column:role_name" json:"role_name"` - BranchID *int64 `gorm:"column:partner_id" json:"partner_id"` - BranchName string `gorm:"column:partner_name" json:"partner_name"` - CreatedAt time.Time `gorm:"column:created_at" json:"created_at"` - UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"` - DeletedAt *time.Time `gorm:"column:deleted_at" json:"deleted_at"` - CreatedBy int64 `gorm:"column:created_by" json:"created_by"` - UpdatedBy int64 `gorm:"column:updated_by" json:"updated_by"` + ID int64 `gorm:"primary_key;column:id" json:"id"` + Name string `gorm:"column:name" json:"name"` + Email string `gorm:"column:email" json:"email"` + Password string `gorm:"column:password" json:"-"` + Status userstatus.UserStatus `gorm:"column:status" json:"status"` + UserType string `gorm:"column:user_type" json:"user_type"` + PhoneNumber string `gorm:"column:phone_number" json:"phone_number"` + NIK string `gorm:"column:nik" json:"nik"` + RoleID int64 `gorm:"column:role_id" json:"role_id"` + RoleName string `gorm:"column:role_name" json:"role_name"` + PartnerID *int64 `gorm:"column:partner_id" json:"partner_id"` + PartnerName string `gorm:"column:partner_name" json:"partner_name"` + PartnerStatus string `gorm:"column:partner_status" json:"partner_status"` + CreatedAt time.Time `gorm:"column:created_at" json:"created_at"` + UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"` + DeletedAt *time.Time `gorm:"column:deleted_at" json:"deleted_at"` + CreatedBy int64 `gorm:"column:created_by" json:"created_by"` + UpdatedBy int64 `gorm:"column:updated_by" json:"updated_by"` } func (u *UserDB) ToUser() *User { @@ -39,16 +40,16 @@ func (u *UserDB) ToUser() *User { } userEntity := &User{ - ID: u.ID, - Name: u.Name, - Email: u.Email, - Status: u.Status, - CreatedAt: u.CreatedAt, - UpdatedAt: u.UpdatedAt, - RoleID: role.Role(u.RoleID), - RoleName: u.RoleName, - PartnerID: u.BranchID, - BranchName: u.BranchName, + ID: u.ID, + Name: u.Name, + Email: u.Email, + Status: u.Status, + CreatedAt: u.CreatedAt, + UpdatedAt: u.UpdatedAt, + RoleID: role.Role(u.RoleID), + RoleName: u.RoleName, + PartnerID: u.PartnerID, + PartnerName: u.PartnerName, } return userEntity @@ -63,7 +64,7 @@ func (u *UserDB) ToUserRoleDB() *UserRoleDB { ID: 0, UserID: u.ID, RoleID: u.RoleID, - PartnerID: u.BranchID, + PartnerID: u.PartnerID, CreatedAt: u.CreatedAt, UpdatedAt: u.UpdatedAt, } @@ -77,12 +78,13 @@ func (UserDB) TableName() string { func (u *UserDB) ToUserAuthenticate(signedToken string) *AuthenticateUser { return &AuthenticateUser{ - Token: signedToken, - Name: u.Name, - RoleID: role.Role(u.RoleID), - RoleName: u.RoleName, - BranchID: u.BranchID, - BranchName: u.BranchName, + Token: signedToken, + Name: u.Name, + RoleID: role.Role(u.RoleID), + RoleName: u.RoleName, + PartnerID: u.PartnerID, + PartnerName: u.PartnerName, + PartnerStatus: u.PartnerStatus, } } @@ -116,7 +118,7 @@ func (u *UserDB) ToUpdatedUser(req User) error { } if *req.PartnerID > 0 { - u.BranchID = req.PartnerID + u.PartnerID = req.PartnerID } if req.RoleID > 0 { diff --git a/internal/entity/jwt.go b/internal/entity/jwt.go index 3946991..53686a6 100644 --- a/internal/entity/jwt.go +++ b/internal/entity/jwt.go @@ -3,10 +3,10 @@ package entity import "github.com/golang-jwt/jwt" type JWTAuthClaims struct { - UserID int64 `json:"id"` - Name string `json:"name"` - Email string `json:"email"` - Role int `json:"role"` - BranchID int64 `json:"branch_id"` + UserID int64 `json:"id"` + Name string `json:"name"` + Email string `json:"email"` + Role int `json:"role"` + PartnerID int64 `json:"partner_id"` jwt.StandardClaims } diff --git a/internal/entity/partner.go b/internal/entity/partner.go index 222ceaf..d87cc4f 100644 --- a/internal/entity/partner.go +++ b/internal/entity/partner.go @@ -1,19 +1,42 @@ package entity import ( + "furtuna-be/internal/constants/role" "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"` + 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"` +} + type Partner struct { - ID int64 - Name string - Status string - Address string - CreatedAt time.Time - UpdatedAt time.Time - DeletedAt *time.Time - CreatedBy int64 - UpdatedBy int64 + ID int64 `gorm:"primaryKey;autoIncrement;column:id"` + Name string `gorm:"type:varchar(255);not null;column:name"` + Status string `gorm:"type:varchar(50);column:status"` + LicenseExpiredDate *time.Time `gorm:"type:date;column:license_expired_date"` + Address string `gorm:"type:varchar(255);column:address"` + BankName string `gorm:"type:varchar(255);column:bank_name"` + BankAccountNumber string `gorm:"type:varchar(50);column:bank_account_number"` + BankAccountHolderName string `gorm:"type:varchar(255);column:bank_account_holder_name"` + CreatedAt time.Time `gorm:"autoCreateTime;column:created_at"` + UpdatedAt time.Time `gorm:"autoUpdateTime;column:updated_at"` + DeletedAt *time.Time `gorm:"column:deleted_at"` + CreatedBy int64 `gorm:"type:int;column:created_by"` + UpdatedBy int64 `gorm:"type:int;column:updated_by"` +} + +func (Partner) TableName() string { + return "partners" } type PartnerSearch struct { @@ -80,3 +103,46 @@ func (o *PartnerDB) SetDeleted(updatedBy int64) { o.DeletedAt = ¤tTime o.UpdatedBy = updatedBy } + +func (c *CreatePartnerRequest) ToUserAdmin(partnerID int64) *User { + return &User{ + Name: c.FullName, + Password: c.Password, + Email: c.Email, + NIK: c.NIK, + Status: "active", + RoleID: role.PartnerAdmin, + PartnerID: &partnerID, + } +} + +func (e *CreatePartnerRequest) ToPartnerDB(createdBy int64) *PartnerDB { + twoDays := 48 * time.Hour + licenseExpiredDate := time.Now().Add(twoDays) + + return &PartnerDB{ + Partner: Partner{ + Name: e.Name, + Status: "inactive", + Address: e.Address, + CreatedBy: createdBy, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + BankAccountHolderName: e.BankAccountHolderName, + BankAccountNumber: e.BankAccountNumber, + BankName: e.BankName, + LicenseExpiredDate: &licenseExpiredDate, + }, + } +} + +func (e *CreatePartnerRequest) ToWallet(partnerID int64) *Wallet { + return &Wallet{ + PartnerID: partnerID, + Balance: 0, + Currency: "IDR", + Status: "active", + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + } +} diff --git a/internal/entity/product.go b/internal/entity/product.go index cf8d55c..fb261d4 100644 --- a/internal/entity/product.go +++ b/internal/entity/product.go @@ -6,20 +6,25 @@ import ( ) type Product struct { - ID int64 - Name string - Type product.ProductType - Price float64 - Status product.ProductStatus - Description string - Image string - BranchID int64 - StockQty int64 - CreatedAt time.Time - UpdatedAt time.Time - DeletedAt *time.Time - CreatedBy int64 - UpdatedBy int64 + ID int64 `gorm:"primaryKey;autoIncrement;column:id"` + PartnerID int64 `gorm:"type:int;column:partner_id"` + SiteID int64 `gorm:"type:int;column:site_id"` + Name string `gorm:"type:varchar(255);not null;column:name"` + Type string `gorm:"type:varchar;column:type"` + Price float64 `gorm:"type:decimal;column:price"` + IsWeekendTicket bool `gorm:"type:bool;column:is_weekend_ticket"` + IsSeasonTicket bool `gorm:"type:bool;column:is_season_ticket"` + Status string `gorm:"type:varchar;column:status"` + Description string `gorm:"type:varchar(255);not null;column:description"` + CreatedAt time.Time `gorm:"autoCreateTime;column:created_at"` + UpdatedAt time.Time `gorm:"autoUpdateTime;column:updated_at"` + DeletedAt *time.Time `gorm:"column:deleted_at"` + CreatedBy int64 `gorm:"type:int;column:created_by"` + UpdatedBy int64 `gorm:"type:int;column:updated_by"` +} + +func (Product) TableName() string { + return "products" } type ProductSearch struct { @@ -56,9 +61,7 @@ func (e *ProductDB) ToProduct() *Product { Price: e.Price, Status: e.Status, Description: e.Description, - Image: e.Image, - BranchID: e.BranchID, - StockQty: e.StockQty, + PartnerID: e.PartnerID, CreatedAt: e.CreatedAt, UpdatedAt: e.UpdatedAt, DeletedAt: e.DeletedAt, @@ -97,14 +100,6 @@ func (o *ProductDB) ToUpdatedProduct(updatedby int64, req Product) { if req.Description != "" { o.Description = req.Description } - - if req.Image != "" { - o.Image = req.Image - } - - if req.StockQty > 0 { - o.StockQty = req.StockQty - } } func (o *ProductDB) SetDeleted(updatedby int64) { diff --git a/internal/entity/sites.go b/internal/entity/sites.go new file mode 100644 index 0000000..f45941f --- /dev/null +++ b/internal/entity/sites.go @@ -0,0 +1,145 @@ +package entity + +import ( + "time" +) + +type Site struct { + ID int64 `gorm:"primaryKey;autoIncrement;column:id"` + Name string `gorm:"type:varchar(255);not null;column:name"` + PartnerID int64 `gorm:"type:int;column:partner_id"` + Image string `gorm:"type:varchar;column:image"` + Address string `gorm:"type:varchar;column:address"` + LocationLink string `gorm:"type:varchar;column:location_link"` + Description string `gorm:"type:varchar;column:description"` + Highlight string `gorm:"type:varchar;column:highlight"` + ContactPerson string `gorm:"type:varchar;column:contact_person"` + TnC string `gorm:"type:varchar;column:tnc"` + AdditionalInfo string `gorm:"type:varchar;column:additional_info"` + Status string `gorm:"type:varchar;column:status"` + IsSeasonTicket bool `gorm:"type:bool;column:is_season_ticket"` + IsDiscountActive bool `gorm:"type:bool;column:is_discount_active"` + CreatedAt time.Time `gorm:"autoCreateTime;column:created_at"` + UpdatedAt time.Time `gorm:"autoUpdateTime;column:updated_at"` + DeletedAt *time.Time `gorm:"column:deleted_at"` + CreatedBy int64 `gorm:"type:int;column:created_by"` + UpdatedBy int64 `gorm:"type:int;column:updated_by"` + Products []Product `gorm:"foreignKey:SiteID;constraint:OnDelete:CASCADE;"` +} + +type SiteSearch struct { + Search string + Name string + Limit int + Offset int +} + +type SiteList []*SiteDB + +type SiteDB struct { + Site +} + +func (s *Site) ToSiteDB() *SiteDB { + return &SiteDB{ + Site: *s, + } +} + +func (SiteDB) TableName() string { + return "sites" +} + +func (e *SiteDB) ToSite() *Site { + return &Site{ + ID: e.ID, + Name: e.Name, + PartnerID: e.PartnerID, + Image: e.Image, + Address: e.Address, + LocationLink: e.LocationLink, + Description: e.Description, + Highlight: e.Highlight, + ContactPerson: e.ContactPerson, + TnC: e.TnC, + AdditionalInfo: e.AdditionalInfo, + Status: e.Status, + IsSeasonTicket: e.IsSeasonTicket, + IsDiscountActive: e.IsDiscountActive, + CreatedAt: e.CreatedAt, + UpdatedAt: e.UpdatedAt, + DeletedAt: e.DeletedAt, + CreatedBy: e.CreatedBy, + UpdatedBy: e.UpdatedBy, + } +} + +func (s *SiteList) ToSiteList() []*Site { + var sites []*Site + for _, site := range *s { + sites = append(sites, site.ToSite()) + } + return sites +} + +func (o *SiteDB) ToUpdatedSite(updatedBy int64, req Site) { + o.UpdatedBy = updatedBy + + if req.Name != "" { + o.Name = req.Name + } + + if req.PartnerID != 0 { + o.PartnerID = req.PartnerID + } + + if req.Image != "" { + o.Image = req.Image + } + + if req.Address != "" { + o.Address = req.Address + } + + if req.LocationLink != "" { + o.LocationLink = req.LocationLink + } + + if req.Description != "" { + o.Description = req.Description + } + + if req.Highlight != "" { + o.Highlight = req.Highlight + } + + if req.ContactPerson != "" { + o.ContactPerson = req.ContactPerson + } + + if req.TnC != "" { + o.TnC = req.TnC + } + + if req.AdditionalInfo != "" { + o.AdditionalInfo = req.AdditionalInfo + } + + if req.Status != "" { + o.Status = req.Status + } + + if req.IsSeasonTicket { + o.IsSeasonTicket = req.IsSeasonTicket + } + + if req.IsDiscountActive { + o.IsDiscountActive = req.IsDiscountActive + } +} + +func (o *SiteDB) SetDeleted(updatedBy int64) { + currentTime := time.Now() + o.DeletedAt = ¤tTime + o.UpdatedBy = updatedBy +} diff --git a/internal/entity/user.go b/internal/entity/user.go index 1583898..03d5f54 100644 --- a/internal/entity/user.go +++ b/internal/entity/user.go @@ -10,27 +10,28 @@ import ( ) type User struct { - ID int64 - Name string - Email string - Password string - Status userstatus.UserStatus - NIK string - CreatedAt time.Time - UpdatedAt time.Time - RoleID role.Role - RoleName string - PartnerID *int64 - BranchName string + ID int64 + Name string + Email string + Password string + Status userstatus.UserStatus + NIK string + CreatedAt time.Time + UpdatedAt time.Time + RoleID role.Role + RoleName string + PartnerID *int64 + PartnerName string } type AuthenticateUser struct { - Token string - Name string - RoleID role.Role - RoleName string - BranchID *int64 - BranchName string + Token string + Name string + RoleID role.Role + RoleName string + PartnerID *int64 + PartnerName string + PartnerStatus string } type UserRoleDB struct { @@ -53,7 +54,7 @@ func (u *User) ToUserDB(createdBy int64) (*UserDB, error) { return nil, err } - if u.RoleID == role.BranchAdmin && u.PartnerID == nil { + if u.RoleID == role.Admin && u.PartnerID == nil { return nil, errors.New("invalid request") } @@ -62,7 +63,7 @@ func (u *User) ToUserDB(createdBy int64) (*UserDB, error) { Email: u.Email, Password: hashedPassword, RoleID: int64(u.RoleID), - BranchID: u.PartnerID, + PartnerID: u.PartnerID, Status: userstatus.Active, CreatedBy: createdBy, }, nil diff --git a/internal/entity/wallet.go b/internal/entity/wallet.go new file mode 100644 index 0000000..214428a --- /dev/null +++ b/internal/entity/wallet.go @@ -0,0 +1,17 @@ +package entity + +import "time" + +type Wallet struct { + ID int64 `gorm:"primaryKey;autoIncrement;column:id"` + PartnerID int64 `gorm:"type:int;not null;column:partner_id"` + Balance float64 `gorm:"type:decimal(18,2);not null;default:0.00;column:balance"` + Currency string `gorm:"type:varchar(3);not null;column:currency"` + Status string `gorm:"type:varchar(50);column:status"` + CreatedAt time.Time `gorm:"autoCreateTime;column:created_at"` + UpdatedAt time.Time `gorm:"autoUpdateTime;column:updated_at"` +} + +func (Wallet) TableName() string { + return "wallets" +} diff --git a/internal/handlers/http/auth/auth.go b/internal/handlers/http/auth/auth.go index 6ac2d5b..fa398cf 100644 --- a/internal/handlers/http/auth/auth.go +++ b/internal/handlers/http/auth/auth.go @@ -51,19 +51,20 @@ func (h *AuthHandler) AuthLogin(c *gin.Context) { return } - var branch *response.Branch + var partner *response.Partner if authUser.RoleID != role.SuperAdmin { - branch = &response.Branch{ - ID: authUser.BranchID, - Name: authUser.BranchName, + partner = &response.Partner{ + ID: authUser.PartnerID, + Name: authUser.PartnerName, + Status: authUser.PartnerStatus, } } resp := response.LoginResponse{ - Token: authUser.Token, - Branch: branch, - Name: authUser.Name, + Token: authUser.Token, + Partner: partner, + Name: authUser.Name, Role: response.Role{ ID: int64(authUser.RoleID), Role: authUser.RoleName, diff --git a/internal/handlers/http/partner/partner.go b/internal/handlers/http/partner/partner.go index 5d1b1e4..97edde6 100644 --- a/internal/handlers/http/partner/partner.go +++ b/internal/handlers/http/partner/partner.go @@ -51,7 +51,7 @@ func NewHandler(service services.Partner) *Handler { func (h *Handler) Create(c *gin.Context) { ctx := request.GetMyContext(c) - var req request.Partner + var req request.CreatePartnerRequest if err := c.ShouldBindJSON(&req); err != nil { response.ErrorWrapper(c, errors.ErrorBadRequest) return diff --git a/internal/handlers/http/product/product.go b/internal/handlers/http/product/product.go index 7cc6dbd..91a833a 100644 --- a/internal/handlers/http/product/product.go +++ b/internal/handlers/http/product/product.go @@ -246,9 +246,6 @@ func (h *Handler) toProductResponse(resp *entity.Product) response.Product { Price: resp.Price, Status: resp.Status, Description: resp.Description, - Image: resp.Image, - BranchID: resp.BranchID, - StockQty: resp.StockQty, CreatedAt: resp.CreatedAt.Format(time.RFC3339), UpdatedAt: resp.CreatedAt.Format(time.RFC3339), } diff --git a/internal/handlers/http/sites/sites.go b/internal/handlers/http/sites/sites.go new file mode 100644 index 0000000..10fcb46 --- /dev/null +++ b/internal/handlers/http/sites/sites.go @@ -0,0 +1,299 @@ +package site + +import ( + "furtuna-be/internal/common/errors" + "furtuna-be/internal/entity" + "furtuna-be/internal/handlers/request" + "furtuna-be/internal/handlers/response" + "furtuna-be/internal/services" + "net/http" + "strconv" + "time" + + "github.com/gin-gonic/gin" + "github.com/go-playground/validator/v10" +) + +type Handler struct { + service services.Site +} + +func (h *Handler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) { + route := group.Group("/site") + + route.POST("/", jwt, h.Create) + route.GET("/list", jwt, h.GetAll) + route.PUT("/:id", jwt, h.Update) + route.GET("/:id", jwt, h.GetByID) + route.DELETE("/:id", jwt, h.Delete) +} + +func NewHandler(service services.Site) *Handler { + return &Handler{ + service: service, + } +} + +// Create handles the creation of a new Site. +// @Summary Create a new Site +// @Description Create a new Site based on the provided data. +// @Accept json +// @Produce json +// @Param Authorization header string true "JWT token" +// @Param req body request.Site true "New Site details" +// @Success 200 {object} response.BaseResponse{data=response.Site} "Site created successfully" +// @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request" +// @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized" +// @Router /api/v1/site [post] +// @Tags Site APIs +func (h *Handler) Create(c *gin.Context) { + ctx := request.GetMyContext(c) + + var req request.Site + if err := c.ShouldBindJSON(&req); err != nil { + response.ErrorWrapper(c, errors.ErrorBadRequest) + return + } + + if !ctx.IsSuperAdmin() { + req.PartnerID = ctx.GetPartnerID() + } + + validate := validator.New() + if err := validate.Struct(req); err != nil { + response.ErrorWrapper(c, err) + return + } + + res, err := h.service.Create(ctx, req.ToEntity(ctx.RequestedBy())) + + if err != nil { + response.ErrorWrapper(c, err) + return + } + + c.JSON(http.StatusOK, response.BaseResponse{ + Success: true, + Status: http.StatusOK, + Data: h.toSiteResponse(res), + }) +} + +// Update handles the update of an existing Site. +// @Summary Update an existing Site +// @Description Update the details of an existing Site based on the provided ID. +// @Accept json +// @Produce json +// @Param Authorization header string true "JWT token" +// @Param id path int64 true "Site ID to update" +// @Param req body request.Site true "Updated Site details" +// @Success 200 {object} response.BaseResponse{data=response.Site} "Site updated successfully" +// @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request" +// @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized" +// @Router /api/v1/site/{id} [put] +// @Tags Site APIs +func (h *Handler) Update(c *gin.Context) { + ctx := request.GetMyContext(c) + + id := c.Param("id") + + SiteID, err := strconv.ParseInt(id, 10, 64) + if err != nil { + response.ErrorWrapper(c, errors.ErrorBadRequest) + return + } + + var req request.Site + if err := c.ShouldBindJSON(&req); err != nil { + response.ErrorWrapper(c, errors.ErrorBadRequest) + return + } + + validate := validator.New() + if err := validate.Struct(req); err != nil { + response.ErrorWrapper(c, err) + return + } + + updatedSite, err := h.service.Update(ctx, SiteID, req.ToEntity(ctx.RequestedBy())) + if err != nil { + response.ErrorWrapper(c, err) + return + } + + c.JSON(http.StatusOK, response.BaseResponse{ + Success: true, + Status: http.StatusOK, + Data: h.toSiteResponse(updatedSite), + }) +} + +// GetAll retrieves a list of Sites. +// @Summary Get a list of Sites +// @Description Get a paginated list of Sites based on query parameters. +// @Accept json +// @Produce json +// @Param Authorization header string true "JWT token" +// @Param Limit query int false "Number of items to retrieve (default 10)" +// @Param Offset query int false "Offset for pagination (default 0)" +// @Success 200 {object} response.BaseResponse{data=response.SiteList} "List of Sites" +// @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request" +// @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized" +// @Router /api/v1/site/list [get] +// @Tags Site APIs +func (h *Handler) GetAll(c *gin.Context) { + var req request.SiteParam + if err := c.ShouldBindQuery(&req); err != nil { + response.ErrorWrapper(c, errors.ErrorBadRequest) + return + } + + Sites, total, err := h.service.GetAll(c.Request.Context(), req.ToEntity()) + if err != nil { + response.ErrorWrapper(c, err) + return + } + + c.JSON(http.StatusOK, response.BaseResponse{ + Success: true, + Status: http.StatusOK, + Data: h.toSiteResponseList(Sites, int64(total), req), + }) +} + +// Delete handles the deletion of a Site by ID. +// @Summary Delete a Site by ID +// @Description Delete a Site based on the provided ID. +// @Accept json +// @Produce json +// @Param Authorization header string true "JWT token" +// @Param id path int64 true "Site ID to delete" +// @Success 200 {object} response.BaseResponse "Site deleted successfully" +// @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request" +// @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized" +// @Router /api/v1/site/{id} [delete] +// @Tags Site APIs +func (h *Handler) Delete(c *gin.Context) { + ctx := request.GetMyContext(c) + id := c.Param("id") + + // Parse the ID into a uint + SiteID, err := strconv.ParseInt(id, 10, 64) + if err != nil { + response.ErrorWrapper(c, errors.ErrorBadRequest) + return + } + + err = h.service.Delete(ctx, SiteID) + if err != nil { + c.JSON(http.StatusInternalServerError, response.BaseResponse{ + Success: false, + Status: http.StatusInternalServerError, + Message: err.Error(), + Data: nil, + }) + return + } + + c.JSON(http.StatusOK, response.BaseResponse{ + Success: true, + Status: http.StatusOK, + Data: nil, + }) +} + +// GetByID retrieves details of a specific Site by ID. +// @Summary Get details of a Site by ID +// @Description Get details of a Site based on the provided ID. +// @Accept json +// @Produce json +// @Param Authorization header string true "JWT token" +// @Param id path int64 true "Site ID to retrieve" +// @Success 200 {object} response.BaseResponse{data=response.Site} "Site details" +// @Failure 400 {object} response.BaseResponse{data=errors.Error} "Bad request" +// @Failure 401 {object} response.BaseResponse{data=errors.Error} "Unauthorized" +// @Router /api/v1/site/{id} [get] +// @Tags Site APIs +func (h *Handler) GetByID(c *gin.Context) { + id := c.Param("id") + + // Parse the ID into a uint + SiteID, err := strconv.ParseInt(id, 10, 64) + if err != nil { + response.ErrorWrapper(c, errors.ErrorBadRequest) + return + } + + res, err := h.service.GetByID(c.Request.Context(), SiteID) + if err != nil { + c.JSON(http.StatusInternalServerError, response.BaseResponse{ + Success: false, + Status: http.StatusInternalServerError, + Message: err.Error(), + Data: nil, + }) + return + } + + c.JSON(http.StatusOK, response.BaseResponse{ + Success: true, + Status: http.StatusOK, + Data: h.toSiteResponse(res), + }) +} + +func (h *Handler) toSiteResponse(resp *entity.Site) response.Site { + return response.Site{ + ID: &resp.ID, + Name: resp.Name, + PartnerID: resp.PartnerID, + Image: resp.Image, + Address: resp.Address, + LocationLink: resp.LocationLink, + Description: resp.Description, + Highlight: resp.Highlight, + ContactPerson: resp.ContactPerson, + TnC: resp.TnC, + AdditionalInfo: resp.AdditionalInfo, + Status: resp.Status, + IsSeasonTicket: resp.IsSeasonTicket, + IsDiscountActive: resp.IsDiscountActive, + CreatedAt: resp.CreatedAt.Format(time.RFC3339), + UpdatedAt: resp.UpdatedAt.Format(time.RFC3339), + } +} + +func (h *Handler) toSiteResponseList(resp []*entity.Site, total int64, req request.SiteParam) response.SiteList { + var Sites []response.Site + for _, b := range resp { + Sites = append(Sites, h.toSiteResponse(b)) + } + + return response.SiteList{ + Sites: Sites, + Total: total, + Limit: req.Limit, + Offset: req.Offset, + } +} + +func (h *Handler) toProductResponseList(products []entity.Product) []response.Product { + var res []response.Product + for _, product := range products { + res = append(res, response.Product{ + ID: product.ID, + PartnerID: product.PartnerID, + SiteID: product.SiteID, + Name: product.Name, + Type: product.Type, + Price: product.Price, + IsWeekendTicket: product.IsWeekendTicket, + IsSeasonTicket: product.IsSeasonTicket, + Status: product.Status, + Description: product.Description, + CreatedAt: product.CreatedAt.Format(time.RFC3339), + UpdatedAt: product.UpdatedAt.Format(time.RFC3339), + }) + } + return res +} diff --git a/internal/handlers/http/user/user.go b/internal/handlers/http/user/user.go index 52e330e..bd97716 100644 --- a/internal/handlers/http/user/user.go +++ b/internal/handlers/http/user/user.go @@ -56,13 +56,15 @@ func (h *Handler) Create(c *gin.Context) { return } + req.PartnerID = ctx.GetPartnerID() if err := req.Validate(); err != nil { response.ErrorWrapper(c, errors.ErrorInvalidRequest) return } - res, err := h.service.Create(ctx, req.ToEntity()) + ctx.IsSuperAdmin() + res, err := h.service.Create(ctx, req.ToEntity()) if err != nil { response.ErrorWrapper(c, err) return @@ -260,16 +262,16 @@ func (h *Handler) Delete(c *gin.Context) { func (h *Handler) toUserResponse(resp *entity.User) response.User { return response.User{ - ID: resp.ID, - Name: resp.Name, - Email: resp.Email, - Status: string(resp.Status), - RoleID: int64(resp.RoleID), - RoleName: resp.RoleName, - PartnerID: resp.PartnerID, - BranchName: resp.BranchName, - CreatedAt: resp.CreatedAt.Format(time.RFC3339), - UpdatedAt: resp.CreatedAt.Format(time.RFC3339), + ID: resp.ID, + Name: resp.Name, + Email: resp.Email, + Status: string(resp.Status), + RoleID: int64(resp.RoleID), + RoleName: resp.RoleName, + PartnerID: resp.PartnerID, + PartnerName: resp.PartnerName, + CreatedAt: resp.CreatedAt.Format(time.RFC3339), + UpdatedAt: resp.CreatedAt.Format(time.RFC3339), } } diff --git a/internal/handlers/request/partner.go b/internal/handlers/request/partner.go index fff9c4d..ca64b08 100644 --- a/internal/handlers/request/partner.go +++ b/internal/handlers/request/partner.go @@ -26,6 +26,36 @@ type Partner struct { Status string `json:"status"` } +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"` + 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"` +} + +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, + NIK: e.NIK, + PhoneNumber: e.PhoneNumber, + BankName: e.BankName, + BankAccountNumber: e.BankAccountNumber, + BankAccountHolderName: e.BankAccountHolderName, + } +} + func (e *Partner) ToEntity() *entity.Partner { return &entity.Partner{ Name: e.Name, diff --git a/internal/handlers/request/product.go b/internal/handlers/request/product.go index 413322c..3b99b01 100644 --- a/internal/handlers/request/product.go +++ b/internal/handlers/request/product.go @@ -28,14 +28,15 @@ func (p *ProductParam) ToEntity() entity.ProductSearch { } type Product struct { - Name string `json:"name" validate:"required"` - Type product.ProductType `json:"type" validate:"required"` - Price float64 `json:"price" validate:"required"` - Status product.ProductStatus `json:"status" validate:"required"` - Description string `json:"description" ` - Image string `json:"image" ` - BranchID int64 `json:"branch_id" validate:"required"` - StockQty int64 `json:"stock_qty" ` + ID int64 `json:"id,omitempty"` + PartnerID int64 `json:"partner_id"` + Name string `json:"name" validate:"required"` + Type string `json:"type"` + Price float64 `json:"price" validate:"required"` + IsWeekendTicket bool `json:"is_weekend_ticket"` + IsSeasonTicket bool `json:"is_season_ticket"` + Status string `json:"status"` + Description string `json:"description" validate:"required"` } func (e *Product) ToEntity() *entity.Product { @@ -45,8 +46,5 @@ func (e *Product) ToEntity() *entity.Product { Price: e.Price, Status: e.Status, Description: e.Description, - Image: e.Image, - BranchID: e.BranchID, - StockQty: e.StockQty, } } diff --git a/internal/handlers/request/site.go b/internal/handlers/request/site.go new file mode 100644 index 0000000..10b11ff --- /dev/null +++ b/internal/handlers/request/site.go @@ -0,0 +1,75 @@ +package request + +import ( + "furtuna-be/internal/entity" +) + +type Site struct { + ID int64 `json:"id"` + Name string `json:"name" validate:"required"` + PartnerID *int64 `json:"partner_id"` + Image string `json:"image"` + Address string `json:"address"` + LocationLink string `json:"location_link"` + Description string `json:"description"` + Highlight string `json:"highlight"` + ContactPerson string `json:"contact_person"` + TnC string `json:"tnc"` + AdditionalInfo string `json:"additional_info"` + Status string `json:"status"` + IsSeasonTicket bool `json:"is_season_ticket"` + IsDiscountActive bool `json:"is_discount_active"` + Products []Product `json:"products"` +} + +func (r *Site) ToEntity(createdBy int64) *entity.Site { + var products []entity.Product + for _, p := range r.Products { + products = append(products, entity.Product{ + ID: p.ID, + PartnerID: *r.PartnerID, + Name: p.Name, + Type: p.Type, + Price: p.Price, + IsWeekendTicket: p.IsWeekendTicket, + IsSeasonTicket: p.IsSeasonTicket, + Status: p.Status, + Description: p.Description, + CreatedBy: createdBy, + }) + } + + return &entity.Site{ + ID: r.ID, + Name: r.Name, + PartnerID: *r.PartnerID, + Image: r.Image, + Address: r.Address, + LocationLink: r.LocationLink, + Description: r.Description, + Highlight: r.Highlight, + ContactPerson: r.ContactPerson, + TnC: r.TnC, + AdditionalInfo: r.AdditionalInfo, + Status: r.Status, + IsSeasonTicket: r.IsSeasonTicket, + IsDiscountActive: r.IsDiscountActive, + Products: products, + } +} + +type SiteParam struct { + Search string `form:"search"` + Name string `form:"name"` + Limit int `form:"limit,default=10"` + Offset int `form:"offset,default=0"` +} + +func (r *SiteParam) ToEntity() entity.SiteSearch { + return entity.SiteSearch{ + Search: r.Search, + Name: r.Name, + Limit: r.Limit, + Offset: r.Offset, + } +} diff --git a/internal/handlers/response/auth.go b/internal/handlers/response/auth.go index 1822ab2..37b3c77 100644 --- a/internal/handlers/response/auth.go +++ b/internal/handlers/response/auth.go @@ -1,10 +1,10 @@ package response type LoginResponse struct { - Token string `json:"token"` - Name string `json:"name"` - Role Role `json:"role"` - Branch *Branch `json:"branch"` + Token string `json:"token"` + Name string `json:"name"` + Role Role `json:"role"` + Partner *Partner `json:"partner"` } type Role struct { diff --git a/internal/handlers/response/partner.go b/internal/handlers/response/partner.go index 8578dfc..a7105f2 100644 --- a/internal/handlers/response/partner.go +++ b/internal/handlers/response/partner.go @@ -4,9 +4,9 @@ type Partner struct { ID *int64 `json:"id"` Name string `json:"name"` Status string `json:"status"` - Address string `json:"address"` - CreatedAt string `json:"created_at"` - UpdatedAt string `json:"updated_at"` + Address string `json:"address,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` } type PartnerList struct { diff --git a/internal/handlers/response/product.go b/internal/handlers/response/product.go index a514131..e867522 100644 --- a/internal/handlers/response/product.go +++ b/internal/handlers/response/product.go @@ -1,19 +1,18 @@ package response -import "furtuna-be/internal/constants/product" - type Product struct { - ID int64 `json:"id"` - Name string `json:"name"` - Type product.ProductType `json:"type"` - Price float64 `json:"price"` - Status product.ProductStatus `json:"status"` - Description string `json:"description" ` - Image string `json:"image" ` - BranchID int64 `json:"branch_id"` - StockQty int64 `json:"stock_qty"` - CreatedAt string `json:"created_at"` - UpdatedAt string `json:"updated_at"` + ID int64 `json:"id"` + PartnerID int64 `json:"partner_id"` + SiteID int64 `json:"site_id"` + Name string `json:"name"` + Type string `json:"type"` + Price float64 `json:"price"` + IsWeekendTicket bool `json:"is_weekend_ticket"` + IsSeasonTicket bool `json:"is_season_ticket"` + Status string `json:"status"` + Description string `json:"description"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` } type ProductList struct { diff --git a/internal/handlers/response/site.go b/internal/handlers/response/site.go new file mode 100644 index 0000000..9ddb7ee --- /dev/null +++ b/internal/handlers/response/site.go @@ -0,0 +1,27 @@ +package response + +type Site struct { + ID *int64 `json:"id"` + Name string `json:"name"` + PartnerID int64 `json:"partner_id"` + Image string `json:"image"` + Address string `json:"address"` + LocationLink string `json:"location_link"` + Description string `json:"description"` + Highlight string `json:"highlight"` + ContactPerson string `json:"contact_person"` + TnC string `json:"tnc"` + AdditionalInfo string `json:"additional_info"` + Status string `json:"status"` + IsSeasonTicket bool `json:"is_season_ticket"` + IsDiscountActive bool `json:"is_discount_active"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` +} + +type SiteList struct { + Sites []Site `json:"sites"` + Total int64 `json:"total"` + Limit int `json:"limit"` + Offset int `json:"offset"` +} diff --git a/internal/handlers/response/user.go b/internal/handlers/response/user.go index 01ad00c..a14ea22 100644 --- a/internal/handlers/response/user.go +++ b/internal/handlers/response/user.go @@ -1,16 +1,16 @@ package response type User struct { - ID int64 `json:"id"` - Name string `json:"name"` - Email string `json:"email"` - Status string `json:"status"` - RoleID int64 `json:"role_id"` - RoleName string `json:"role_name"` - PartnerID *int64 `json:"partner_id"` - BranchName string `json:"partner_name"` - CreatedAt string `json:"created_at,omitempty"` - UpdatedAt string `json:"updated_at,omitempty"` + ID int64 `json:"id"` + Name string `json:"name"` + Email string `json:"email"` + Status string `json:"status"` + RoleID int64 `json:"role_id"` + RoleName string `json:"role_name"` + PartnerID *int64 `json:"partner_id"` + PartnerName string `json:"partner_name"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` } type UserList struct { diff --git a/internal/repository/auth/init.go b/internal/repository/auth/init.go index 57a57d8..9d462bc 100644 --- a/internal/repository/auth/init.go +++ b/internal/repository/auth/init.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "go.uber.org/zap" "gorm.io/gorm" @@ -27,13 +26,17 @@ func (r *AuthRepository) CheckExistsUserAccount(ctx context.Context, email strin err := r.db. Table("users"). - Select("users.*, user_roles.role_id, user_roles.partner_id, roles.role_name, partners.name as partner_name"). + Select("users.*, user_roles.role_id, user_roles.partner_id, roles.role_name, partners.name as partner_name, partners.status as partner_status"). Where("users.email = ?", email). Joins("left join user_roles on users.id = user_roles.user_id"). Joins("left join roles on user_roles.role_id = roles.role_id"). Joins("left join partners on user_roles.partner_id = partners.id"). First(&user).Error + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, fmt.Errorf("user with email %s does not exist", email) // or use a custom error type diff --git a/internal/repository/crypto/init.go b/internal/repository/crypto/init.go index 0619d75..c5cd4cf 100644 --- a/internal/repository/crypto/init.go +++ b/internal/repository/crypto/init.go @@ -44,9 +44,9 @@ func (c *CryptoImpl) ValidateWT(tokenString string) (*jwt.Token, error) { } func (c *CryptoImpl) GenerateJWT(user *entity.User) (string, error) { - branchID := int64(0) + partnerID := int64(0) if user.PartnerID != nil { - branchID = *user.PartnerID + partnerID = *user.PartnerID } claims := &entity.JWTAuthClaims{ @@ -56,11 +56,11 @@ func (c *CryptoImpl) GenerateJWT(user *entity.User) (string, error) { IssuedAt: time.Now().Unix(), NotBefore: time.Now().Unix(), }, - UserID: user.ID, - Name: user.Name, - Email: user.Email, - Role: int(user.RoleID), - BranchID: branchID, + UserID: user.ID, + Name: user.Name, + Email: user.Email, + Role: int(user.RoleID), + PartnerID: partnerID, } token, err := jwt. diff --git a/internal/repository/partners/partners.go b/internal/repository/partners/partners.go index 68b8678..8cdf4f2 100644 --- a/internal/repository/partners/partners.go +++ b/internal/repository/partners/partners.go @@ -28,6 +28,15 @@ func (b *PartnerRepository) Create(ctx context.Context, Partner *entity.PartnerD return Partner, nil } +func (b *PartnerRepository) CreateWithTx(ctx context.Context, tx *gorm.DB, Partner *entity.PartnerDB) (*entity.PartnerDB, error) { + err := tx.Create(Partner).Error + if err != nil { + logger.ContextLogger(ctx).Error("error when create Partner", zap.Error(err)) + return nil, err + } + return Partner, nil +} + func (b *PartnerRepository) Update(ctx context.Context, Partner *entity.PartnerDB) (*entity.PartnerDB, error) { if err := b.db.Save(Partner).Error; err != nil { logger.ContextLogger(ctx).Error("error when update Partner", zap.Error(err)) diff --git a/internal/repository/repository.go b/internal/repository/repository.go index 925b300..acf6018 100644 --- a/internal/repository/repository.go +++ b/internal/repository/repository.go @@ -2,14 +2,17 @@ package repository import ( "context" + "database/sql" "furtuna-be/internal/repository/branches" "furtuna-be/internal/repository/orders" "furtuna-be/internal/repository/oss" "furtuna-be/internal/repository/partners" "furtuna-be/internal/repository/products" + "furtuna-be/internal/repository/sites" "furtuna-be/internal/repository/studios" + "furtuna-be/internal/repository/trx" "furtuna-be/internal/repository/users" - + repository "furtuna-be/internal/repository/wallet" "github.com/golang-jwt/jwt" "gorm.io/gorm" @@ -31,6 +34,9 @@ type RepoManagerImpl struct { Order Order OSS OSSRepository Partner PartnerRepository + Site SiteRepository + Trx TransactionManager + Wallet WalletRepository } func NewRepoManagerImpl(db *gorm.DB, cfg *config.Config) *RepoManagerImpl { @@ -45,6 +51,9 @@ func NewRepoManagerImpl(db *gorm.DB, cfg *config.Config) *RepoManagerImpl { Order: orders.NewOrderRepository(db), OSS: oss.NewOssRepositoryImpl(cfg.OSSConfig), Partner: partners.NewPartnerRepository(db), + Site: sites.NewSiteRepository(db), + Trx: trx.NewGormTransactionManager(db), + Wallet: repository.NewWalletRepository(db), } } @@ -69,6 +78,7 @@ type Crypto interface { type User interface { Create(ctx context.Context, user *entity.UserDB) (*entity.UserDB, error) + CreateWithTx(ctx context.Context, tx *gorm.DB, user *entity.UserDB) (*entity.UserDB, error) GetAllUsers(ctx context.Context, req entity.UserSearch) (entity.UserList, int, error) GetUserByID(ctx context.Context, id int64) (*entity.UserDB, error) GetUserByEmail(ctx context.Context, email string) (*entity.UserDB, error) @@ -115,8 +125,28 @@ type OSSRepository interface { type PartnerRepository interface { Create(ctx context.Context, branch *entity.PartnerDB) (*entity.PartnerDB, error) + CreateWithTx(ctx context.Context, tx *gorm.DB, Partner *entity.PartnerDB) (*entity.PartnerDB, error) Update(ctx context.Context, branch *entity.PartnerDB) (*entity.PartnerDB, error) GetByID(ctx context.Context, id int64) (*entity.PartnerDB, error) GetAll(ctx context.Context, req entity.PartnerSearch) (entity.PartnerList, int, error) Delete(ctx context.Context, id int64) error } + +type SiteRepository interface { + Upsert(ctx context.Context, site *entity.Site) (*entity.Site, error) + Create(ctx context.Context, branch *entity.SiteDB) (*entity.SiteDB, error) + Update(ctx context.Context, branch *entity.SiteDB) (*entity.SiteDB, error) + GetByID(ctx context.Context, id int64) (*entity.SiteDB, error) + GetAll(ctx context.Context, req entity.SiteSearch) (entity.SiteList, int, error) + Delete(ctx context.Context, id int64) error +} + +type TransactionManager interface { + Begin(ctx context.Context, opts ...*sql.TxOptions) (*gorm.DB, error) + Commit(session *gorm.DB) *gorm.DB + Rollback(session *gorm.DB) *gorm.DB +} + +type WalletRepository interface { + Create(ctx context.Context, tx *gorm.DB, wallet *entity.Wallet) (*entity.Wallet, error) +} diff --git a/internal/repository/sites/sites.go b/internal/repository/sites/sites.go new file mode 100644 index 0000000..83da564 --- /dev/null +++ b/internal/repository/sites/sites.go @@ -0,0 +1,131 @@ +package sites + +import ( + "context" + "furtuna-be/internal/common/logger" + "furtuna-be/internal/entity" + + "go.uber.org/zap" + "gorm.io/gorm" +) + +type SiteRepository struct { + db *gorm.DB +} + +func NewSiteRepository(db *gorm.DB) *SiteRepository { + return &SiteRepository{ + db: db, + } +} + +func (r *SiteRepository) Upsert(ctx context.Context, site *entity.Site) (*entity.Site, error) { + err := r.db.Transaction(func(tx *gorm.DB) error { + if site.ID != 0 { + // Update site + if err := tx.Save(site).Error; err != nil { + return err + } + } else { + // Create new site + if err := tx.Create(site).Error; err != nil { + return err + } + } + + if len(site.Products) > 0 { + for i := range site.Products { + site.Products[i].SiteID = site.ID + if site.Products[i].ID != 0 { + // Update existing product + if err := tx.Save(&site.Products[i]).Error; err != nil { + return err + } + } else { + // Create new product + if err := tx.Create(&site.Products[i]).Error; err != nil { + return err + } + } + } + } + return nil + }) + + if err != nil { + logger.ContextLogger(ctx).Error("error when upserting site", zap.Error(err)) + return nil, err + } + return site, nil +} + +func (r *SiteRepository) Create(ctx context.Context, site *entity.SiteDB) (*entity.SiteDB, error) { + err := r.db.Create(site).Error + if err != nil { + logger.ContextLogger(ctx).Error("error when creating site", zap.Error(err)) + return nil, err + } + return site, nil +} + +func (r *SiteRepository) Update(ctx context.Context, site *entity.SiteDB) (*entity.SiteDB, error) { + if err := r.db.Save(site).Error; err != nil { + logger.ContextLogger(ctx).Error("error when updating site", zap.Error(err)) + return nil, err + } + return site, nil +} + +func (r *SiteRepository) GetByID(ctx context.Context, id int64) (*entity.SiteDB, error) { + site := new(entity.SiteDB) + if err := r.db.First(site, id).Error; err != nil { + logger.ContextLogger(ctx).Error("error when getting site by ID", zap.Error(err)) + return nil, err + } + return site, nil +} + +func (r *SiteRepository) GetAll(ctx context.Context, req entity.SiteSearch) (entity.SiteList, int, error) { + var sites []*entity.SiteDB + var total int64 + + query := r.db + query = query.Where("deleted_at IS NULL") + + if req.Search != "" { + query = query.Where("name ILIKE ?", "%"+req.Search+"%") + } + + if req.Name != "" { + query = query.Where("name ILIKE ?", "%"+req.Name+"%") + } + + if req.Limit > 0 { + query = query.Limit(req.Limit) + } + + if req.Offset > 0 { + query = query.Offset(req.Offset) + } + + if err := query.Find(&sites).Error; err != nil { + logger.ContextLogger(ctx).Error("error when getting all sites", zap.Error(err)) + return nil, 0, err + } + + if err := r.db.Model(&entity.SiteDB{}).Where(query).Count(&total).Error; err != nil { + logger.ContextLogger(ctx).Error("error when counting sites", zap.Error(err)) + return nil, 0, err + } + + return sites, int(total), nil +} + +func (r *SiteRepository) Delete(ctx context.Context, id int64) error { + site := new(entity.SiteDB) + site.ID = id + if err := r.db.Delete(site).Error; err != nil { + return err + } + return nil +} diff --git a/internal/repository/trx/trx.go b/internal/repository/trx/trx.go new file mode 100644 index 0000000..4c583e6 --- /dev/null +++ b/internal/repository/trx/trx.go @@ -0,0 +1,33 @@ +package trx + +import ( + "context" + "database/sql" + "gorm.io/gorm" +) + +type GormTransactionManager struct { + db *gorm.DB +} + +func NewGormTransactionManager(db *gorm.DB) *GormTransactionManager { + return &GormTransactionManager{ + db: db, + } +} + +func (tm *GormTransactionManager) Begin(ctx context.Context, opts ...*sql.TxOptions) (*gorm.DB, error) { + tx := tm.db.Begin(opts...) + if tx.Error != nil { + return nil, tx.Error + } + return tx, nil +} + +func (tm *GormTransactionManager) Commit(tx *gorm.DB) *gorm.DB { + return tx.Commit() +} + +func (tm *GormTransactionManager) Rollback(tx *gorm.DB) *gorm.DB { + return tx.Rollback() +} diff --git a/internal/repository/users/user.go b/internal/repository/users/user.go index 8ef2869..589b71e 100644 --- a/internal/repository/users/user.go +++ b/internal/repository/users/user.go @@ -51,6 +51,29 @@ func (r *UserRepository) Create(ctx context.Context, user *entity.UserDB) (*enti return user, nil } +func (r *UserRepository) CreateWithTx(ctx context.Context, tx *gorm.DB, user *entity.UserDB) (*entity.UserDB, error) { + user.ID = 0 + if err := tx.Select("name", "email", "password", "status", "created_by", "nik", "user_type", "phone_number").Create(user).Error; err != nil { + logError(ctx, "creating user", err) + return nil, err + } + + if err := tx.First(user, user.ID).Error; err != nil { + tx.Rollback() + logError(ctx, "retrieving user", err) + return nil, err + } + + userRole := user.ToUserRoleDB() + if err := tx.Create(userRole).Error; err != nil { + tx.Rollback() + logError(ctx, "creating user role", err) + return nil, err + } + + return user, nil +} + func (b *UserRepository) GetAllUsers(ctx context.Context, req entity.UserSearch) (entity.UserList, int, error) { var users []*entity.UserDB var total int64 diff --git a/internal/repository/wallet/wallet.go b/internal/repository/wallet/wallet.go new file mode 100644 index 0000000..ec2e75e --- /dev/null +++ b/internal/repository/wallet/wallet.go @@ -0,0 +1,65 @@ +package repository + +import ( + "context" + "furtuna-be/internal/common/logger" + "furtuna-be/internal/entity" + + "go.uber.org/zap" + "gorm.io/gorm" +) + +type WalletRepository struct { + db *gorm.DB +} + +func NewWalletRepository(db *gorm.DB) *WalletRepository { + return &WalletRepository{ + db: db, + } +} + +func (r *WalletRepository) Create(ctx context.Context, tx *gorm.DB, wallet *entity.Wallet) (*entity.Wallet, error) { + err := tx.Create(wallet).Error + if err != nil { + logger.ContextLogger(ctx).Error("error when creating wallet", zap.Error(err)) + return nil, err + } + return wallet, nil +} + +func (r *WalletRepository) Update(ctx context.Context, wallet *entity.Wallet) (*entity.Wallet, error) { + if err := r.db.Save(wallet).Error; err != nil { + logger.ContextLogger(ctx).Error("error when updating wallet", zap.Error(err)) + return nil, err + } + return wallet, nil +} + +func (r *WalletRepository) GetByID(ctx context.Context, id int64) (*entity.Wallet, error) { + wallet := new(entity.Wallet) + if err := r.db.First(wallet, id).Error; err != nil { + logger.ContextLogger(ctx).Error("error when getting wallet by id", zap.Error(err)) + return nil, err + } + return wallet, nil +} + +func (r *WalletRepository) GetAll(ctx context.Context) ([]*entity.Wallet, error) { + var wallets []*entity.Wallet + if err := r.db.Find(&wallets).Error; err != nil { + logger.ContextLogger(ctx).Error("error when getting all wallets", zap.Error(err)) + return nil, err + } + return wallets, nil +} + +func (r *WalletRepository) Delete(ctx context.Context, id int64) error { + wallet := new(entity.Wallet) + wallet.ID = id + if err := r.db.Delete(wallet).Error; err != nil { + logger.ContextLogger(ctx).Error("error when deleting wallet", zap.Error(err)) + return err + } + return nil +} diff --git a/internal/routes/routes.go b/internal/routes/routes.go index 158992d..39f502b 100644 --- a/internal/routes/routes.go +++ b/internal/routes/routes.go @@ -6,6 +6,7 @@ import ( "furtuna-be/internal/handlers/http/oss" "furtuna-be/internal/handlers/http/partner" "furtuna-be/internal/handlers/http/product" + site "furtuna-be/internal/handlers/http/sites" "furtuna-be/internal/handlers/http/studio" "furtuna-be/internal/handlers/http/user" swaggerFiles "github.com/swaggo/files" @@ -52,6 +53,7 @@ func RegisterPrivateRoutes(app *app.Server, serviceManager *services.ServiceMana order.NewHandler(serviceManager.OrderSvc), oss.NewOssHandler(serviceManager.OSSSvc), partner.NewHandler(serviceManager.PartnerSvc), + site.NewHandler(serviceManager.SiteSvc), } for _, handler := range serverRoutes { diff --git a/internal/services/partner/partner.go b/internal/services/partner/partner.go index 99db53e..6cd984a 100644 --- a/internal/services/partner/partner.go +++ b/internal/services/partner/partner.go @@ -6,31 +6,68 @@ import ( "furtuna-be/internal/common/mycontext" "furtuna-be/internal/entity" "furtuna-be/internal/repository" - + "furtuna-be/internal/services/users" "go.uber.org/zap" ) type PartnerService struct { - repo repository.PartnerRepository + repo repository.PartnerRepository + trx repository.TransactionManager + userSvc *users.UserService + walletRepo repository.WalletRepository } -func NewPartnerService(repo repository.PartnerRepository) *PartnerService { +func NewPartnerService(repo repository.PartnerRepository, + userSvc *users.UserService, repoManager repository.TransactionManager, + walletRepo repository.WalletRepository) *PartnerService { return &PartnerService{ - repo: repo, + repo: repo, + userSvc: userSvc, + trx: repoManager, + walletRepo: walletRepo, } } -func (s *PartnerService) Create(ctx mycontext.Context, PartnerReq *entity.Partner) (*entity.Partner, error) { - PartnerDB := PartnerReq.ToPartnerDB() - PartnerDB.CreatedBy = ctx.RequestedBy() +func (s *PartnerService) Create(ctx mycontext.Context, partnerReq *entity.CreatePartnerRequest) (*entity.Partner, error) { + var err error - PartnerDB, err := s.repo.Create(ctx, PartnerDB) + tx, err := s.trx.Begin(ctx) if err != nil { - logger.ContextLogger(ctx).Error("error when create Partner", zap.Error(err)) + logger.ContextLogger(ctx).Error("error when starting transaction", zap.Error(err)) return nil, err } - return PartnerDB.ToPartner(), nil + 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 + } + }() + + // Create Partner + partnerDB := partnerReq.ToPartnerDB(ctx.RequestedBy()) + if partnerDB, err = s.repo.CreateWithTx(ctx, tx, partnerDB); err != nil { + logger.ContextLogger(ctx).Error("error when creating partner", zap.Error(err)) + return nil, err + } + + adminUser := partnerReq.ToUserAdmin(partnerDB.ID) + if adminUser, err = s.userSvc.CreateWithTx(ctx, tx, adminUser); err != nil { + logger.ContextLogger(ctx).Error("error when creating admin user", zap.Error(err)) + return nil, err + } + + partnerWallet := partnerReq.ToWallet(partnerDB.ID) + if partnerWallet, err = s.walletRepo.Create(ctx, tx, partnerWallet); err != nil { + logger.ContextLogger(ctx).Error("error when creating wallet", zap.Error(err)) + return nil, err + } + + return partnerDB.ToPartner(), nil } func (s *PartnerService) Update(ctx mycontext.Context, id int64, PartnerReq *entity.Partner) (*entity.Partner, error) { diff --git a/internal/services/service.go b/internal/services/service.go index ddcab25..99b122b 100644 --- a/internal/services/service.go +++ b/internal/services/service.go @@ -8,8 +8,10 @@ import ( "furtuna-be/internal/services/oss" "furtuna-be/internal/services/partner" "furtuna-be/internal/services/product" + "furtuna-be/internal/services/sites" "furtuna-be/internal/services/studio" "furtuna-be/internal/services/users" + "gorm.io/gorm" "furtuna-be/config" "furtuna-be/internal/entity" @@ -28,6 +30,7 @@ type ServiceManagerImpl struct { OrderSvc Order OSSSvc OSSService PartnerSvc Partner + SiteSvc Site } func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl) *ServiceManagerImpl { @@ -40,7 +43,9 @@ func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl) ProductSvc: product.NewProductService(repo.Product), OrderSvc: order.NewOrderService(repo.Order, repo.Product, repo.Studio), OSSSvc: oss.NewOSSService(repo.OSS), - PartnerSvc: partner.NewPartnerService(repo.Partner), + PartnerSvc: partner.NewPartnerService( + repo.Partner, users.NewUserService(repo.User, repo.Branch), repo.Trx, repo.Wallet), + SiteSvc: site.NewSiteService(repo.Site), } } @@ -58,6 +63,7 @@ type Event interface { type User interface { Create(ctx mycontext.Context, userReq *entity.User) (*entity.User, error) + 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) GetByID(ctx mycontext.Context, id int64) (*entity.User, error) @@ -102,9 +108,17 @@ type OSSService interface { } type Partner interface { - Create(ctx mycontext.Context, branchReq *entity.Partner) (*entity.Partner, error) + Create(ctx mycontext.Context, branchReq *entity.CreatePartnerRequest) (*entity.Partner, error) Update(ctx mycontext.Context, id int64, branchReq *entity.Partner) (*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 } + +type Site interface { + Create(ctx mycontext.Context, branchReq *entity.Site) (*entity.Site, error) + Update(ctx mycontext.Context, id int64, branchReq *entity.Site) (*entity.Site, error) + GetByID(ctx context.Context, id int64) (*entity.Site, error) + GetAll(ctx context.Context, search entity.SiteSearch) ([]*entity.Site, int, error) + Delete(ctx mycontext.Context, id int64) error +} diff --git a/internal/services/sites/sites.go b/internal/services/sites/sites.go new file mode 100644 index 0000000..e0d5f67 --- /dev/null +++ b/internal/services/sites/sites.go @@ -0,0 +1,89 @@ +package site + +import ( + "context" + "furtuna-be/internal/common/logger" + "furtuna-be/internal/common/mycontext" + "furtuna-be/internal/entity" + "furtuna-be/internal/repository" + + "go.uber.org/zap" +) + +type SiteService struct { + repo repository.SiteRepository +} + +func NewSiteService(repo repository.SiteRepository) *SiteService { + return &SiteService{ + repo: repo, + } +} + +func (s *SiteService) Create(ctx mycontext.Context, siteReq *entity.Site) (*entity.Site, error) { + siteDB := siteReq + siteDB.CreatedBy = ctx.RequestedBy() + + siteDB, err := s.repo.Upsert(ctx, siteDB) + if err != nil { + logger.ContextLogger(ctx).Error("error when creating site", zap.Error(err)) + return nil, err + } + + return siteDB, nil +} + +func (s *SiteService) Update(ctx mycontext.Context, id int64, siteReq *entity.Site) (*entity.Site, error) { + existingSite, err := s.repo.GetByID(ctx, id) + if err != nil { + return nil, err + } + + existingSite.ToUpdatedSite(ctx.RequestedBy(), *siteReq) + + updatedSiteDB, err := s.repo.Update(ctx, existingSite.ToSiteDB()) + if err != nil { + logger.ContextLogger(ctx).Error("error when updating site", zap.Error(err)) + return nil, err + } + + return updatedSiteDB.ToSite(), nil +} + +func (s *SiteService) GetByID(ctx context.Context, id int64) (*entity.Site, error) { + siteDB, err := s.repo.GetByID(ctx, id) + if err != nil { + logger.ContextLogger(ctx).Error("error when getting site by ID", zap.Error(err)) + return nil, err + } + + return siteDB.ToSite(), nil +} + +func (s *SiteService) GetAll(ctx context.Context, search entity.SiteSearch) ([]*entity.Site, int, error) { + sites, total, err := s.repo.GetAll(ctx, search) + if err != nil { + logger.ContextLogger(ctx).Error("error when getting all sites", zap.Error(err)) + return nil, 0, err + } + + return sites.ToSiteList(), total, nil +} + +func (s *SiteService) Delete(ctx mycontext.Context, id int64) error { + siteDB, err := s.repo.GetByID(ctx, id) + if err != nil { + logger.ContextLogger(ctx).Error("error when getting site by ID", zap.Error(err)) + return err + } + + siteDB.SetDeleted(ctx.RequestedBy()) + + _, err = s.repo.Update(ctx, siteDB) + if err != nil { + logger.ContextLogger(ctx).Error("error when updating site", zap.Error(err)) + return err + } + + return nil +} diff --git a/internal/services/users/users.go b/internal/services/users/users.go index e9cb3c1..a6b6ce4 100644 --- a/internal/services/users/users.go +++ b/internal/services/users/users.go @@ -3,6 +3,7 @@ package users import ( "fmt" "furtuna-be/internal/common/mycontext" + "gorm.io/gorm" "go.uber.org/zap" @@ -48,6 +49,31 @@ func (s *UserService) Create(ctx mycontext.Context, userReq *entity.User) (*enti return userDB.ToUser(), nil } +func (s *UserService) CreateWithTx(ctx mycontext.Context, tx *gorm.DB, userReq *entity.User) (*entity.User, error) { + //check + userExist, err := s.repo.GetUserByEmail(ctx, userReq.Email) + if err != nil { + return nil, err + } + + if userExist != nil { + return nil, fmt.Errorf("Email already exist") + } + + userDB, err := userReq.ToUserDB(ctx.RequestedBy()) + if err != nil { + return nil, err + } + + userDB, err = s.repo.CreateWithTx(ctx, tx, userDB) + if err != nil { + logger.ContextLogger(ctx).Error("error when create user", zap.Error(err)) + return nil, err + } + + return userDB.ToUser(), nil +} + func (s *UserService) GetAll(ctx mycontext.Context, search entity.UserSearch) ([]*entity.User, int, error) { users, total, err := s.repo.GetAllUsers(ctx, search) @@ -82,7 +108,7 @@ func (s *UserService) Update(ctx mycontext.Context, id int64, userReq *entity.Us return nil, err } - existingUser.BranchName = branch.Name + existingUser.PartnerName = branch.Name } err = existingUser.ToUpdatedUser(*userReq) diff --git a/migrations/000002_add-role-table.up.sql b/migrations/000002_add-role-table.up.sql index b739b30..68ecb23 100644 --- a/migrations/000002_add-role-table.up.sql +++ b/migrations/000002_add-role-table.up.sql @@ -3,4 +3,9 @@ CREATE TABLE roles ( role_name VARCHAR(255) NOT NULL ); -INSERT INTO roles(role_id, role_name) VALUES (1, 'super_admin'), (2, 'branch_admin'); +INSERT INTO roles(role_id, role_name) +VALUES (1, 'super_admin'), + (2, 'admin'), + (3, 'admin_partner'), + (4, 'admin_site'), + (5, 'cashier'); diff --git a/migrations/000004_partner-table.up.sql b/migrations/000004_partner-table.up.sql index 3a3ef31..4637326 100644 --- a/migrations/000004_partner-table.up.sql +++ b/migrations/000004_partner-table.up.sql @@ -1,12 +1,16 @@ CREATE TABLE partners ( - id SERIAL PRIMARY KEY, - name VARCHAR(255) NOT NULL, - status varchar, - Address varchar, - created_at TIMESTAMP NOT NULL DEFAULT NOW(), - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - created_by INTEGER, - updated_by INTEGER, - deleted_at timestamp + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + status VARCHAR(50), + license_expired_date DATE, + address VARCHAR(255), + bank_name VARCHAR(255), + bank_account_number VARCHAR(50), + bank_account_holder_name VARCHAR(255), + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + created_by INTEGER, + updated_by INTEGER, + deleted_at TIMESTAMP ); \ No newline at end of file diff --git a/migrations/000006_add_table_products.up.sql b/migrations/000006_add_table_products.up.sql index 1a2ffdd..7000684 100644 --- a/migrations/000006_add_table_products.up.sql +++ b/migrations/000006_add_table_products.up.sql @@ -1,15 +1,18 @@ CREATE TABLE products ( - id SERIAL PRIMARY KEY, - site_id INTEGER, - name VARCHAR(255) NOT NULL, - type VARCHAR, - prices INTEGER[], - status VARCHAR, - description VARCHAR(255) NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT NOW(), - updated_at TIMESTAMP NOT NULL DEFAULT NOW(), - deleted_at TIMESTAMP, - created_by INTEGER, - updated_by INTEGER + id SERIAL PRIMARY KEY, + partner_id INTEGER, + site_id INTEGER, + name VARCHAR(255) NOT NULL, + type VARCHAR, + price decimal, + is_weekend_ticket boolean, + is_season_ticket boolean, + status VARCHAR, + description VARCHAR(255) NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW(), + deleted_at TIMESTAMP, + created_by INTEGER, + updated_by INTEGER ); \ No newline at end of file diff --git a/migrations/000009_add_wallet.down.sql b/migrations/000009_add_wallet.down.sql new file mode 100644 index 0000000..e69de29 diff --git a/migrations/000009_add_wallet.up.sql b/migrations/000009_add_wallet.up.sql new file mode 100644 index 0000000..e9bbeda --- /dev/null +++ b/migrations/000009_add_wallet.up.sql @@ -0,0 +1,11 @@ +CREATE TABLE public.wallets +( + id SERIAL PRIMARY KEY, + partner_id INTEGER NOT NULL, + balance DECIMAL(18, 2) NOT NULL DEFAULT 0.00, + currency VARCHAR(3) NOT NULL, + status VARCHAR(50), + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW(), + FOREIGN KEY (partner_id) REFERENCES partners (id) ON DELETE CASCADE +); \ No newline at end of file