| package controllers |
|
|
| import ( |
| "encoding/json" |
| "fmt" |
| "net/http" |
| "strconv" |
| "time" |
|
|
| "service-warungpos-go/config" |
| "service-warungpos-go/models" |
|
|
| "github.com/gin-gonic/gin" |
| "gorm.io/gorm" |
| ) |
|
|
| type FeeSummaryData struct { |
| TotalTransactions int64 `json:"total_transactions"` |
| TotalRevenue int `json:"total_revenue"` |
| TotalFee int `json:"total_fee"` |
| TotalCash int `json:"total_cash"` |
| TotalMidtrans int `json:"total_midtrans"` |
| FeeCash int `json:"fee_cash"` |
| FeeMidtrans int `json:"fee_midtrans"` |
| } |
|
|
| type CompanyFeeRow struct { |
| CompanyID uint `json:"company_id"` |
| CompanyName string `json:"company_name"` |
| FeePercentage float64 `json:"fee_percentage"` |
| TotalTransactions int64 `json:"total_transactions"` |
| TotalRevenue int `json:"total_revenue"` |
| TotalFee int `json:"total_fee"` |
| TotalCash int `json:"total_cash"` |
| TotalMidtrans int `json:"total_midtrans"` |
| } |
|
|
| type BranchFeeRow struct { |
| CompanyID uint `json:"company_id"` |
| CompanyName string `json:"company_name"` |
| BranchID *uint `json:"branch_id"` |
| BranchName string `json:"branch_name"` |
| TotalTransactions int64 `json:"total_transactions"` |
| TotalRevenue int `json:"total_revenue"` |
| TotalFee int `json:"total_fee"` |
| TotalCash int `json:"total_cash"` |
| TotalMidtrans int `json:"total_midtrans"` |
| } |
|
|
| type DailyFeeRow struct { |
| Day time.Time `json:"day"` |
| CompanyID uint `json:"company_id"` |
| CompanyName string `json:"company_name"` |
| FeePercentage float64 `json:"fee_percentage"` |
| TotalTransactions int64 `json:"total_transactions"` |
| TotalRevenue int `json:"total_revenue"` |
| TotalFee int `json:"total_fee"` |
| } |
|
|
| func GetFeeReport(c *gin.Context) { |
| dateFrom := c.Query("date_from") |
| dateTo := c.Query("date_to") |
| companyIDStr := c.Query("company_id") |
|
|
| today := time.Now() |
| var dFrom, dTo time.Time |
| var err error |
|
|
| if dateFrom != "" { |
| dFrom, err = time.Parse("2006-01-02", dateFrom) |
| if err != nil { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": "Format date_from salah, gunakan YYYY-MM-DD"}) |
| return |
| } |
| } else { |
| |
| dFrom = today.AddDate(0, 0, -29) |
| } |
|
|
| if dateTo != "" { |
| dTo, err = time.Parse("2006-01-02", dateTo) |
| if err != nil { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": "Format date_to salah, gunakan YYYY-MM-DD"}) |
| return |
| } |
| } else { |
| dTo = today |
| } |
|
|
| loc, _ := time.LoadLocation("Asia/Jakarta") |
| if loc == nil { |
| loc = time.Local |
| } |
| startDt := time.Date(dFrom.Year(), dFrom.Month(), dFrom.Day(), 0, 0, 0, 0, loc) |
| endDt := time.Date(dTo.Year(), dTo.Month(), dTo.Day(), 23, 59, 59, 0, loc) |
|
|
| |
| getFreshFeeQuery := func() *gorm.DB { |
| q := config.DB.Table("orders"). |
| Where("orders.payment_status = ? AND orders.created_at >= ? AND orders.created_at <= ?", "paid", startDt, endDt) |
| if companyIDStr != "" { |
| if cID, err := strconv.Atoi(companyIDStr); err == nil { |
| q = q.Where("orders.company_id = ?", cID) |
| } |
| } |
| return q |
| } |
|
|
| |
| var summary FeeSummaryData |
| getFreshFeeQuery().Select(` |
| count(orders.id) as total_transactions, |
| coalesce(sum(orders.total_amount), 0) as total_revenue, |
| coalesce(sum(orders.platform_fee), 0) as total_fee, |
| coalesce(sum(case when orders.payment_method = 'cash' then orders.total_amount else 0 end), 0) as total_cash, |
| coalesce(sum(case when orders.payment_method != 'cash' then orders.total_amount else 0 end), 0) as total_midtrans, |
| coalesce(sum(case when orders.payment_method = 'cash' then orders.platform_fee else 0 end), 0) as fee_cash, |
| coalesce(sum(case when orders.payment_method != 'cash' then orders.platform_fee else 0 end), 0) as fee_midtrans |
| `).Scan(&summary) |
|
|
| |
| var perCompany []CompanyFeeRow |
| getFreshFeeQuery().Select(` |
| orders.company_id, |
| companies.name as company_name, |
| companies.fee_percentage, |
| count(orders.id) as total_transactions, |
| coalesce(sum(orders.total_amount), 0) as total_revenue, |
| coalesce(sum(orders.platform_fee), 0) as total_fee, |
| coalesce(sum(case when orders.payment_method = 'cash' then orders.total_amount else 0 end), 0) as total_cash, |
| coalesce(sum(case when orders.payment_method != 'cash' then orders.total_amount else 0 end), 0) as total_midtrans |
| `). |
| Joins("join companies on companies.id = orders.company_id"). |
| Group("orders.company_id, companies.name, companies.fee_percentage"). |
| Order("total_fee desc"). |
| Scan(&perCompany) |
|
|
| |
| var perBranch []BranchFeeRow |
| getFreshFeeQuery().Select(` |
| orders.company_id, |
| companies.name as company_name, |
| orders.branch_id, |
| coalesce(branches.name, 'Tanpa Cabang') as branch_name, |
| count(orders.id) as total_transactions, |
| coalesce(sum(orders.total_amount), 0) as total_revenue, |
| coalesce(sum(orders.platform_fee), 0) as total_fee, |
| coalesce(sum(case when orders.payment_method = 'cash' then orders.total_amount else 0 end), 0) as total_cash, |
| coalesce(sum(case when orders.payment_method != 'cash' then orders.total_amount else 0 end), 0) as total_midtrans |
| `). |
| Joins("join companies on companies.id = orders.company_id"). |
| Joins("left join branches on branches.id = orders.branch_id"). |
| Group("orders.company_id, companies.name, orders.branch_id, branches.name"). |
| Order("orders.company_id, total_revenue desc"). |
| Scan(&perBranch) |
|
|
| |
| var dailyBreakdown []DailyFeeRow |
| getFreshFeeQuery().Select(` |
| DATE(orders.created_at) as day, |
| orders.company_id, |
| companies.name as company_name, |
| companies.fee_percentage, |
| count(orders.id) as total_transactions, |
| coalesce(sum(orders.total_amount), 0) as total_revenue, |
| coalesce(sum(orders.platform_fee), 0) as total_fee |
| `). |
| Joins("join companies on companies.id = orders.company_id"). |
| Group("DATE(orders.created_at), orders.company_id, companies.name, companies.fee_percentage"). |
| Order("day desc"). |
| Scan(&dailyBreakdown) |
|
|
| |
| type DailyBreakdownResp struct { |
| Date string `json:"date"` |
| CompanyID uint `json:"company_id"` |
| CompanyName string `json:"company_name"` |
| FeePercentage float64 `json:"fee_percentage"` |
| TotalTransactions int64 `json:"total_transactions"` |
| TotalRevenue int `json:"total_revenue"` |
| TotalFee int `json:"total_fee"` |
| } |
|
|
| var dailyResp []DailyBreakdownResp |
| for _, row := range dailyBreakdown { |
| dailyResp = append(dailyResp, DailyBreakdownResp{ |
| Date: row.Day.Format("2006-01-02"), |
| CompanyID: row.CompanyID, |
| CompanyName: row.CompanyName, |
| FeePercentage: row.FeePercentage, |
| TotalTransactions: row.TotalTransactions, |
| TotalRevenue: row.TotalRevenue, |
| TotalFee: row.TotalFee, |
| }) |
| } |
|
|
| c.JSON(http.StatusOK, gin.H{ |
| "period": gin.H{ |
| "date_from": dFrom.Format("2006-01-02"), |
| "date_to": dTo.Format("2006-01-02"), |
| }, |
| "summary": summary, |
| "per_company": perCompany, |
| "per_branch": perBranch, |
| "daily_breakdown": dailyResp, |
| }) |
| } |
|
|
| type CashFeeRule struct { |
| MinAmount int `json:"min_amount"` |
| MaxAmount int `json:"max_amount"` |
| Fee int `json:"fee"` |
| } |
|
|
| type UpdateFeeRequest struct { |
| CashFeeRules []CashFeeRule `json:"cash_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"` |
| } |
|
|
| func SetCompanyFee(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 UpdateFeeRequest |
| if err := c.ShouldBindJSON(&req); err != nil { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": "Data konfigurasi fee tidak valid"}) |
| return |
| } |
|
|
| if req.QrisFeePercentage < 0 || req.QrisFeePercentage > 100 { |
| c.JSON(http.StatusBadRequest, gin.H{"detail": "Fee percentage harus berupa angka antara 0 dan 100"}) |
| 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 |
| } |
|
|
| rulesBytes, err := json.Marshal(req.CashFeeRules) |
| if err != nil { |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal memproses aturan fee tunai"}) |
| return |
| } |
|
|
| company.CashFeeRules = string(rulesBytes) |
| company.QrisFeePercentage = req.QrisFeePercentage |
| company.QrisFeeFixed = req.QrisFeeFixed |
| company.QrisFeeMin = req.QrisFeeMin |
| company.QrisMinTransaction = req.QrisMinTransaction |
| company.AllowQrisBelowMin = req.AllowQrisBelowMin |
|
|
| |
| company.FeePercentage = req.QrisFeePercentage |
|
|
| if err := config.DB.Save(&company).Error; err != nil { |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menyimpan konfigurasi fee perusahaan"}) |
| return |
| } |
|
|
| c.JSON(http.StatusOK, gin.H{ |
| "message": fmt.Sprintf("Konfigurasi fee perusahaan '%s' berhasil diperbarui", company.Name), |
| "company_id": company.ID, |
| "company_name": company.Name, |
| }) |
| } |
|
|