Newer
Older
package db
import (
"errors"
"fmt"
"log"
"time"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
type Transaction struct {
gorm.Model
MemberNum int `json:"-" gorm:"column:member"`
Member *Member `json:"member,omitempty" gorm:"foreignKey:MemberNum;references:Num"`
Date time.Time `json:"date"`
Total int `json:"total"`
Type string `json:"type"`
ProxyNum int `json:"-" gorm:"column:proxy"`
Proxy *Member `json:"proxy,omitempty" gorm:"foreignKey:ProxyNum;references:Num"`
Purchase []Purchase `json:"purchase,omitempty"`
Topup *Topup `json:"topup,omitempty"`
OrderPurchase []OrderPurchase `json:"order_purchase,omitempty" gorm:"foreignKey:TransactionID;constraint:OnDelete:CASCADE"`
Order *Order `json:"order,omitempty" gorm:"constraint:OnDelete:CASCADE"`
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
OrderID *uint `json:"-"`
Refund *Order `json:"refund,omitempty" gorm:"foreignKey:TransactionID"`
}
type Topup struct {
gorm.Model `json:"-"`
TransactionID uint `json:"-" gorm:"column:transaction"`
Comment string `json:"comment"`
}
type Purchase struct {
gorm.Model `json:"-"`
TransactionID uint `json:"-" gorm:"column:transaction"`
ProductCode int `json:"code" gorm:"column:product"`
Product Product `json:"product" gorm:"foreignKey:ProductCode;references:Code"`
Price int `json:"price"`
Amount int `json:"amount"`
}
func (d *DB) ListTransactions() (transactions []Transaction, err error) {
err = d.transactionQuery().
Order("date desc").
Find(&transactions).Error
return
}
func (d *DB) TransactionByMember(num int) (transactions []Transaction, err error) {
err = d.transactionQuery().
Where("member = ?", num).
Order("date desc").
Find(&transactions).Error
return
}
func (d *DB) GetTransaction(id int) (transaction Transaction, err error) {
err = d.transactionQuery().
First(&transaction, id).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
err = ErrorNotFound
}
return
}
func (d *DB) AddTopup(adminNum int, memberNum int, amount int, comment string) (transaction Transaction, err error) {
transaction = Transaction{
MemberNum: memberNum,
},
Type: "topup",
Total: amount,
}
err = createTransaction(d.db, &transaction)
return
}
func (d *DB) AddPurchase(adminNum int, memberNum int, purchase []Purchase) (transaction Transaction, err error) {
total := 0
for i, p := range purchase {
var product Product
err = d.db.Where("code = ?", p.ProductCode).First(&product).Error
if err != nil {
log.Printf("Can't get product %d: %v", p.ProductCode, err)
err = ErrorNotFound
return
}
total += product.Price * p.Amount
purchase[i].Price = product.Price
}
if total == 0 {
log.Printf("Empty purchase (%d)", memberNum)
err = ErrorInvalidRequest
return
}
transaction = Transaction{
MemberNum: memberNum,
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
Date: time.Now(),
Purchase: purchase,
Type: "purchase",
Total: -total,
}
err = d.db.Transaction(func(tx *gorm.DB) error {
err := createTransaction(tx, &transaction)
if err != nil {
return err
}
for _, p := range purchase {
err = tx.Model(&Product{}).
Where("code = ?", p.ProductCode).
Update("stock", gorm.Expr("stock - ?", p.Amount)).Error
if err != nil {
return fmt.Errorf("Can't update product stock %d-%d: %v", p.ProductCode, p.Amount, err)
}
}
return nil
})
return
}
func (d *DB) transactionQuery() *gorm.DB {
return d.db.Preload("Purchase.Product").
Preload("Order.Products").
Preload("OrderPurchase.OrderProduct.Product").
Preload(clause.Associations)
}
func createTransaction(db *gorm.DB, transaction *Transaction) error {
return db.Transaction(func(tx *gorm.DB) error {
err := updateMemberBalance(tx, transaction.MemberNum, transaction.Total)
if err != nil {
return err
}
return tx.Create(&transaction).Error
})
}
func updateMemberBalance(tx *gorm.DB, memberNum int, amount int) error {
var member Member
err := tx.Where("num = ?", memberNum).Find(&member).Error
if err != nil {
log.Printf("Can't find member for transaction %d: %v", memberNum, err)
return ErrorNotFound
}
if member.Balance < -amount {
log.Printf("Member %d don't have enough money (%d-%d)", member.Num, member.Balance, amount)
return ErrorInvalidRequest
}
err = tx.Model(&Member{}).
Where("num = ?", memberNum).
Update("balance", gorm.Expr("balance + ?", amount)).Error
if err != nil {
log.Printf("Can't update update member balance %d-%d: %v", member.Num, amount, err)
}
return err
}