Skip to content
Snippets Groups Projects
transaction.go 3.93 KiB
package api

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"time"

	"github.com/gorilla/mux"
	"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"`

	Purchase []Purchase      `json:"purchase"`
	Topup    *Topup          `json:"topup"`
	Order    []OrderPurchase `json:"order"`
	Refund   *Order          `json:"refund"`
}

func (a *api) ListTransactions(w http.ResponseWriter, req *http.Request) {
	var transactions []Transaction
	err := a.db.Preload("Purchase.Product").
		Preload(clause.Associations).
		Order("date desc").
		Find(&transactions).Error
	if err != nil {
		log.Printf("Can't list transactions: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	err = json.NewEncoder(w).Encode(transactions)
	if err != nil {
		log.Printf("Can't encode transactions: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
	}
}

func (a *api) GetTransaction(num int, role string, w http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	var transaction Transaction
	err := a.db.Preload("Purchase.Product").
		Preload(clause.Associations).
		First(&transaction, vars["id"]).Error
	if err != nil {
		if err.Error() == "record not found" {
			w.WriteHeader(http.StatusNotFound)
			return
		}
		log.Printf("Can't get transaction %s: %v", vars["id"], err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	if transaction.MemberNum != num && role != "admin" {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	err = json.NewEncoder(w).Encode(transaction)
	if err != nil {
		log.Printf("Can't encode transaction: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func (a *api) GetMemberTransactions(w http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	num, _ := strconv.Atoi(vars["num"])
	a.getTransactionsByMember(num, w, req)
}

func (a *api) getTransactionsByMember(num int, w http.ResponseWriter, req *http.Request) {
	var transactions []Transaction
	err := a.db.Where("member = ?", num).
		Preload("Purchase.Product").
		Preload(clause.Associations).
		Order("date desc").
		Find(&transactions).Error
	if err != nil {
		log.Printf("Can't list transactions: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	err = json.NewEncoder(w).Encode(transactions)
	if err != nil {
		log.Printf("Can't encode transactions: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
	}
}

func createTransaction(db *gorm.DB, transaction *Transaction) (httpStatus int, err error) {
	httpStatus = http.StatusOK
	err = db.Transaction(func(tx *gorm.DB) error {
		var member Member
		err := tx.Where("num = ?", transaction.MemberNum).Find(&member).Error
		if err != nil {
			httpStatus = http.StatusNotAcceptable
			return fmt.Errorf("Can't find member %d: %v", transaction.MemberNum, err)
		}
		if member.Balance < -transaction.Total {
			httpStatus = http.StatusBadRequest
			return fmt.Errorf("Member %d don't have enough money (%d-%d)", member.Num, member.Balance, transaction.Total)
		}
		err = tx.Model(&Member{}).
			Where("num = ?", transaction.MemberNum).
			Update("balance", gorm.Expr("balance + ?", transaction.Total)).Error
		if err != nil {
			httpStatus = http.StatusNotAcceptable
			fmt.Errorf("Can't update update member balance %d-%d: %v", member.Num, transaction.Total, err)
		}

		err = tx.Create(&transaction).Error
		if err != nil {
			httpStatus = http.StatusInternalServerError
			return fmt.Errorf("Can't create transaction: %v\n%v", err, transaction)
		}
		return nil
	})
	return
}