Compare commits
9 Commits
a52b108bf8
...
cb60cb39d5
Author | SHA1 | Date | |
---|---|---|---|
cb60cb39d5 | |||
3ce1c5c2d0 | |||
9cd9b20163 | |||
81b379afbd | |||
4244e397f3 | |||
dbe140ecaf | |||
b5747c9d9d | |||
bd2005bf5d | |||
953394335b |
|
@ -123,6 +123,10 @@ func main() {
|
||||||
{
|
{
|
||||||
metricRoutes.GET("/all", handlers.MetricGetAll)
|
metricRoutes.GET("/all", handlers.MetricGetAll)
|
||||||
}
|
}
|
||||||
|
paymentRoutes := api.Group("/payment", middleware.AuthMiddleware())
|
||||||
|
{
|
||||||
|
paymentRoutes.POST("/add", handlers.PaymentAdd)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
||||||
|
|
|
@ -10,3 +10,7 @@ type UserIdentifiable interface {
|
||||||
GetUserID() uint
|
GetUserID() uint
|
||||||
SetUserID(userID uint)
|
SetUserID(userID uint)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PaymentGroup interface {
|
||||||
|
__internalBelogingToPayment()
|
||||||
|
}
|
||||||
|
|
43
db/item.go
43
db/item.go
|
@ -1,11 +1,16 @@
|
||||||
package db
|
package db
|
||||||
|
|
||||||
import "gorm.io/gorm"
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
type Item struct {
|
type Item struct {
|
||||||
gorm.Model
|
gorm.Model
|
||||||
Name string
|
Name string
|
||||||
Comment string
|
Comment string
|
||||||
|
Price uint64
|
||||||
MetricType uint8
|
MetricType uint8
|
||||||
MetricValue uint64
|
MetricValue uint64
|
||||||
//
|
//
|
||||||
|
@ -16,7 +21,7 @@ type Item struct {
|
||||||
Carbs uint64
|
Carbs uint64
|
||||||
Fats uint64
|
Fats uint64
|
||||||
//
|
//
|
||||||
Prices []ItemPrice
|
Prices []ItemPrice `gorm:"constraint:OnDelete:CASCADE;"`
|
||||||
CurrentPriceID uint
|
CurrentPriceID uint
|
||||||
CurrentPrice *ItemPrice
|
CurrentPrice *ItemPrice
|
||||||
//
|
//
|
||||||
|
@ -26,6 +31,8 @@ type Item struct {
|
||||||
User *User
|
User *User
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i Item) __internalBelogingToPayment() {}
|
||||||
|
|
||||||
// Implements db.UserIdentifiable:1
|
// Implements db.UserIdentifiable:1
|
||||||
func (i Item) GetID() uint {
|
func (i Item) GetID() uint {
|
||||||
return i.ID
|
return i.ID
|
||||||
|
@ -70,3 +77,35 @@ func GetItemToRootCat(id uint, preloadPrices bool) (*Item, error) {
|
||||||
}).Preload("CurrentPrice").First(&item, id).Error
|
}).Preload("CurrentPrice").First(&item, id).Error
|
||||||
return &item, err
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -15,3 +15,5 @@ type ItemBought struct {
|
||||||
MetricType uint8
|
MetricType uint8
|
||||||
MetricValue uint64
|
MetricValue uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i ItemBought) __internalBelogingToPayment() {}
|
||||||
|
|
|
@ -18,6 +18,8 @@ type Payment struct {
|
||||||
Title string
|
Title string
|
||||||
Descr string
|
Descr string
|
||||||
Note string
|
Note string
|
||||||
Items []ItemBought
|
Items []ItemBought `gorm:"constraint:OnDelete:CASCADE;"`
|
||||||
Date time.Time
|
Date time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p Payment) __internalBelogingToPayment() {}
|
||||||
|
|
|
@ -21,7 +21,7 @@ var itemTransform func(inp *db.Item) types.DbItem = func(inp *db.Item) types.DbI
|
||||||
Proteins: inp.Proteins,
|
Proteins: inp.Proteins,
|
||||||
Carbs: inp.Carbs,
|
Carbs: inp.Carbs,
|
||||||
Fats: inp.Fats,
|
Fats: inp.Fats,
|
||||||
Price: inp.CurrentPrice.Price,
|
Price: inp.Price,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
146
handlers/payment.go
Normal file
146
handlers/payment.go
Normal 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)})
|
||||||
|
}
|
|
@ -65,16 +65,6 @@ type DbType struct {
|
||||||
Color string `json:"color" example:"red"`
|
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 {
|
type Session struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
UserID uint `json:"user_id" example:"1"`
|
UserID uint `json:"user_id" example:"1"`
|
||||||
|
@ -122,3 +112,28 @@ type DbMetric struct {
|
||||||
Name string `json:"name" example:"Kilogram"`
|
Name string `json:"name" example:"Kilogram"`
|
||||||
Short string `json:"short" example:"kg"`
|
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"`
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user