Compare commits

...

9 Commits

8 changed files with 226 additions and 14 deletions

View File

@ -123,6 +123,10 @@ func main() {
{
metricRoutes.GET("/all", handlers.MetricGetAll)
}
paymentRoutes := api.Group("/payment", middleware.AuthMiddleware())
{
paymentRoutes.POST("/add", handlers.PaymentAdd)
}
}
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))

View File

@ -10,3 +10,7 @@ type UserIdentifiable interface {
GetUserID() uint
SetUserID(userID uint)
}
type PaymentGroup interface {
__internalBelogingToPayment()
}

View File

@ -1,11 +1,16 @@
package db
import "gorm.io/gorm"
import (
"errors"
"gorm.io/gorm"
)
type Item struct {
gorm.Model
Name string
Comment string
Price uint64
MetricType uint8
MetricValue uint64
//
@ -16,7 +21,7 @@ type Item struct {
Carbs uint64
Fats uint64
//
Prices []ItemPrice
Prices []ItemPrice `gorm:"constraint:OnDelete:CASCADE;"`
CurrentPriceID uint
CurrentPrice *ItemPrice
//
@ -26,6 +31,8 @@ type Item struct {
User *User
}
func (i Item) __internalBelogingToPayment() {}
// Implements db.UserIdentifiable:1
func (i Item) GetID() uint {
return i.ID
@ -70,3 +77,35 @@ func GetItemToRootCat(id uint, preloadPrices bool) (*Item, error) {
}).Preload("CurrentPrice").First(&item, id).Error
return &item, err
}
var (
ERROR_ITEM_PRICE_ISZERO = errors.New("Item's Price is zero")
ERROR_ITEM_ITEMPRICE_INTERR = errors.New("Item's ItemPrice ID is zero after creating")
)
func (i *Item) BeforeCreate(tx *gorm.DB) error {
if i.Price == 0 {
return ERROR_ITEM_PRICE_ISZERO
}
return nil
}
func (i *Item) AfterCreate(tx *gorm.DB) error {
if i.CurrentPriceID == 0 {
itemPrice := &ItemPrice{
ItemID: i.ID,
Price: i.Price,
}
if err := tx.Create(itemPrice).Error; err != nil {
return err
}
if itemPrice.ID == 0 {
return ERROR_ITEM_ITEMPRICE_INTERR
}
i.CurrentPriceID = itemPrice.ID
if err := tx.Save(i).Error; err != nil {
return err
}
}
return nil
}

View File

@ -15,3 +15,5 @@ type ItemBought struct {
MetricType uint8
MetricValue uint64
}
func (i ItemBought) __internalBelogingToPayment() {}

View File

@ -18,6 +18,8 @@ type Payment struct {
Title string
Descr string
Note string
Items []ItemBought
Items []ItemBought `gorm:"constraint:OnDelete:CASCADE;"`
Date time.Time
}
func (p Payment) __internalBelogingToPayment() {}

View File

@ -21,7 +21,7 @@ var itemTransform func(inp *db.Item) types.DbItem = func(inp *db.Item) types.DbI
Proteins: inp.Proteins,
Carbs: inp.Carbs,
Fats: inp.Fats,
Price: inp.CurrentPrice.Price,
Price: inp.Price,
}
}

146
handlers/payment.go Normal file
View File

