Dev / controllers /report_controller.go
Mhamdans17
Update WhatsApp notification format, add voucher to order reports, and add tenant settings toggle
7121ebc
Raw
History Blame Contribute Delete
8.02 kB
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,
})
}