Mhamdans17 commited on
Commit
ae76db4
·
1 Parent(s): 0f64726

feat: add Member service, points, and Kasir Name tracking

Browse files
controllers/member_controller.go ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package controllers
2
+
3
+ import (
4
+ "net/http"
5
+ "service-warungpos-go/config"
6
+ "service-warungpos-go/models"
7
+
8
+ "github.com/gin-gonic/gin"
9
+ )
10
+
11
+ // ListMembers (GET /api/members)
12
+ func ListMembers(c *gin.Context) {
13
+ companyIDRaw, exists := c.Get("company_id")
14
+ if !exists {
15
+ c.JSON(http.StatusUnauthorized, gin.H{"detail": "Unauthorized"})
16
+ return
17
+ }
18
+ companyID := companyIDRaw.(uint)
19
+
20
+ var members []models.Member
21
+ if err := config.DB.Where("company_id = ?", companyID).Order("created_at desc").Find(&members).Error; err != nil {
22
+ c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal mengambil data member"})
23
+ return
24
+ }
25
+
26
+ c.JSON(http.StatusOK, members)
27
+ }
28
+
29
+ // CreateMember (POST /api/members)
30
+ func CreateMember(c *gin.Context) {
31
+ companyIDRaw, exists := c.Get("company_id")
32
+ if !exists {
33
+ c.JSON(http.StatusUnauthorized, gin.H{"detail": "Unauthorized"})
34
+ return
35
+ }
36
+ companyID := companyIDRaw.(uint)
37
+
38
+ var input struct {
39
+ MemberID string `json:"member_id" binding:"required"`
40
+ Name string `json:"name" binding:"required"`
41
+ Phone string `json:"phone"`
42
+ }
43
+
44
+ if err := c.ShouldBindJSON(&input); err != nil {
45
+ c.JSON(http.StatusBadRequest, gin.H{"detail": "Data tidak valid", "error": err.Error()})
46
+ return
47
+ }
48
+
49
+ // Cek apakah member_id sudah dipakai di company ini
50
+ var existing models.Member
51
+ if err := config.DB.Where("company_id = ? AND member_id = ?", companyID, input.MemberID).First(&existing).Error; err == nil {
52
+ c.JSON(http.StatusBadRequest, gin.H{"detail": "ID Member sudah digunakan"})
53
+ return
54
+ }
55
+
56
+ newMember := models.Member{
57
+ CompanyID: companyID,
58
+ MemberID: input.MemberID,
59
+ Name: input.Name,
60
+ Phone: input.Phone,
61
+ Points: 0,
62
+ }
63
+
64
+ if err := config.DB.Create(&newMember).Error; err != nil {
65
+ c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal membuat member"})
66
+ return
67
+ }
68
+
69
+ c.JSON(http.StatusCreated, newMember)
70
+ }
71
+
72
+ // UpdateMember (PUT /api/members/:member_id)
73
+ func UpdateMember(c *gin.Context) {
74
+ companyIDRaw, exists := c.Get("company_id")
75
+ if !exists {
76
+ c.JSON(http.StatusUnauthorized, gin.H{"detail": "Unauthorized"})
77
+ return
78
+ }
79
+ companyID := companyIDRaw.(uint)
80
+ memberID := c.Param("member_id")
81
+
82
+ var member models.Member
83
+ if err := config.DB.Where("company_id = ? AND id = ?", companyID, memberID).First(&member).Error; err != nil {
84
+ c.JSON(http.StatusNotFound, gin.H{"detail": "Member tidak ditemukan"})
85
+ return
86
+ }
87
+
88
+ var input struct {
89
+ Name string `json:"name" binding:"required"`
90
+ Phone string `json:"phone"`
91
+ }
92
+
93
+ if err := c.ShouldBindJSON(&input); err != nil {
94
+ c.JSON(http.StatusBadRequest, gin.H{"detail": "Data tidak valid"})
95
+ return
96
+ }
97
+
98
+ member.Name = input.Name
99
+ member.Phone = input.Phone
100
+
101
+ if err := config.DB.Save(&member).Error; err != nil {
102
+ c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal update member"})
103
+ return
104
+ }
105
+
106
+ c.JSON(http.StatusOK, member)
107
+ }
108
+
109
+ // SetPoints (PUT /api/members/:member_id/points) - Owner ONLY
110
+ func SetPoints(c *gin.Context) {
111
+ companyIDRaw, exists := c.Get("company_id")
112
+ if !exists {
113
+ c.JSON(http.StatusUnauthorized, gin.H{"detail": "Unauthorized"})
114
+ return
115
+ }
116
+ companyID := companyIDRaw.(uint)
117
+ memberID := c.Param("member_id")
118
+
119
+ var member models.Member
120
+ if err := config.DB.Where("company_id = ? AND id = ?", companyID, memberID).First(&member).Error; err != nil {
121
+ c.JSON(http.StatusNotFound, gin.H{"detail": "Member tidak ditemukan"})
122
+ return
123
+ }
124
+
125
+ var input struct {
126
+ Points int `json:"points"`
127
+ }
128
+
129
+ if err := c.ShouldBindJSON(&input); err != nil {
130
+ c.JSON(http.StatusBadRequest, gin.H{"detail": "Data tidak valid"})
131
+ return
132
+ }
133
+
134
+ member.Points = input.Points
135
+
136
+ if err := config.DB.Save(&member).Error; err != nil {
137
+ c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal update poin"})
138
+ return
139
+ }
140
+
141
+ c.JSON(http.StatusOK, member)
142
+ }
143
+
144
+ // DeleteMember (DELETE /api/members/:member_id)
145
+ func DeleteMember(c *gin.Context) {
146
+ companyIDRaw, exists := c.Get("company_id")
147
+ if !exists {
148
+ c.JSON(http.StatusUnauthorized, gin.H{"detail": "Unauthorized"})
149
+ return
150
+ }
151
+ companyID := companyIDRaw.(uint)
152
+ memberID := c.Param("member_id")
153
+
154
+ if err := config.DB.Where("company_id = ? AND id = ?", companyID, memberID).Delete(&models.Member{}).Error; err != nil {
155
+ c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal menghapus member"})
156
+ return
157
+ }
158
+
159
+ c.JSON(http.StatusOK, gin.H{"detail": "Member dihapus"})
160
+ }
controllers/order_controller.go CHANGED
@@ -59,6 +59,8 @@ type CashPaymentRequest struct {
59
  PaidAmount int `json:"paid_amount" binding:"required,gt=0"`
60
  CustomerName string `json:"customer_name"`
61
  CustomerPhone string `json:"customer_phone"`
 
 
62
  }