@ -0,0 +1,146 @@
package handlers
import (
"fmt"
"log"
"git.qowevisa.me/Qowevisa/fin-check-api/db"
"git.qowevisa.me/Qowevisa/fin-check-api/types"
"github.com/gin-gonic/gin"
)
var itemBoughtTransform func(inp *db.ItemBought) types.ItemBought = func(inp *db.ItemBought) types.ItemBought {
return types.ItemBought{}
}
var paymentTransform func(inp *db.Payment) types.Payment = func(inp *db.Payment) types.Payment {
var items []types.ItemBought
for _, item := range inp.Items {
items = append(items, itemBoughtTransform(&item))
}
return types.Payment{
ID: inp.ID,
CardID: inp.CardID,
CategoryID: inp.CategoryID,
Title: inp.Title,
Description: inp.Descr,
Note: inp.Note,
Date: inp.Date,
Items: items,
}
}
// @Summary Add payment
// @Description Add payment
// @Tags payment
// @Accept json
// @Produce json
// @Param Authorization header string true "Bearer token"
// @Param payment body types.Payment true "Payment"
// @Success 200 {object} types.Message
// @Failure 400 {object} types.ErrorResponse
// @Failure 500 {object} types.ErrorResponse
// @Security ApiKeyAuth
// @Router /payment/add [post]
func PaymentAdd(c *gin.Context) {
userID, err := GetUserID(c)
if err != nil {
c.JSON(500, types.ErrorResponse{Message: err.Error()})
return
}
var updates types.Payment
if err := c.ShouldBindJSON(&updates); err != nil {
log.Printf("err is %v\n", err)
c.JSON(400, types.ErrorResponse{Message: "Invalid request"})
return
}
// As this handler will likely create more than one row in database we need to
// create some sort of defer func that will rollback all created rows
weNeedRollback := false
var deletableIfRollback []db.PaymentGroup
defer func() {
if weNeedRollback {
dbc := db.Connect()
for _, deleteIt := range deletableIfRollback {
if err := dbc.Debug().Delete(deleteIt).Error; err != nil {
log.Printf("ERROR: dbc.Delete: %v\n", err)
continue
}
}
}
}()
dbc := db.Connect()
payment := &db.Payment{
CardID: updates.CardID,
CategoryID: updates.CategoryID,
UserID: userID,
Title: updates.Title,
Descr: updates.Description,
Note: updates.Note,
Date: updates.Date,
}
if err := dbc.Debug().Create(payment).Error; err != nil {
c.JSON(500, types.ErrorResponse{Message: err.Error()})
weNeedRollback = true
return
}
if payment.ID == 0 {
c.JSON(500, types.ErrorResponse{Message: "Internal error: ERR.P.A.1"})
weNeedRollback = true
return
}
deletableIfRollback = append(deletableIfRollback, payment)
for _, uItemBought := range updates.Items {
// Creating item and adding it to rollback if itemID is set to 0
if uItemBought.ItemID == 0 {
newItem := &db.Item{
Name: uItemBought.NewName,
Comment: uItemBought.NewComment,
Price: uItemBought.Price,
MetricType: uItemBought.MetricType,
MetricValue: uItemBought.MetricValue,
CategoryID: updates.CategoryID,
TypeID: uItemBought.TypeID,
UserID: userID,
}
if err := dbc.Create(newItem).Error; err != nil {
c.JSON(500, types.ErrorResponse{Message: err.Error()})
weNeedRollback = true
return
}
if newItem.ID == 0 {
c.JSON(500, types.ErrorResponse{Message: "Internal error: ERR.P.A.2"})
weNeedRollback = true
return
}
deletableIfRollback = append(deletableIfRollback, newItem)
newItemBought := &db.ItemBought{
ItemID: newItem.ID,
PaymentID: payment.ID,
TypeID: uItemBought.TypeID,
Quantity: uItemBought.Quantity,
TotalCost: newItem.Price * uint64(uItemBought.Quantity),
MetricType: uItemBought.MetricType,
MetricValue: uItemBought.MetricValue,
}
if err := dbc.Create(newItemBought).Error; err != nil {
c.JSON(500, types.ErrorResponse{Message: err.Error()})
weNeedRollback = true
return
}
if newItemBought.ID == 0 {
c.JSON(500, types.ErrorResponse{Message: "Internal error: ERR.P.A.3"})
weNeedRollback = true
return
}
deletableIfRollback = append(deletableIfRollback, newItemBought)
newItemBought.Item = newItem
} else {
// TODO: check if Item has same userID and potentially update Item
}
}
c.JSON(200, types.Message{Info: fmt.Sprintf("Entity with %d ID is created successfully!", payment.ID)})
}

View File

@ -65,16 +65,6 @@ type DbType struct {
Color string `json:"color" example:"red"`
}
type DbPayment struct {
ID uint `json:"id" example:"1"`
CardID uint `json:"card_id" example:"1"`
CategoryID uint `json:"category_id" example:"1"`
Title string `json:"title" example:"Veggies"`
Descr string `json:"description" example:""`
Note string `json:"not" example:"I'm a teapot"`
Date time.Time `json:"date" example:"29/11/2001 12:00"`
}
type Session struct {
ID string `json:"id"`
UserID uint `json:"user_id" example:"1"`
@ -122,3 +112,28 @@ type DbMetric struct {
Name string `json:"name" example:"Kilogram"`
Short string `json:"short" example:"kg"`
}
type Payment struct {
ID uint `json:"id" example:"1"`
CardID uint `json:"card_id" example:"1"`
CategoryID uint `json:"category_id" example:"1"`
Title string `json:"title" example:"some title"`
Description string `json:"descr" example:"i bought some title for 20$"`
Note string `json:"note" example:"no i did not hit domain"`
Date time.Time `json:"date" example:"29/11/2001 12:00"`
Items []ItemBought `json:"items" example:"[]"`
}
type ItemBought struct {
ID uint `json:"id" example:"1"`
NewName string `json:"new_name" example:"itemName"`
NewComment string `json:"new_comment" example:"itemName"`
ItemID uint `json:"item_id" example:"0"`
PaymentID uint `json:"payment_id" example:"1"`
TypeID uint `json:"type_id" example:"1"`
Price uint64 `json:"price" example:"1025"`
Quantity uint `json:"quantity" example:"2"`
TotalCost uint64 `json:"total_cost" example:"2050"`
MetricType uint8 `json:"metric_type" example:"0"`
MetricValue uint64 `json:"metric_value" example:"100"`
}