| package controllers |
|
|
| import ( |
| "fmt" |
| "net/http" |
| "strconv" |
| "time" |
|
|
| "service-warungpos-go/config" |
| "service-warungpos-go/models" |
| "service-warungpos-go/services" |
|
|
| "github.com/gin-gonic/gin" |
| ) |
|
|
| |
| type CompanyResponse struct { |
| ID uint `json:"id"` |
| Name string `json:"name"` |
| BankAccountNumber string `json:"bank_account_number"` |
| BankName string `json:"bank_name"` |
| Address string `json:"address"` |
| Email string `json:"email"` |
| Phone string `json:"phone"` |
| LogoURL string `json:"logo_url"` |
| IsActive bool `json:"is_active"` |
| FeePercentage float64 `json:"fee_percentage"` |
| CashFeeRules string `json:"cash_fee_rules"` |
| QrisFeeRules string `json:"qris_fee_rules"` |
| QrisFeePercentage float64 `json:"qris_fee_percentage"` |
| QrisFeeFixed int `json:"qris_fee_fixed"` |
| QrisFeeMin int `json:"qris_fee_min"` |
| QrisMinTransaction int `json:"qris_min_transaction"` |
| AllowQrisBelowMin bool `json:"allow_qris_below_min"` |
| ChargeFeeBeforeDiscount bool `json:"charge_fee_before_discount"` |
| WalletBalance float64 `json:"wallet_balance"` |
| WalletLimit float64 `json:"wallet_limit"` |
| EnableMember bool `json:"enable_member"` |
| EnableVoucher bool `json:"enable_voucher"` |
| EnableMultiBranch bool `json:"enable_multi_branch"` |
| MaxBranches int `json:"max_branches"` |
| EnableMultiKasir bool `json:"enable_multi_kasir"` |
| MaxCashiers int `json:"max_cashiers"` |
| EnforceSingleSession bool `json:"enforce_single_session"` |
| ApprovedAt *time.Time `json:"approved_at"` |
| OwnerVerified *bool `json:"owner_verified,omitempty"` |
| OwnerName string `json:"owner_name,omitempty"` |
| OwnerEmail string `json:"owner_email,omitempty"` |
| OwnerPhone string `json:"owner_phone,omitempty"` |
| PointRules []models.PointRule `json:"point_rules"` |
| CreatedAt time.Time `json:"created_at"` |
| } |
|
|
| type CompanyCreateRequest struct { |
| Name string `json:"name" binding:"required"` |
| BankAccountNumber string `json:"bank_account_number"` |
| BankName string `json:"bank_name"` |
| Address string `json:"address"` |
| Email string `json:"email"` |
| Phone string `json:"phone"` |
| LogoURL string `json:"logo_url"` |
| FeePercentage float64 `json:"fee_percentage"` |
| TripayMerchantCode string `json:"tripay_merchant_code"` |
| TripayAPIKey string `json:"tripay_api_key"` |
| TripayPrivateKey string `json:"tripay_private_key"` |
| TripayProxyURL string `json:"tripay_proxy_url"` |
| } |
|
|
| type PointRuleInput struct { |
| MinAmount int `json:"min_amount" binding:"required"` |
| PointReward int `json:"point_reward" binding:"required"` |
| } |
|
|
| type UpdateCompanyPointsRequest struct { |
| PointRules []PointRuleInput `json:"point_rules"` |
| } |
|
|
| type BranchCreateRequest struct { |
| Name string `json:"name" binding:"required"` |
| Address string `json:"address"` |
| Phone string `json:"phone"` |
| IsPointEnabled *bool `json:"is_point_enabled"` |
| } |
|
|
| type BranchResponse struct { |
| ID uint `json:"id"` |
| CompanyID uint `json:"company_id"` |
| Name string `json:"name"` |
| Address string `json:"address"` |
| Phone string `json:"phone"` |
| IsActive bool `json:"is_active"` |
| CreatedAt time.Time `json:"created_at"` |
| } |
|
|
| type BranchProductResponse struct { |
| ID uint `json:"id"` |
| Name string `json:"name"` |
| Price int `json:"price"` |
| CategoryID *uint `json:"category_id"` |
| ImageURL string `json:"image_url"` |
| IsAvailable bool `json:"is_available"` |
| } |
|
|
| |
|
|
| func ListCompanies(c *gin.Context) { |
| limitStr := c.Query("limit") |
| skipStr := c.Query("skip") |
| searchQuery := c.Query("search") |
|
|
| query := config.DB.Model(&models.Company{}). |
| Select("id, name, bank_account_number, bank_name, address, email, phone, logo_url, is_active, fee_percentage, cash_fee_rules, qris_fee_rules, qris_fee_percentage, qris_fee_fixed, qris_fee_min, qris_min_transaction, allow_qris_below_min, charge_fee_before_discount, wallet_balance, wallet_limit, enable_member, enable_voucher, enable_multi_branch, max_branches, enable_multi_kasir, max_cashiers, enforce_single_session, approved_at, created_at") |
|
|
| if searchQuery != "" { |
| query = query.Where("name ILIKE ? OR email ILIKE ? OR phone ILIKE ? OR address ILIKE ?", |
| "%"+searchQuery+"%", |
| "%"+searchQuery+"%", |
| "%"+searchQuery+"%", |
| "%"+searchQuery+"%") |
| } |
|
|
| if limitStr != "" { |
| if limit, err := strconv.Atoi(limitStr); err == nil { |
| query = query.Limit(limit) |
| } |
| } |
| if skipStr != "" { |
| if skip, err := strconv.Atoi(skipStr); err == nil { |
| query = query.Offset(skip) |
| } |
| } |
|
|
| var companies []models.Company |
| if err := query.Preload("PointRules").Find(&companies).Error; err != nil { |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal mengambil data perusahaan"}) |
| return |
| } |
|
|
| if len(companies) == 0 { |
| c.JSON(http.StatusOK, []CompanyResponse{}) |
| return |
| } |
|
|
| var companyIDs []uint |
| for _, comp := range companies { |
| companyIDs = append(companyIDs, comp.ID) |
| } |
|
|
| |
| var owners []models.User |
| config.DB.Select("id, name, email, phone, company_id"). |
| Where("company_id IN ? AND role = ?", companyIDs, "owner"). |
| Find(&owners) |
|
|
| |
| ownerMap := make(map[uint]models.User) |
| var ownerPhones []string |
| for _, o := range owners { |
| if o.CompanyID != nil { |
| ownerMap[*o.CompanyID] = o |
| if o.Phone != "" { |
| ownerPhones = append(ownerPhones, o.Phone) |
| } |
| } |
| } |
|
|
| |
| otpMap := make(map[string]bool) |
| if len(ownerPhones) > 0 { |
| var otps []models.OTP |
| config.DB.Select("phone_number"). |
| Where("phone_number IN ? AND is_verified = ?", ownerPhones, true). |
| Find(&otps) |
| for _, otp := range otps { |
| otpMap[otp.PhoneNumber] = true |
| } |
| } |
|
|
| resp := []CompanyResponse{} |
| for _, comp := range companies { |
| owner, hasOwner := ownerMap[comp.ID] |
| var ownerVerified *bool |
| var ownerName, ownerEmail, ownerPhone string |
|
|
| if hasOwner { |
| ownerName = owner.Name |
| ownerEmail = owner.Email |
| ownerPhone = owner.Phone |
|
|
| if owner.Phone != "" { |
| isVerified := otpMap[owner.Phone] |
| ownerVerified = &isVerified |
| } |
| } |
|
|
| resp = append(resp, CompanyResponse{ |
| ID: comp.ID, |
| Name: comp.Name, |
| BankAccountNumber: comp.BankAccountNumber, |
| BankName: comp.BankName, |
| Address: comp.Address, |
| Email: comp.Email, |
| Phone: comp.Phone, |
| LogoURL: comp.LogoURL, |
| IsActive: comp.IsActive, |
| FeePercentage: comp.FeePercentage, |
| ApprovedAt: comp.ApprovedAt, |
| OwnerVerified: ownerVerified, |
| OwnerName: ownerName, |
| OwnerEmail: ownerEmail, |
| OwnerPhone: ownerPhone, |
| PointRules: comp.PointRules, |
| CashFeeRules: comp.CashFeeRules, |
| QrisFeeRules: comp.QrisFeeRules, |
| QrisFeePercentage: comp.QrisFeePercentage, |
| QrisFeeFixed: comp.QrisFeeFixed, |
| QrisFeeMin: comp.QrisFeeMin, |
| QrisMinTransaction: comp.QrisMinTransaction, |
| AllowQrisBelowMin: comp.AllowQrisBelowMin, |
| ChargeFeeBeforeDiscount: comp.ChargeFeeBeforeDiscount, |
| WalletBalance: comp.WalletBalance, |
| WalletLimit: comp.WalletLimit, |
| EnableMember: comp.EnableMember, |
| EnableVoucher: comp.EnableVoucher, |
| EnableMultiBranch: comp.EnableMultiBranch, |
| MaxBranches: comp.MaxBranches, |
| EnableMultiKasir: comp.EnableMultiKasir, |
| MaxCashiers: comp.MaxCashiers, |
| EnforceSingleSession: comp.EnforceSingleSession, |
| CreatedAt: comp.CreatedAt, |
| }) |
| } |
|
|
| c.JSON(http.StatusOK, resp) |
| } |
|
|
| func GetMyCompany(c *gin.Context) { |
| userVal, _ := c.Get("user") |
| user := userVal.(*models.User) |
|
|
| if user.CompanyID == nil { |
| c.JSON(http.StatusNotFound, gin.H{"detail": "Tidak terhubung ke perusahaan"}) |
| return |
| } |
|
|
| var company models.Company |
| if err := config.DB.Preload("PointRules").First(&company, *user.CompanyID).Error; err != nil { |
| c.JSON(http.StatusNotFound, gin.H{"detail": "Perusahaan tidak ditemukan"}) |
| return |
| } |
|
|
| c.JSON(http.StatusOK, company) |
| } |
|
|
| func UpdateMyCompanyPoints(c *gin.Context) { |
| userVal, exists := c.Get("user") |
| if !exists { |
| c.JSON(http.StatusUnauthorized, gin.H{"detail": "Unauthorized"}) |
| return |
| } |
| user := userVal.(*models.User) |
|
|
| if user.CompanyID == nil || user.Role != "owner" { |
| c.JSON(http.StatusForbidden, gin.H{"detail": "Hanya owner yang bisa mengatur poin"}) |
| return |
| } |
|
|
| var req UpdateCompanyPointsRequest |
| if err := c.ShouldBindJSON(&req); err != nil { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": "Data tidak valid"}) |
| return |
| } |
| |
| config.DB.Where("company_id = ?", *user.CompanyID).Delete(&models.PointRule{}) |
|
|
| |
| var newRules []models.PointRule |
| for _, r := range req.PointRules { |
| newRules = append(newRules, models.PointRule{ |
| CompanyID: *user.CompanyID, |
| MinAmount: r.MinAmount, |
| PointReward: r.PointReward, |
| }) |
| } |
|
|
| if len(newRules) > 0 { |
| if err := config.DB.Create(&newRules).Error; err != nil { |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menyimpan pengaturan poin"}) |
| return |
| } |
| } |
|
|
| var updatedCompany models.Company |
| config.DB.Preload("PointRules").First(&updatedCompany, *user.CompanyID) |
|
|
| c.JSON(http.StatusOK, gin.H{"message": "Pengaturan poin berhasil disimpan", "company": updatedCompany}) |
| } |
|
|
| func GetCompany(c *gin.Context) { |
| companyIDStr := c.Param("company_id") |
| companyID, err := strconv.Atoi(companyIDStr) |
| if err != nil { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": "ID perusahaan tidak valid"}) |
| return |
| } |
|
|
| var company models.Company |
| if err := config.DB.First(&company, companyID).Error; err != nil { |
| c.JSON(http.StatusNotFound, gin.H{"detail": "Company not found"}) |
| return |
| } |
|
|
| c.JSON(http.StatusOK, company) |
| } |
|
|
| func CreateCompany(c *gin.Context) { |
| var req CompanyCreateRequest |
| if err := c.ShouldBindJSON(&req); err != nil { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": err.Error()}) |
| return |
| } |
|
|
| var count int64 |
| config.DB.Model(&models.Company{}).Where("name = ?", req.Name).Count(&count) |
| if count > 0 { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": "Company name already exists"}) |
| return |
| } |
|
|
| newComp := models.Company{ |
| Name: req.Name, |
| BankAccountNumber: req.BankAccountNumber, |
| BankName: req.BankName, |
| Address: req.Address, |
| Email: req.Email, |
| Phone: req.Phone, |
| LogoURL: req.LogoURL, |
| IsActive: true, |
| FeePercentage: req.FeePercentage, |
| TripayMerchantCode: req.TripayMerchantCode, |
| TripayAPIKey: req.TripayAPIKey, |
| TripayPrivateKey: req.TripayPrivateKey, |
| TripayProxyURL: req.TripayProxyURL, |
| } |
|
|
| if err := config.DB.Create(&newComp).Error; err != nil { |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal membuat perusahaan baru"}) |
| return |
| } |
|
|
| c.JSON(http.StatusCreated, newComp) |
| } |
|
|
| func UpdateCompany(c *gin.Context) { |
| companyIDStr := c.Param("company_id") |
| companyID, err := strconv.Atoi(companyIDStr) |
| if err != nil { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": "ID perusahaan tidak valid"}) |
| return |
| } |
|
|
| var req CompanyCreateRequest |
| if err := c.ShouldBindJSON(&req); err != nil { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": err.Error()}) |
| return |
| } |
|
|
| var company models.Company |
| if err := config.DB.First(&company, companyID).Error; err != nil { |
| c.JSON(http.StatusNotFound, gin.H{"detail": "Company not found"}) |
| return |
| } |
|
|
| if req.Name != "" { |
| var count int64 |
| config.DB.Model(&models.Company{}).Where("name = ? AND id != ?", req.Name, companyID).Count(&count) |
| if count > 0 { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": "Company name already exists"}) |
| return |
| } |
| company.Name = req.Name |
| } |
|
|
| company.BankAccountNumber = req.BankAccountNumber |
| company.BankName = req.BankName |
| company.Address = req.Address |
| company.Email = req.Email |
| company.Phone = req.Phone |
| company.LogoURL = req.LogoURL |
| company.FeePercentage = req.FeePercentage |
| company.TripayMerchantCode = req.TripayMerchantCode |
| company.TripayAPIKey = req.TripayAPIKey |
| company.TripayPrivateKey = req.TripayPrivateKey |
| company.TripayProxyURL = req.TripayProxyURL |
|
|
| if err := config.DB.Save(&company).Error; err != nil { |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal meng-update perusahaan"}) |
| return |
| } |
|
|
| c.JSON(http.StatusOK, company) |
| } |
|
|
| func ToggleActiveCompany(c *gin.Context) { |
| companyIDStr := c.Param("company_id") |
| companyID, err := strconv.Atoi(companyIDStr) |
| if err != nil { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": "ID perusahaan tidak valid"}) |
| return |
| } |
|
|
| var company models.Company |
| if err := config.DB.First(&company, companyID).Error; err != nil { |
| c.JSON(http.StatusNotFound, gin.H{"detail": "Company not found"}) |
| return |
| } |
|
|
| tx := config.DB.Begin() |
| defer func() { |
| if r := recover(); r != nil { |
| tx.Rollback() |
| } |
| }() |
|
|
| company.IsActive = !company.IsActive |
| tx.Save(&company) |
|
|
| |
| var owner models.User |
| if err := tx.Where("company_id = ? AND role = ?", companyID, "owner").First(&owner).Error; err == nil { |
| owner.IsActive = company.IsActive |
| tx.Save(&owner) |
| } |
|
|
| if err := tx.Commit().Error; err != nil { |
| tx.Rollback() |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal melakukan toggle status"}) |
| return |
| } |
|
|
| status := "dinonaktifkan" |
| if company.IsActive { |
| status = "diaktifkan" |
| } |
|
|
| c.JSON(http.StatusOK, gin.H{ |
| "message": fmt.Sprintf("Perusahaan %s berhasil %s", company.Name, status), |
| "is_active": company.IsActive, |
| }) |
| } |
|
|
| func DeleteCompany(c *gin.Context) { |
| companyIDStr := c.Param("company_id") |
| companyID, err := strconv.Atoi(companyIDStr) |
| if err != nil { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": "ID perusahaan tidak valid"}) |
| return |
| } |
|
|
| var company models.Company |
| if err := config.DB.First(&company, companyID).Error; err != nil { |
| c.JSON(http.StatusNotFound, gin.H{"detail": "Company not found"}) |
| return |
| } |
|
|
| tx := config.DB.Begin() |
| defer func() { |
| if r := recover(); r != nil { |
| tx.Rollback() |
| } |
| }() |
|
|
| |
| var users []models.User |
| tx.Where("company_id = ?", companyID).Find(&users) |
| for _, u := range users { |
| if u.Phone != "" { |
| tx.Where("phone_number = ?", u.Phone).Delete(&models.OTP{}) |
| } |
| } |
|
|
| |
| if err := tx.Exec("DELETE FROM order_items WHERE order_id IN (SELECT id FROM orders WHERE company_id = ?)", companyID).Error; err != nil { |
| tx.Rollback() |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menghapus data order items"}) |
| return |
| } |
|
|
| |
| if err := tx.Where("company_id = ?", companyID).Delete(&models.Order{}).Error; err != nil { |
| tx.Rollback() |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menghapus data order"}) |
| return |
| } |
|
|
| |
| if err := tx.Exec("DELETE FROM branch_products WHERE product_id IN (SELECT id FROM products WHERE company_id = ?)", companyID).Error; err != nil { |
| tx.Rollback() |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menghapus data branch products"}) |
| return |
| } |
|
|
| |
| if err := tx.Where("company_id = ?", companyID).Delete(&models.Product{}).Error; err != nil { |
| tx.Rollback() |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menghapus data produk"}) |
| return |
| } |
|
|
| |
| if err := tx.Where("company_id = ?", companyID).Delete(&models.Category{}).Error; err != nil { |
| tx.Rollback() |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menghapus data kategori"}) |
| return |
| } |
|
|
| |
| if err := tx.Where("company_id = ?", companyID).Delete(&models.User{}).Error; err != nil { |
| tx.Rollback() |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menghapus data user"}) |
| return |
| } |
|
|
| |
| if err := tx.Where("company_id = ?", companyID).Delete(&models.Branch{}).Error; err != nil { |
| tx.Rollback() |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menghapus data cabang"}) |
| return |
| } |
|
|
| |
| if err := tx.Delete(&company).Error; err != nil { |
| tx.Rollback() |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menghapus data perusahaan"}) |
| return |
| } |
|
|
| tx.Commit() |
|
|
| c.JSON(http.StatusOK, gin.H{ |
| "message": fmt.Sprintf("Perusahaan '%s' dan seluruh data terkait berhasil dihapus", company.Name), |
| }) |
| } |
|
|
| func RejectCompany(c *gin.Context) { |
| companyIDStr := c.Param("company_id") |
| companyID, err := strconv.Atoi(companyIDStr) |
| if err != nil { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": "ID perusahaan tidak valid"}) |
| return |
| } |
|
|
| var company models.Company |
| if err := config.DB.First(&company, companyID).Error; err != nil { |
| c.JSON(http.StatusNotFound, gin.H{"detail": "Company not found"}) |
| return |
| } |
|
|
| if company.IsActive { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": "Tidak bisa menolak perusahaan yang sudah aktif"}) |
| return |
| } |
|
|
| tx := config.DB.Begin() |
| defer func() { |
| if r := recover(); r != nil { |
| tx.Rollback() |
| } |
| }() |
|
|
| |
| var users []models.User |
| tx.Where("company_id = ?", companyID).Find(&users) |
| for _, u := range users { |
| if u.Phone != "" { |
| tx.Where("phone_number = ?", u.Phone).Delete(&models.OTP{}) |
| } |
| } |
|
|
| |
| if err := tx.Exec("DELETE FROM order_items WHERE order_id IN (SELECT id FROM orders WHERE company_id = ?)", companyID).Error; err != nil { |
| tx.Rollback() |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menghapus data order items"}) |
| return |
| } |
|
|
| |
| if err := tx.Where("company_id = ?", companyID).Delete(&models.Order{}).Error; err != nil { |
| tx.Rollback() |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menghapus data order"}) |
| return |
| } |
|
|
| |
| if err := tx.Exec("DELETE FROM branch_products WHERE product_id IN (SELECT id FROM products WHERE company_id = ?)", companyID).Error; err != nil { |
| tx.Rollback() |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menghapus data branch products"}) |
| return |
| } |
|
|
| |
| if err := tx.Where("company_id = ?", companyID).Delete(&models.Product{}).Error; err != nil { |
| tx.Rollback() |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menghapus data produk"}) |
| return |
| } |
|
|
| |
| if err := tx.Where("company_id = ?", companyID).Delete(&models.Category{}).Error; err != nil { |
| tx.Rollback() |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menghapus data kategori"}) |
| return |
| } |
|
|
| |
| if err := tx.Where("company_id = ?", companyID).Delete(&models.User{}).Error; err != nil { |
| tx.Rollback() |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menghapus data user"}) |
| return |
| } |
|
|
| |
| if err := tx.Where("company_id = ?", companyID).Delete(&models.Branch{}).Error; err != nil { |
| tx.Rollback() |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menghapus data cabang"}) |
| return |
| } |
|
|
| |
| if err := tx.Delete(&company).Error; err != nil { |
| tx.Rollback() |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menghapus data perusahaan"}) |
| return |
| } |
|
|
| tx.Commit() |
|
|
| c.JSON(http.StatusOK, gin.H{ |
| "message": fmt.Sprintf("Pendaftaran '%s' ditolak dan dihapus dari sistem", company.Name), |
| }) |
| } |
|
|
| func ApproveCompany(c *gin.Context) { |
| companyIDStr := c.Param("company_id") |
| companyID, err := strconv.Atoi(companyIDStr) |
| if err != nil { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": "ID perusahaan tidak valid"}) |
| return |
| } |
|
|
| tx := config.DB.Begin() |
| defer func() { |
| if r := recover(); r != nil { |
| tx.Rollback() |
| } |
| }() |
|
|
| var company models.Company |
| if err := tx.First(&company, companyID).Error; err != nil { |
| tx.Rollback() |
| c.JSON(http.StatusNotFound, gin.H{"detail": "Company not found"}) |
| return |
| } |
|
|
| var owner models.User |
| if err := tx.Where("company_id = ? AND role = ?", companyID, "owner").First(&owner).Error; err != nil { |
| tx.Rollback() |
| c.JSON(http.StatusBadRequest, gin.H{"detail": "Company ini belum memiliki Owner"}) |
| return |
| } |
|
|
| now := time.Now() |
| company.IsActive = true |
| company.ApprovedAt = &now |
| tx.Save(&company) |
|
|
| owner.IsActive = true |
| tx.Save(&owner) |
|
|
| if err := tx.Commit().Error; err != nil { |
| tx.Rollback() |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menyetujui perusahaan"}) |
| return |
| } |
|
|
| |
| go services.SendApprovalWa(owner.Phone, company.Name, owner.Email) |
|
|
| c.JSON(http.StatusOK, gin.H{ |
| "message": "Perusahaan dan owner berhasil disetujui", |
| }) |
| } |
|
|
| |
|
|
| func ListBranches(c *gin.Context) { |
| userVal, _ := c.Get("user") |
| owner := userVal.(*models.User) |
|
|
| branches := []models.Branch{} |
| if err := config.DB.Select("id, company_id, name, address, phone, is_active, is_point_enabled, created_at").Where("company_id = ?", *owner.CompanyID).Find(&branches).Error; err != nil { |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal mengambil data cabang"}) |
| return |
| } |
|
|
| c.JSON(http.StatusOK, branches) |
| } |
|
|
| func CreateBranch(c *gin.Context) { |
| userVal, _ := c.Get("user") |
| owner := userVal.(*models.User) |
|
|
| var req BranchCreateRequest |
| if err := c.ShouldBindJSON(&req); err != nil { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": err.Error()}) |
| return |
| } |
|
|
| var count int64 |
| config.DB.Model(&models.Branch{}).Where("company_id = ? AND name = ?", *owner.CompanyID, req.Name).Count(&count) |
| if count > 0 { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": "Nama cabang sudah ada"}) |
| return |
| } |
|
|
| var company models.Company |
| if err := config.DB.First(&company, *owner.CompanyID).Error; err != nil { |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal mengambil data perusahaan"}) |
| return |
| } |
|
|
| if !company.EnableMultiBranch && company.MaxBranches <= 0 { |
| var branchCount int64 |
| config.DB.Model(&models.Branch{}).Where("company_id = ?", *owner.CompanyID).Count(&branchCount) |
| if branchCount >= 1 { |
| c.JSON(http.StatusForbidden, gin.H{"detail": "Paket perusahaan Anda tidak mendukung pembuatan lebih dari 1 cabang"}) |
| return |
| } |
| } else if company.MaxBranches > 0 { |
| var branchCount int64 |
| config.DB.Model(&models.Branch{}).Where("company_id = ?", *owner.CompanyID).Count(&branchCount) |
| if branchCount >= int64(company.MaxBranches) { |
| c.JSON(http.StatusForbidden, gin.H{"detail": fmt.Sprintf("Batas maksimal cabang (%d) telah tercapai untuk perusahaan Anda", company.MaxBranches)}) |
| return |
| } |
| } |
|
|
| isPointEnabled := true |
| if req.IsPointEnabled != nil { |
| isPointEnabled = *req.IsPointEnabled |
| } |
|
|
| newBranch := models.Branch{ |
| CompanyID: *owner.CompanyID, |
| Name: req.Name, |
| Address: req.Address, |
| Phone: req.Phone, |
| IsActive: true, |
| IsPointEnabled: isPointEnabled, |
| } |
|
|
| if err := config.DB.Create(&newBranch).Error; err != nil { |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menyimpan cabang baru"}) |
| return |
| } |
|
|
| c.JSON(http.StatusCreated, newBranch) |
| } |
|
|
| func UpdateBranch(c *gin.Context) { |
| userVal, _ := c.Get("user") |
| owner := userVal.(*models.User) |
|
|
| branchIDStr := c.Param("branch_id") |
| branchID, err := strconv.Atoi(branchIDStr) |
| if err != nil { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": "ID cabang tidak valid"}) |
| return |
| } |
|
|
| var req BranchCreateRequest |
| if err := c.ShouldBindJSON(&req); err != nil { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": err.Error()}) |
| return |
| } |
|
|
| var branch models.Branch |
| if err := config.DB.Where("id = ? AND company_id = ?", branchID, *owner.CompanyID).First(&branch).Error; err != nil { |
| c.JSON(http.StatusNotFound, gin.H{"detail": "Cabang tidak ditemukan"}) |
| return |
| } |
|
|
| if req.Name != "" { |
| var count int64 |
| config.DB.Model(&models.Branch{}).Where("company_id = ? AND name = ? AND id != ?", *owner.CompanyID, req.Name, branchID).Count(&count) |
| if count > 0 { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": "Nama cabang sudah ada"}) |
| return |
| } |
| branch.Name = req.Name |
| } |
|
|
| branch.Address = req.Address |
| branch.Phone = req.Phone |
| if req.IsPointEnabled != nil { |
| branch.IsPointEnabled = *req.IsPointEnabled |
| } |
|
|
| if err := config.DB.Save(&branch).Error; err != nil { |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal meng-update cabang"}) |
| return |
| } |
|
|
| c.JSON(http.StatusOK, branch) |
| } |
|
|
| func DeleteBranch(c *gin.Context) { |
| userVal, _ := c.Get("user") |
| owner := userVal.(*models.User) |
|
|
| branchIDStr := c.Param("branch_id") |
| branchID, err := strconv.Atoi(branchIDStr) |
| if err != nil { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": "ID cabang tidak valid"}) |
| return |
| } |
|
|
| var branch models.Branch |
| if err := config.DB.Where("id = ? AND company_id = ?", branchID, *owner.CompanyID).First(&branch).Error; err != nil { |
| c.JSON(http.StatusNotFound, gin.H{"detail": "Cabang tidak ditemukan"}) |
| return |
| } |
|
|
| branch.IsActive = false |
| config.DB.Save(&branch) |
|
|
| c.JSON(http.StatusOK, gin.H{"message": "Cabang dinonaktifkan"}) |
| } |
|
|
| func ToggleBranchStatus(c *gin.Context) { |
| userVal, _ := c.Get("user") |
| owner := userVal.(*models.User) |
|
|
| branchIDStr := c.Param("branch_id") |
| branchID, err := strconv.Atoi(branchIDStr) |
| if err != nil { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": "ID cabang tidak valid"}) |
| return |
| } |
|
|
| var branch models.Branch |
| if err := config.DB.Where("id = ? AND company_id = ?", branchID, *owner.CompanyID).First(&branch).Error; err != nil { |
| c.JSON(http.StatusNotFound, gin.H{"detail": "Cabang tidak ditemukan"}) |
| return |
| } |
|
|
| branch.IsActive = !branch.IsActive |
| config.DB.Save(&branch) |
|
|
| status := "dinonaktifkan" |
| if branch.IsActive { |
| status = "diaktifkan" |
| } |
|
|
| c.JSON(http.StatusOK, gin.H{ |
| "message": fmt.Sprintf("Cabang berhasil %s", status), |
| "is_active": branch.IsActive, |
| }) |
| } |
|
|
| func GetBranchProducts(c *gin.Context) { |
| userVal, _ := c.Get("user") |
| owner := userVal.(*models.User) |
|
|
| branchIDStr := c.Param("branch_id") |
| branchID, err := strconv.Atoi(branchIDStr) |
| if err != nil { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": "ID cabang tidak valid"}) |
| return |
| } |
|
|
| |
| var branch models.Branch |
| if err := config.DB.Where("id = ? AND company_id = ?", branchID, *owner.CompanyID).First(&branch).Error; err != nil { |
| c.JSON(http.StatusNotFound, gin.H{"detail": "Cabang tidak ditemukan"}) |
| return |
| } |
|
|
| |
| var products []models.Product |
| if err := config.DB.Where("company_id = ? AND is_active = ?", *owner.CompanyID, true).Find(&products).Error; err != nil { |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal mengambil data produk"}) |
| return |
| } |
|
|
| |
| var branchProducts []models.BranchProduct |
| config.DB.Where("branch_id = ?", branchID).Find(&branchProducts) |
|
|
| bpMap := make(map[uint]bool) |
| for _, bp := range branchProducts { |
| bpMap[bp.ProductID] = bp.IsAvailable |
| } |
| resp := []BranchProductResponse{} |
| for _, p := range products { |
| isAvailable, exists := bpMap[p.ID] |
| if !exists { |
| isAvailable = true |
| } |
|
|
| resp = append(resp, BranchProductResponse{ |
| ID: p.ID, |
| Name: p.Name, |
| Price: p.Price, |
| CategoryID: p.CategoryID, |
| ImageURL: p.ImageURL, |
| IsAvailable: isAvailable, |
| }) |
| } |
|
|
| c.JSON(http.StatusOK, resp) |
| } |
|
|
| func ToggleProductAvailability(c *gin.Context) { |
| userVal, _ := c.Get("user") |
| owner := userVal.(*models.User) |
|
|
| branchIDStr := c.Param("branch_id") |
| branchID, err := strconv.Atoi(branchIDStr) |
| if err != nil { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": "ID cabang tidak valid"}) |
| return |
| } |
|
|
| productIDStr := c.Param("product_id") |
| productID, err := strconv.Atoi(productIDStr) |
| if err != nil { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": "ID produk tidak valid"}) |
| return |
| } |
|
|
| var branch models.Branch |
| if err := config.DB.Where("id = ? AND company_id = ?", branchID, *owner.CompanyID).First(&branch).Error; err != nil { |
| c.JSON(http.StatusNotFound, gin.H{"detail": "Cabang tidak ditemukan"}) |
| return |
| } |
|
|
| var product models.Product |
| if err := config.DB.Where("id = ? AND company_id = ?", productID, *owner.CompanyID).First(&product).Error; err != nil { |
| c.JSON(http.StatusNotFound, gin.H{"detail": "Produk tidak ditemukan"}) |
| return |
| } |
|
|
| var bp models.BranchProduct |
| err = config.DB.Where("branch_id = ? AND product_id = ?", branchID, productID).First(&bp).Error |
|
|
| if err == nil { |
| bp.IsAvailable = !bp.IsAvailable |
| config.DB.Save(&bp) |
| } else { |
| |
| bp = models.BranchProduct{ |
| BranchID: uint(branchID), |
| ProductID: uint(productID), |
| IsAvailable: false, |
| } |
| config.DB.Create(&bp) |
| } |
|
|
| c.JSON(http.StatusOK, gin.H{ |
| "message": "Status diperbarui", |
| "is_available": bp.IsAvailable, |
| }) |
| } |
|
|
| type UpdateCompanySettingsRequest struct { |
| ChargeFeeBeforeDiscount *bool `json:"charge_fee_before_discount"` |
| EnableMember *bool `json:"enable_member"` |
| EnableVoucher *bool `json:"enable_voucher"` |
| EnableMultiBranch *bool `json:"enable_multi_branch"` |
| MaxBranches *int `json:"max_branches"` |
| EnableMultiKasir *bool `json:"enable_multi_kasir"` |
| MaxCashiers *int `json:"max_cashiers"` |
| EnforceSingleSession *bool `json:"enforce_single_session"` |
| } |
|
|
| func AdminUpdateCompanySettings(c *gin.Context) { |
| companyIDStr := c.Param("id") |
| companyID, err := strconv.Atoi(companyIDStr) |
| if err != nil { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": "ID Perusahaan tidak valid"}) |
| return |
| } |
|
|
| var req UpdateCompanySettingsRequest |
| if err := c.ShouldBindJSON(&req); err != nil { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": err.Error()}) |
| return |
| } |
|
|
| var company models.Company |
| if err := config.DB.First(&company, companyID).Error; err != nil { |
| c.JSON(http.StatusNotFound, gin.H{"detail": "Perusahaan tidak ditemukan"}) |
| return |
| } |
|
|
| if req.ChargeFeeBeforeDiscount != nil { |
| company.ChargeFeeBeforeDiscount = *req.ChargeFeeBeforeDiscount |
| } |
| if req.EnableMember != nil { |
| company.EnableMember = *req.EnableMember |
| } |
| if req.EnableVoucher != nil { |
| company.EnableVoucher = *req.EnableVoucher |
| } |
| if req.EnableMultiBranch != nil { |
| company.EnableMultiBranch = *req.EnableMultiBranch |
| } |
| if req.EnableMultiKasir != nil { |
| company.EnableMultiKasir = *req.EnableMultiKasir |
| } |
| if req.MaxBranches != nil { |
| company.MaxBranches = *req.MaxBranches |
| } |
| if req.MaxCashiers != nil { |
| company.MaxCashiers = *req.MaxCashiers |
| } |
| if req.EnforceSingleSession != nil { |
| company.EnforceSingleSession = *req.EnforceSingleSession |
| |
| if !*req.EnforceSingleSession { |
| var kasirIDs []uint |
| config.DB.Model(&models.User{}).Where("company_id = ? AND role = ?", company.ID, "kasir").Pluck("id", &kasirIDs) |
| if len(kasirIDs) > 0 { |
| config.DB.Where("user_id IN ?", kasirIDs).Delete(&models.UserSession{}) |
| } |
| } |
| } |
|
|
| if err := config.DB.Save(&company).Error; err != nil { |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menyimpan pengaturan"}) |
| return |
| } |
|
|
| c.JSON(http.StatusOK, company) |
| } |
|
|