63
 
64
  type MidtransPaymentRequest struct {
@@ -66,6 +68,8 @@ type MidtransPaymentRequest struct {
66
  CustomerName string `json:"customer_name"`
67
  CustomerEmail string `json:"customer_email"`
68
  CustomerPhone string `json:"customer_phone"`
 
 
69
  }
70
 
71
  type PaymentStatusResponse struct {
@@ -367,6 +371,12 @@ func PayCash(c *gin.Context) {
367
  order.PaymentStatus = "paid"
368
  order.PaidAmount = req.PaidAmount
369
  order.ChangeAmount = req.PaidAmount - order.TotalAmount
 
 
 
 
 
 
370
 
371
  // Hitung fee platform berdasarkan Company
372
  var company models.Company
@@ -497,9 +507,15 @@ func PayTripay(c *gin.Context) {
497
  // Simpan Tripay details ke DB
498
  order.MidtransOrderID = tripayRes.Data.Reference // Simpan nomor referensi Tripay di midtrans_order_id demi kecocokan DB
499
  order.MidtransToken = tripayRes.Data.CheckoutURL // Simpan checkout_url di midtrans_token demi kecocokan DB
500
- order.PaymentMethod = "tripay"
501
  order.CustomerName = req.CustomerName
502
  order.CustomerPhone = req.CustomerPhone
 
 
 
 
 
 
503
  config.DB.Save(&order)
504
 
505
  expTime := tripayRes.Data.ExpiredTime
 
59
  PaidAmount int `json:"paid_amount" binding:"required,gt=0"`
60
  CustomerName string `json:"customer_name"`
61
  CustomerPhone string `json:"customer_phone"`
62
+ ServedBy string `json:"served_by"`
63
+ MemberID *uint `json:"member_id"`
64
  }
65
 
66
  type MidtransPaymentRequest struct {
 
68
  CustomerName string `json:"customer_name"`
69
  CustomerEmail string `json:"customer_email"`
70
  CustomerPhone string `json:"customer_phone"`
71
+ ServedBy string `json:"served_by"`
72
+ MemberID *uint `json:"member_id"`
73
  }
74
 
75
  type PaymentStatusResponse struct {
 
371
  order.PaymentStatus = "paid"
372
  order.PaidAmount = req.PaidAmount
373
  order.ChangeAmount = req.PaidAmount - order.TotalAmount
374
+ if req.ServedBy != "" {
375
+ order.ServedBy = req.ServedBy
376
+ }
377
+ if req.MemberID != nil {
378
+ order.MemberID = req.MemberID
379
+ }
380
 
381
  // Hitung fee platform berdasarkan Company
382
  var company models.Company
 
507
  // Simpan Tripay details ke DB
508
  order.MidtransOrderID = tripayRes.Data.Reference // Simpan nomor referensi Tripay di midtrans_order_id demi kecocokan DB
509
  order.MidtransToken = tripayRes.Data.CheckoutURL // Simpan checkout_url di midtrans_token demi kecocokan DB
510
+ order.PaymentStatus = "pending"
511
  order.CustomerName = req.CustomerName
512
  order.CustomerPhone = req.CustomerPhone
513
+ if req.ServedBy != "" {
514
+ order.ServedBy = req.ServedBy
515
+ }
516
+ if req.MemberID != nil {
517
+ order.MemberID = req.MemberID
518
+ }
519
  config.DB.Save(&order)
520
 
521
  expTime := tripayRes.Data.ExpiredTime
fix_db.go ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ //go:build ignore
2
+
3
+ package main
4
+
5
+ import (
6
+ "log"
7
+ "gorm.io/driver/postgres"
8
+ "gorm.io/gorm"
9
+ )
10
+
11
+ func main() {
12
+ dsn := "postgres://postgres.gmzonnwzocuhtjhywtdd:Imtoky0d3vjava@aws-1-ap-south-1.pooler.supabase.com:5432/postgres"
13
+ db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
14
+ if err != nil {
15
+ log.Fatal(err)
16
+ }
17
+
18
+ db.Exec("UPDATE orders SET total_amount = total_amount + 757, paid_amount = paid_amount + 757 WHERE payment_method IN ('tripay', 'midtrans') AND payment_status = 'paid' AND total_amount = 1000")
19
+ }
main.go CHANGED
@@ -35,6 +35,7 @@ func main() {
35
  &models.OrderItem{},
36
  &models.OTP{},
37
  &models.Setting{},
 
38
  )
39
  if err != nil {
40
  log.Fatalf("Gagal melakukan Auto-Migration: %v", err)
@@ -146,6 +147,19 @@ func main() {
146
  }
147
  }
148
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  // === PRODUCTS & CATEGORIES ROUTES ===
150
  products := api.Group("/products")
151
  {
 
35
  &models.OrderItem{},
36
  &models.OTP{},
37
  &models.Setting{},
38
+ &models.Member{},
39
  )
40
  if err != nil {
41
  log.Fatalf("Gagal melakukan Auto-Migration: %v", err)
 
147
  }
148
  }
149
 
150
+ // === MEMBERS ROUTES ===
151
+ members := api.Group("/members")
152
+ {
153
+ members.Use(middleware.GetCurrentUser(), middleware.RequireTenantContext())
154
+ {
155
+ members.GET("/", controllers.ListMembers)
156
+ members.POST("/", controllers.CreateMember)
157
+ members.PUT("/:member_id", controllers.UpdateMember)
158
+ members.PUT("/:member_id/points", middleware.RequireOwner(), controllers.SetPoints)
159
+ members.DELETE("/:member_id", middleware.RequireOwner(), controllers.DeleteMember)
160
+ }
161
+ }
162
+
163
  // === PRODUCTS & CATEGORIES ROUTES ===
164
  products := api.Group("/products")
165
  {
models/models.go CHANGED
@@ -26,6 +26,18 @@ type Company struct {
26
  Categories []Category `gorm:"foreignKey:CompanyID;constraint:OnDelete:CASCADE" json:"-"`
27
  Products []Product `gorm:"foreignKey:CompanyID;constraint:OnDelete:CASCADE" json:"-"`
28
  Orders []Order `gorm:"foreignKey:CompanyID;constraint:OnDelete:CASCADE" json:"-"`
 
 
 
 
 
 
 
 
 
 
 
 
29
  }
30
 
31
  type Branch struct {
@@ -110,6 +122,9 @@ type Order struct {
110
  CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
111
  Company *Company `gorm:"foreignKey:CompanyID" json:"company,omitempty"`
112
  Branch *Branch `gorm:"foreignKey:BranchID" json:"branch,omitempty"`
 
 
 
113
  CustomerName string `gorm:"type:varchar(100)" json:"customer_name"`
114
  CustomerPhone string `gorm:"type:varchar(50)" json:"customer_phone"`
115
  Items []OrderItem `gorm:"foreignKey:OrderID;constraint:OnDelete:CASCADE" json:"items"`
 
26
  Categories []Category `gorm:"foreignKey:CompanyID;constraint:OnDelete:CASCADE" json:"-"`
27
  Products []Product `gorm:"foreignKey:CompanyID;constraint:OnDelete:CASCADE" json:"-"`
28
  Orders []Order `gorm:"foreignKey:CompanyID;constraint:OnDelete:CASCADE" json:"-"`
29
+ Members []Member `gorm:"foreignKey:CompanyID;constraint:OnDelete:CASCADE" json:"-"`
30
+ }
31
+
32
+ type Member struct {
33
+ ID uint `gorm:"primaryKey" json:"id"`
34
+ CompanyID uint `gorm:"not null;index" json:"company_id"`
35
+ MemberID string `gorm:"type:varchar(50);not null;uniqueIndex:uq_company_member" json:"member_id"`
36
+ Name string `gorm:"type:varchar(100);not null" json:"name"`
37
+ Phone string `gorm:"type:varchar(20)" json:"phone"`
38
+ Points int `gorm:"default:0" json:"points"`
39
+ CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
40
+ Company *Company `gorm:"foreignKey:CompanyID" json:"company,omitempty"`
41
  }
42
 
43
  type Branch struct {
 
122
  CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
123
  Company *Company `gorm:"foreignKey:CompanyID" json:"company,omitempty"`
124
  Branch *Branch `gorm:"foreignKey:BranchID" json:"branch,omitempty"`
125
+ MemberID *uint `gorm:"index" json:"member_id"`
126
+ Member *Member `gorm:"foreignKey:MemberID" json:"member,omitempty"`
127
+ ServedBy string `gorm:"type:varchar(100)" json:"served_by"`
128
  CustomerName string `gorm:"type:varchar(100)" json:"customer_name"`
129
  CustomerPhone string `gorm:"type:varchar(50)" json:"customer_phone"`
130
  Items []OrderItem `gorm:"foreignKey:OrderID;constraint:OnDelete:CASCADE" json:"items"`
test_db.go ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ //go:build ignore
2
+
3
+ package main
4
+
5
+ import (
6
+ "fmt"
7
+ "log"
8
+ "gorm.io/driver/postgres"
9
+ "gorm.io/gorm"
10
+ "time"
11
+ )
12
+
13
+ type Order struct {
14
+ ID uint
15
+ OrderNumber string
16
+ PaymentStatus string
17
+ TotalAmount int
18
+ PaidAmount int
19
+ PaymentMethod string
20
+ CreatedAt time.Time
21
+ }
22
+
23
+ func main() {
24
+ dsn := "postgres://postgres.gmzonnwzocuhtjhywtdd:Imtoky0d3vjava@aws-1-ap-south-1.pooler.supabase.com:5432/postgres"
25
+ db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
26
+ if err != nil {
27
+ log.Fatal(err)
28
+ }
29
+
30
+ var orders []Order
31
+ db.Order("id desc").Limit(5).Find(&orders)
32
+ for _, o := range orders {
33
+ fmt.Printf("ID: %d | Ref: %s | Status: %s | Total: %d | Paid: %d | Method: %s | Created: %v\n", o.ID, o.OrderNumber, o.PaymentStatus, o.TotalAmount, o.PaidAmount, o.PaymentMethod, o.CreatedAt)
34
+ }
35
+ }
warungpos.db ADDED
File without changes