Mhamdans17
Update WhatsApp notification format, add voucher to order reports, and add tenant settings toggle
7121ebc | package controllers | |
| import ( | |
| "fmt" | |
| "net/http" | |
| "strconv" | |
| "strings" | |
| "time" | |
| "service-warungpos-go/config" | |
| "service-warungpos-go/models" | |
| "github.com/gin-gonic/gin" | |
| ) | |
| type TopProductRow struct { | |
| ProductName string `json:"product_name"` | |
| TotalQuantity int `json:"total_quantity"` | |
| TotalRevenue int `json:"total_revenue"` | |
| } | |
| type SummaryTopProduct struct { | |
| Name string `json:"name"` | |
| TotalSold int `json:"total_sold"` | |
| TotalRevenue int `json:"total_revenue"` | |
| } | |
| type ReportOrderRow struct { | |
| OrderNumber string `json:"order_number"` | |
| TotalAmount int `json:"total_amount"` | |
| SubtotalAmount int `json:"subtotal_amount"` | |
| PaymentMethod string `json:"payment_method"` | |
| CreatedAt time.Time `json:"created_at"` | |
| Items string `json:"items"` | |
| } | |
| func getDayRange(targetDate time.Time) (time.Time, time.Time) { | |
| // Buat range awal dan akhir hari di timezone lokal/WIB (Asia/Jakarta) | |
| loc, _ := time.LoadLocation("Asia/Jakarta") | |
| if loc == nil { | |
| loc = time.Local | |
| } | |
| start := time.Date(targetDate.Year(), targetDate.Month(), targetDate.Day(), 0, 0, 0, 0, loc) | |
| end := start.Add(24 * time.Hour) | |
| return start, end | |
| } | |
| func fmtRupiah(amount int) string { | |
| // Helper formatting rupiah untuk internal log atau respons | |
| str := fmt.Sprintf("%d", amount) | |
| var result []string | |
| length := len(str) | |
| for i := length; i > 0; i -= 3 { | |
| start := i - 3 | |
| if start < 0 { | |
| start = 0 | |
| } | |
| result = append([]string{str[start:i]}, result...) | |
| } | |
| return "Rp " + strings.Join(result, ".") | |
| } | |
| // Helper strings.Join untuk formatting di atas | |
| func DailyReport(c *gin.Context) { | |
| userVal, _ := c.Get("user") | |
| user := userVal.(*models.User) | |
| dateQuery := c.Query("date") // YYYY-MM-DD | |
| branchQuery := c.Query("branch_id") | |
| var targetDate time.Time | |
| var err error | |
| if dateQuery != "" { | |
| targetDate, err = time.Parse("2006-01-02", dateQuery) | |
| if err != nil { | |
| c.JSON(http.StatusBadRequest, gin.H{"detail": "Format tanggal salah, gunakan YYYY-MM-DD"}) | |
| return | |
| } | |
| } else { | |
| // Default: Hari ini | |
| targetDate = time.Now() | |
| } | |
| startDt, endDt := getDayRange(targetDate) | |
| query := config.DB.Preload("Items.Product"). | |
| Where("company_id = ? AND created_at >= ? AND created_at < ? AND payment_status = ?", *user.CompanyID, startDt, endDt, "paid") | |
| if branchQuery != "" { | |
| if bID, err := strconv.Atoi(branchQuery); err == nil { | |
| query = query.Where("branch_id = ?", bID) | |
| } | |
| } | |
| var orders []models.Order | |
| if err := query.Find(&orders).Error; err != nil { | |
| c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal mengambil data laporan harian"}) | |
| return | |
| } | |
| totalRevenue := 0 | |
| totalCash := 0 | |
| totalMidtrans := 0 | |
| totalPlatformFee := 0 | |
| var orderRows []ReportOrderRow | |
| for _, o := range orders { | |
| totalRevenue += o.TotalAmount | |
| if o.PaymentMethod == "cash" { | |
| totalCash += o.TotalAmount | |
| } else if o.PaymentMethod == "midtrans" || o.PaymentMethod == "tripay" { | |
| totalMidtrans += o.TotalAmount | |
| } | |
| totalPlatformFee += o.PlatformFee | |
| itemNames := []string{} | |
| for _, item := range o.Items { | |
| name := "Item" | |
| if item.Product != nil { | |
| name = item.Product.Name | |
| } | |
| itemNames = append(itemNames, fmt.Sprintf("%s (x%d)", name, item.Quantity)) | |
| } | |
| itemsStr := strings.Join(itemNames, ", ") | |
| orderRows = append(orderRows, ReportOrderRow{ | |
| OrderNumber: o.OrderNumber, | |
| TotalAmount: o.TotalAmount, | |
| SubtotalAmount: o.SubtotalAmount, | |
| PaymentMethod: o.PaymentMethod, | |
| CreatedAt: o.CreatedAt, | |
| Items: itemsStr, | |
| }) | |
| } | |
| // Query Top 5 produk terlaris hari ini | |
| var topProducts []TopProductRow | |
| topQuery := config.DB.Table("products"). | |
| Select("products.name as product_name, sum(order_items.quantity) as total_quantity, sum(order_items.subtotal) as total_revenue"). | |
| Joins("join order_items on products.id = order_items.product_id"). | |
| Joins("join orders on order_items.order_id = orders.id"). | |
| Where("orders.company_id = ? AND orders.payment_status = ? AND orders.created_at >= ? AND orders.created_at < ?", *user.CompanyID, "paid", startDt, endDt) | |
| if branchQuery != "" { | |
| if bID, err := strconv.Atoi(branchQuery); err == nil { | |
| topQuery = topQuery.Where("orders.branch_id = ?", bID) | |
| } | |
| } | |
| topQuery.Group("products.name"). | |
| Order("total_quantity desc"). | |
| Limit(5). | |
| Scan(&topProducts) | |
| c.JSON(http.StatusOK, gin.H{ | |
| "date": targetDate.Format("2006-01-02"), | |
| "total_transactions": len(orders), | |
| "total_revenue": totalRevenue, | |
| "total_cash": totalCash, | |
| "total_midtrans": totalMidtrans, | |
| "total_platform_fee": totalPlatformFee, | |
| "top_products": topProducts, | |
| "orders": orderRows, | |
| }) | |
| } | |
| func SummaryReport(c *gin.Context) { | |
| userVal, _ := c.Get("user") | |
| user := userVal.(*models.User) | |
| branchQuery := c.Query("branch_id") | |
| // 1. Total Semua Transaksi Sukses | |
| totalOrders := int64(0) | |
| totalRevenue := int64(0) | |
| totalQuery := config.DB.Model(&models.Order{}). | |
| Where("company_id = ? AND payment_status = ?", *user.CompanyID, "paid") | |
| if branchQuery != "" { | |
| if bID, err := strconv.Atoi(branchQuery); err == nil { | |
| totalQuery = totalQuery.Where("branch_id = ?", bID) | |
| } | |
| } | |
| totalQuery.Count(&totalOrders) | |
| totalQuery.Select("coalesce(sum(total_amount), 0)").Row().Scan(&totalRevenue) | |
| // 2. Total Produk Aktif yang Tersedia | |
| var totalProducts int64 | |
| prodQuery := config.DB.Model(&models.Product{}). | |
| Where("company_id = ? AND is_active = ?", *user.CompanyID, true) | |
| if branchQuery != "" { | |
| if bID, err := strconv.Atoi(branchQuery); err == nil { | |
| // Subquery pengecualian produk tidak aktif di cabang ini | |
| subQuery := config.DB.Model(&models.BranchProduct{}). | |
| Select("product_id"). | |
| Where("branch_id = ? AND is_available = ?", bID, false) | |
| prodQuery = prodQuery.Where("id NOT IN (?)", subQuery) | |
| } | |
| } | |
| prodQuery.Count(&totalProducts) | |
| // 3. Top 5 Produk Terlaris All-Time | |
| var topProducts []SummaryTopProduct | |
| topQuery := config.DB.Table("products"). | |
| Select("products.name as name, sum(order_items.quantity) as total_sold, sum(order_items.subtotal) as total_revenue"). | |
| Joins("join order_items on products.id = order_items.product_id"). | |
| Joins("join orders on order_items.order_id = orders.id"). | |
| Where("orders.company_id = ? AND orders.payment_status = ?", *user.CompanyID, "paid") | |
| if branchQuery != "" { | |
| if bID, err := strconv.Atoi(branchQuery); err == nil { | |
| topQuery = topQuery.Where("orders.branch_id = ?", bID) | |
| } | |
| } | |
| topQuery.Group("products.name"). | |
| Order("total_sold desc"). | |
| Limit(5). | |
| Scan(&topProducts) | |
| // 4. Laporan Hari Ini | |
| todayOrders := int64(0) | |
| todayRevenue := int64(0) | |
| startToday, endToday := getDayRange(time.Now()) | |
| todayQuery := config.DB.Model(&models.Order{}). | |
| Where("company_id = ? AND payment_status = ? AND created_at >= ? AND created_at < ?", *user.CompanyID, "paid", startToday, endToday) | |
| if branchQuery != "" { | |
| if bID, err := strconv.Atoi(branchQuery); err == nil { | |
| todayQuery = todayQuery.Where("branch_id = ?", bID) | |
| } | |
| } | |
| todayQuery.Count(&todayOrders) | |
| todayQuery.Select("coalesce(sum(total_amount), 0)").Row().Scan(&todayRevenue) | |
| // 5. Laporan QRIS | |
| var qrisRevenue int64 | |
| var qrisFee int64 | |
| qrisQuery := config.DB.Model(&models.Order{}). | |
| Where("company_id = ? AND payment_status = ? AND payment_method != 'cash'", *user.CompanyID, "paid") | |
| if branchQuery != "" { | |
| if bID, err := strconv.Atoi(branchQuery); err == nil { | |
| qrisQuery = qrisQuery.Where("branch_id = ?", bID) | |
| } | |
| } | |
| qrisQuery.Select("coalesce(sum(total_amount), 0), coalesce(sum(platform_fee), 0)").Row().Scan(&qrisRevenue, &qrisFee) | |
| c.JSON(http.StatusOK, gin.H{ | |
| "all_time": gin.H{ | |
| "total_orders": totalOrders, | |
| "total_revenue": totalRevenue, | |
| }, | |
| "today": gin.H{ | |
| "total_orders": todayOrders, | |
| "total_revenue": todayRevenue, | |
| }, | |
| "qris_report": gin.H{ | |
| "total_transactions": qrisRevenue, | |
| "system_fee": qrisFee, | |
| "net_amount": qrisRevenue - qrisFee, | |
| }, | |
| "total_products": totalProducts, | |
| "top_products": topProducts, | |
| }) | |
| } | |