Mhamdans17 commited on
Commit ·
8879e0b
1
Parent(s): 5cd9ed5
feat: add max branches and cashiers limit, refine admin UI settings, implement tier-based qris fee
Browse files- controllers/auth_controller.go +8 -1
- controllers/company_controller.go +22 -1
- controllers/fee_controller.go +16 -0
- controllers/order_controller.go +25 -3
- models/models.go +3 -0
controllers/auth_controller.go
CHANGED
|
@@ -686,13 +686,20 @@ func CreateKasir(c *gin.Context) {
|
|
| 686 |
return
|
| 687 |
}
|
| 688 |
|
| 689 |
-
if !company.EnableMultiKasir {
|
| 690 |
var kasirCount int64
|
| 691 |
config.DB.Model(&models.User{}).Where("company_id = ? AND role = ?", *owner.CompanyID, "kasir").Count(&kasirCount)
|
| 692 |
if kasirCount >= 1 {
|
| 693 |
c.JSON(http.StatusForbidden, gin.H{"detail": "Paket perusahaan Anda tidak mendukung pembuatan lebih dari 1 kasir"})
|
| 694 |
return
|
| 695 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 696 |
}
|
| 697 |
|
| 698 |
hashedPassword, _ := middleware.HashPassword(req.Password)
|
|
|
|
| 686 |
return
|
| 687 |
}
|
| 688 |
|
| 689 |
+
if !company.EnableMultiKasir && company.MaxCashiers <= 0 {
|
| 690 |
var kasirCount int64
|
| 691 |
config.DB.Model(&models.User{}).Where("company_id = ? AND role = ?", *owner.CompanyID, "kasir").Count(&kasirCount)
|
| 692 |
if kasirCount >= 1 {
|
| 693 |
c.JSON(http.StatusForbidden, gin.H{"detail": "Paket perusahaan Anda tidak mendukung pembuatan lebih dari 1 kasir"})
|
| 694 |
return
|
| 695 |
}
|
| 696 |
+
} else if company.MaxCashiers > 0 {
|
| 697 |
+
var kasirCount int64
|
| 698 |
+
config.DB.Model(&models.User{}).Where("company_id = ? AND role = ?", *owner.CompanyID, "kasir").Count(&kasirCount)
|
| 699 |
+
if kasirCount >= int64(company.MaxCashiers) {
|
| 700 |
+
c.JSON(http.StatusForbidden, gin.H{"detail": fmt.Sprintf("Batas maksimal kasir (%d) telah tercapai untuk perusahaan Anda", company.MaxCashiers)})
|
| 701 |
+
return
|
| 702 |
+
}
|
| 703 |
}
|
| 704 |
|
| 705 |
hashedPassword, _ := middleware.HashPassword(req.Password)
|
controllers/company_controller.go
CHANGED
|
@@ -26,6 +26,7 @@ type CompanyResponse struct {
|
|
| 26 |
IsActive bool `json:"is_active"`
|
| 27 |
FeePercentage float64 `json:"fee_percentage"`
|
| 28 |
CashFeeRules string `json:"cash_fee_rules"`
|
|
|
|
| 29 |
QrisFeePercentage float64 `json:"qris_fee_percentage"`
|
| 30 |
QrisFeeFixed int `json:"qris_fee_fixed"`
|
| 31 |
QrisFeeMin int `json:"qris_fee_min"`
|
|
@@ -37,7 +38,9 @@ type CompanyResponse struct {
|
|
| 37 |
EnableMember bool `json:"enable_member"`
|
| 38 |
EnableVoucher bool `json:"enable_voucher"`
|
| 39 |
EnableMultiBranch bool `json:"enable_multi_branch"`
|
|
|
|
| 40 |
EnableMultiKasir bool `json:"enable_multi_kasir"`
|
|
|
|
| 41 |
ApprovedAt *time.Time `json:"approved_at"`
|
| 42 |
OwnerVerified *bool `json:"owner_verified,omitempty"`
|
| 43 |
OwnerName string `json:"owner_name,omitempty"`
|
|
@@ -207,6 +210,7 @@ func ListCompanies(c *gin.Context) {
|
|
| 207 |
OwnerPhone: ownerPhone,
|
| 208 |
PointRules: comp.PointRules,
|
| 209 |
CashFeeRules: comp.CashFeeRules,
|
|
|
|
| 210 |
QrisFeePercentage: comp.QrisFeePercentage,
|
| 211 |
QrisFeeFixed: comp.QrisFeeFixed,
|
| 212 |
QrisFeeMin: comp.QrisFeeMin,
|
|
@@ -218,7 +222,9 @@ func ListCompanies(c *gin.Context) {
|
|
| 218 |
EnableMember: comp.EnableMember,
|
| 219 |
EnableVoucher: comp.EnableVoucher,
|
| 220 |
EnableMultiBranch: comp.EnableMultiBranch,
|
|
|
|
| 221 |
EnableMultiKasir: comp.EnableMultiKasir,
|
|
|
|
| 222 |
CreatedAt: comp.CreatedAt,
|
| 223 |
})
|
| 224 |
}
|
|
@@ -721,13 +727,20 @@ func CreateBranch(c *gin.Context) {
|
|
| 721 |
return
|
| 722 |
}
|
| 723 |
|
| 724 |
-
if !company.EnableMultiBranch {
|
| 725 |
var branchCount int64
|
| 726 |
config.DB.Model(&models.Branch{}).Where("company_id = ?", *owner.CompanyID).Count(&branchCount)
|
| 727 |
if branchCount >= 1 {
|
| 728 |
c.JSON(http.StatusForbidden, gin.H{"detail": "Paket perusahaan Anda tidak mendukung pembuatan lebih dari 1 cabang"})
|
| 729 |
return
|
| 730 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 731 |
}
|
| 732 |
|
| 733 |
isPointEnabled := true
|
|
@@ -963,7 +976,9 @@ type UpdateCompanySettingsRequest struct {
|
|
| 963 |
EnableMember *bool `json:"enable_member"`
|
| 964 |
EnableVoucher *bool `json:"enable_voucher"`
|
| 965 |
EnableMultiBranch *bool `json:"enable_multi_branch"`
|
|
|
|
| 966 |
EnableMultiKasir *bool `json:"enable_multi_kasir"`
|
|
|
|
| 967 |
}
|
| 968 |
|
| 969 |
func AdminUpdateCompanySettings(c *gin.Context) {
|
|
@@ -1001,6 +1016,12 @@ func AdminUpdateCompanySettings(c *gin.Context) {
|
|
| 1001 |
if req.EnableMultiKasir != nil {
|
| 1002 |
company.EnableMultiKasir = *req.EnableMultiKasir
|
| 1003 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1004 |
|
| 1005 |
if err := config.DB.Save(&company).Error; err != nil {
|
| 1006 |
c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menyimpan pengaturan"})
|
|
|
|
| 26 |
IsActive bool `json:"is_active"`
|
| 27 |
FeePercentage float64 `json:"fee_percentage"`
|
| 28 |
CashFeeRules string `json:"cash_fee_rules"`
|
| 29 |
+
QrisFeeRules string `json:"qris_fee_rules"`
|
| 30 |
QrisFeePercentage float64 `json:"qris_fee_percentage"`
|
| 31 |
QrisFeeFixed int `json:"qris_fee_fixed"`
|
| 32 |
QrisFeeMin int `json:"qris_fee_min"`
|
|
|
|
| 38 |
EnableMember bool `json:"enable_member"`
|
| 39 |
EnableVoucher bool `json:"enable_voucher"`
|
| 40 |
EnableMultiBranch bool `json:"enable_multi_branch"`
|
| 41 |
+
MaxBranches int `json:"max_branches"`
|
| 42 |
EnableMultiKasir bool `json:"enable_multi_kasir"`
|
| 43 |
+
MaxCashiers int `json:"max_cashiers"`
|
| 44 |
ApprovedAt *time.Time `json:"approved_at"`
|
| 45 |
OwnerVerified *bool `json:"owner_verified,omitempty"`
|
| 46 |
OwnerName string `json:"owner_name,omitempty"`
|
|
|
|
| 210 |
OwnerPhone: ownerPhone,
|
| 211 |
PointRules: comp.PointRules,
|
| 212 |
CashFeeRules: comp.CashFeeRules,
|
| 213 |
+
QrisFeeRules: comp.QrisFeeRules,
|
| 214 |
QrisFeePercentage: comp.QrisFeePercentage,
|
| 215 |
QrisFeeFixed: comp.QrisFeeFixed,
|
| 216 |
QrisFeeMin: comp.QrisFeeMin,
|
|
|
|
| 222 |
EnableMember: comp.EnableMember,
|
| 223 |
EnableVoucher: comp.EnableVoucher,
|
| 224 |
EnableMultiBranch: comp.EnableMultiBranch,
|
| 225 |
+
MaxBranches: comp.MaxBranches,
|
| 226 |
EnableMultiKasir: comp.EnableMultiKasir,
|
| 227 |
+
MaxCashiers: comp.MaxCashiers,
|
| 228 |
CreatedAt: comp.CreatedAt,
|
| 229 |
})
|
| 230 |
}
|
|
|
|
| 727 |
return
|
| 728 |
}
|
| 729 |
|
| 730 |
+
if !company.EnableMultiBranch && company.MaxBranches <= 0 {
|
| 731 |
var branchCount int64
|
| 732 |
config.DB.Model(&models.Branch{}).Where("company_id = ?", *owner.CompanyID).Count(&branchCount)
|
| 733 |
if branchCount >= 1 {
|
| 734 |
c.JSON(http.StatusForbidden, gin.H{"detail": "Paket perusahaan Anda tidak mendukung pembuatan lebih dari 1 cabang"})
|
| 735 |
return
|
| 736 |
}
|
| 737 |
+
} else if company.MaxBranches > 0 {
|
| 738 |
+
var branchCount int64
|
| 739 |
+
config.DB.Model(&models.Branch{}).Where("company_id = ?", *owner.CompanyID).Count(&branchCount)
|
| 740 |
+
if branchCount >= int64(company.MaxBranches) {
|
| 741 |
+
c.JSON(http.StatusForbidden, gin.H{"detail": fmt.Sprintf("Batas maksimal cabang (%d) telah tercapai untuk perusahaan Anda", company.MaxBranches)})
|
| 742 |
+
return
|
| 743 |
+
}
|
| 744 |
}
|
| 745 |
|
| 746 |
isPointEnabled := true
|
|
|
|
| 976 |
EnableMember *bool `json:"enable_member"`
|
| 977 |
EnableVoucher *bool `json:"enable_voucher"`
|
| 978 |
EnableMultiBranch *bool `json:"enable_multi_branch"`
|
| 979 |
+
MaxBranches *int `json:"max_branches"`
|
| 980 |
EnableMultiKasir *bool `json:"enable_multi_kasir"`
|
| 981 |
+
MaxCashiers *int `json:"max_cashiers"`
|
| 982 |
}
|
| 983 |
|
| 984 |
func AdminUpdateCompanySettings(c *gin.Context) {
|
|
|
|
| 1016 |
if req.EnableMultiKasir != nil {
|
| 1017 |
company.EnableMultiKasir = *req.EnableMultiKasir
|
| 1018 |
}
|
| 1019 |
+
if req.MaxBranches != nil {
|
| 1020 |
+
company.MaxBranches = *req.MaxBranches
|
| 1021 |
+
}
|
| 1022 |
+
if req.MaxCashiers != nil {
|
| 1023 |
+
company.MaxCashiers = *req.MaxCashiers
|
| 1024 |
+
}
|
| 1025 |
|
| 1026 |
if err := config.DB.Save(&company).Error; err != nil {
|
| 1027 |
c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menyimpan pengaturan"})
|
controllers/fee_controller.go
CHANGED
|
@@ -212,8 +212,17 @@ type CashFeeRule struct {
|
|
| 212 |
Fee int `json:"fee"`
|
| 213 |
}
|
| 214 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 215 |
type UpdateFeeRequest struct {
|
| 216 |
CashFeeRules []CashFeeRule `json:"cash_fee_rules"`
|
|
|
|
| 217 |
QrisFeePercentage float64 `json:"qris_fee_percentage"`
|
| 218 |
QrisFeeFixed int `json:"qris_fee_fixed"`
|
| 219 |
QrisFeeMin int `json:"qris_fee_min"`
|
|
@@ -252,7 +261,14 @@ func SetCompanyFee(c *gin.Context) {
|
|
| 252 |
return
|
| 253 |
}
|
| 254 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 255 |
company.CashFeeRules = string(rulesBytes)
|
|
|
|
| 256 |
company.QrisFeePercentage = req.QrisFeePercentage
|
| 257 |
company.QrisFeeFixed = req.QrisFeeFixed
|
| 258 |
company.QrisFeeMin = req.QrisFeeMin
|
|
|
|
| 212 |
Fee int `json:"fee"`
|
| 213 |
}
|
| 214 |
|
| 215 |
+
type QrisFeeRule struct {
|
| 216 |
+
MinAmount int `json:"min_amount"`
|
| 217 |
+
MaxAmount int `json:"max_amount"`
|
| 218 |
+
FeePercentage float64 `json:"fee_percentage"`
|
| 219 |
+
FeeFixed int `json:"fee_fixed"`
|
| 220 |
+
FeeMin int `json:"fee_min"`
|
| 221 |
+
}
|
| 222 |
+
|
| 223 |
type UpdateFeeRequest struct {
|
| 224 |
CashFeeRules []CashFeeRule `json:"cash_fee_rules"`
|
| 225 |
+
QrisFeeRules []QrisFeeRule `json:"qris_fee_rules"`
|
| 226 |
QrisFeePercentage float64 `json:"qris_fee_percentage"`
|
| 227 |
QrisFeeFixed int `json:"qris_fee_fixed"`
|
| 228 |
QrisFeeMin int `json:"qris_fee_min"`
|
|
|
|
| 261 |
return
|
| 262 |
}
|
| 263 |
|
| 264 |
+
qrisRulesBytes, err := json.Marshal(req.QrisFeeRules)
|
| 265 |
+
if err != nil {
|
| 266 |
+
c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal memproses aturan fee qris"})
|
| 267 |
+
return
|
| 268 |
+
}
|
| 269 |
+
|
| 270 |
company.CashFeeRules = string(rulesBytes)
|
| 271 |
+
company.QrisFeeRules = string(qrisRulesBytes)
|
| 272 |
company.QrisFeePercentage = req.QrisFeePercentage
|
| 273 |
company.QrisFeeFixed = req.QrisFeeFixed
|
| 274 |
company.QrisFeeMin = req.QrisFeeMin
|
controllers/order_controller.go
CHANGED
|
@@ -808,10 +808,32 @@ func TripayWebhook(c *gin.Context) {
|
|
| 808 |
baseAmountForFee = order.SubtotalAmount
|
| 809 |
}
|
| 810 |
|
| 811 |
-
fee :=
|
| 812 |
-
|
| 813 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 814 |
}
|
|
|
|
| 815 |
order.PlatformFee = fee
|
| 816 |
awardPoints(tx, &order, &company)
|
| 817 |
}
|
|
|
|
| 808 |
baseAmountForFee = order.SubtotalAmount
|
| 809 |
}
|
| 810 |
|
| 811 |
+
fee := 0
|
| 812 |
+
ruleApplied := false
|
| 813 |
+
|
| 814 |
+
if company.QrisFeeRules != "" && company.QrisFeeRules != "[]" {
|
| 815 |
+
var qrisRules []QrisFeeRule
|
| 816 |
+
if err := json.Unmarshal([]byte(company.QrisFeeRules), &qrisRules); err == nil {
|
| 817 |
+
for _, rule := range qrisRules {
|
| 818 |
+
if baseAmountForFee >= rule.MinAmount && baseAmountForFee <= rule.MaxAmount {
|
| 819 |
+
fee = int((float64(baseAmountForFee) * rule.FeePercentage / 100) + float64(rule.FeeFixed))
|
| 820 |
+
if fee < rule.FeeMin {
|
| 821 |
+
fee = rule.FeeMin
|
| 822 |
+
}
|
| 823 |
+
ruleApplied = true
|
| 824 |
+
break
|
| 825 |
+
}
|
| 826 |
+
}
|
| 827 |
+
}
|
| 828 |
+
}
|
| 829 |
+
|
| 830 |
+
if !ruleApplied {
|
| 831 |
+
fee = int((float64(baseAmountForFee) * company.QrisFeePercentage / 100) + float64(company.QrisFeeFixed))
|
| 832 |
+
if fee < company.QrisFeeMin {
|
| 833 |
+
fee = company.QrisFeeMin
|
| 834 |
+
}
|
| 835 |
}
|
| 836 |
+
|
| 837 |
order.PlatformFee = fee
|
| 838 |
awardPoints(tx, &order, &company)
|
| 839 |
}
|
models/models.go
CHANGED
|
@@ -22,6 +22,7 @@ type Company struct {
|
|
| 22 |
TripayPrivateKey string `gorm:"type:text" json:"tripay_private_key"`
|
| 23 |
TripayProxyURL string `gorm:"type:text" json:"tripay_proxy_url"`
|
| 24 |
CashFeeRules string `gorm:"type:text;default:'[]'" json:"cash_fee_rules"`
|
|
|
|
| 25 |
QrisFeePercentage float64 `gorm:"type:float;default:1.7" json:"qris_fee_percentage"`
|
| 26 |
QrisFeeFixed int `gorm:"default:500" json:"qris_fee_fixed"`
|
| 27 |
QrisFeeMin int `gorm:"default:850" json:"qris_fee_min"`
|
|
@@ -33,7 +34,9 @@ type Company struct {
|
|
| 33 |
EnableMember bool `gorm:"default:true" json:"enable_member"`
|
| 34 |
EnableVoucher bool `gorm:"default:true" json:"enable_voucher"`
|
| 35 |
EnableMultiBranch bool `gorm:"default:false" json:"enable_multi_branch"`
|
|
|
|
| 36 |
EnableMultiKasir bool `gorm:"default:false" json:"enable_multi_kasir"`
|
|
|
|
| 37 |
PointRules []PointRule `gorm:"foreignKey:CompanyID;constraint:OnDelete:CASCADE" json:"point_rules"`
|
| 38 |
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
| 39 |
Branches []Branch `gorm:"foreignKey:CompanyID;constraint:OnDelete:CASCADE" json:"-"`
|
|
|
|
| 22 |
TripayPrivateKey string `gorm:"type:text" json:"tripay_private_key"`
|
| 23 |
TripayProxyURL string `gorm:"type:text" json:"tripay_proxy_url"`
|
| 24 |
CashFeeRules string `gorm:"type:text;default:'[]'" json:"cash_fee_rules"`
|
| 25 |
+
QrisFeeRules string `gorm:"type:text;default:'[]'" json:"qris_fee_rules"`
|
| 26 |
QrisFeePercentage float64 `gorm:"type:float;default:1.7" json:"qris_fee_percentage"`
|
| 27 |
QrisFeeFixed int `gorm:"default:500" json:"qris_fee_fixed"`
|
| 28 |
QrisFeeMin int `gorm:"default:850" json:"qris_fee_min"`
|
|
|
|
| 34 |
EnableMember bool `gorm:"default:true" json:"enable_member"`
|
| 35 |
EnableVoucher bool `gorm:"default:true" json:"enable_voucher"`
|
| 36 |
EnableMultiBranch bool `gorm:"default:false" json:"enable_multi_branch"`
|
| 37 |
+
MaxBranches int `gorm:"default:0" json:"max_branches"`
|
| 38 |
EnableMultiKasir bool `gorm:"default:false" json:"enable_multi_kasir"`
|
| 39 |
+
MaxCashiers int `gorm:"default:0" json:"max_cashiers"`
|
| 40 |
PointRules []PointRule `gorm:"foreignKey:CompanyID;constraint:OnDelete:CASCADE" json:"point_rules"`
|
| 41 |
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
| 42 |
Branches []Branch `gorm:"foreignKey:CompanyID;constraint:OnDelete:CASCADE" json:"-"`
|