Compare commits
8 Commits
8807235dad
...
2e621a6841
Author | SHA1 | Date | |
---|---|---|---|
2e621a6841 | |||
b6f393bd46 | |||
ffd0c8e45b | |||
e655968fc3 | |||
dd5f67536e | |||
e6d53c4d17 | |||
994f623f23 | |||
f221480b2c |
|
@ -127,6 +127,7 @@ func main() {
|
|||
paymentRoutes := api.Group("/payment", middleware.AuthMiddleware())
|
||||
{
|
||||
paymentRoutes.POST("/add", handlers.PaymentAdd)
|
||||
paymentRoutes.GET("/all", handlers.PaymentGetAll)
|
||||
}
|
||||
currencyRoutes := api.Group("/currency", middleware.AuthMiddleware())
|
||||
{
|
||||
|
|
|
@ -34,6 +34,7 @@ type Helper_ExpenseBulk struct {
|
|||
UserID uint
|
||||
}
|
||||
|
||||
// {{{ Helper_ExpenseBulk.CreateExpenseFromChild I'm not proud of this code
|
||||
func (he *Helper_ExpenseBulk) CreateExpenseFromChild(c Expense) *Expense {
|
||||
var cardID uint
|
||||
var typeID uint
|
||||
|
@ -80,6 +81,8 @@ func (he *Helper_ExpenseBulk) CreateExpenseFromChild(c Expense) *Expense {
|
|||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
// Implements db.UserIdentifiable:1
|
||||
func (e Expense) GetID() uint {
|
||||
return e.ID
|
||||
|
@ -96,7 +99,7 @@ func (e *Expense) SetUserID(id uint) {
|
|||
}
|
||||
|
||||
var (
|
||||
ERROR_EXPENSE_INVALID_USERID = errors.New("Expense's `UserID` and Card's `UserID` are not equal")
|
||||
ERROR_EXPENSE_INVALID_CARD_USERID = errors.New("Expense's `UserID` and Card's `UserID` are not equal")
|
||||
ERROR_EXPENSE_CARD_INSUFFICIENT_BALANCE = errors.New("Card's `Balance` is lower than Expense's Value")
|
||||
ERROR_EXPENSE_INVALID_TYPE_USERID = errors.New("Expense's `UserID` and Type's `UserID` are not equal")
|
||||
)
|
||||
|
@ -107,7 +110,7 @@ func (e *Expense) BeforeCreate(tx *gorm.DB) error {
|
|||
return err
|
||||
}
|
||||
if card.UserID != e.UserID {
|
||||
return ERROR_EXPENSE_INVALID_USERID
|
||||
return ERROR_EXPENSE_INVALID_CARD_USERID
|
||||
}
|
||||
if card.Balance < e.Value {
|
||||
return ERROR_EXPENSE_CARD_INSUFFICIENT_BALANCE
|
||||
|
@ -138,7 +141,7 @@ func (e *Expense) BeforeUpdate(tx *gorm.DB) (err error) {
|
|||
return err
|
||||
}
|
||||
if oldCard.UserID != e.UserID {
|
||||
return ERROR_EXPENSE_INVALID_USERID
|
||||
return ERROR_EXPENSE_INVALID_CARD_USERID
|
||||
}
|
||||
oldCard.Balance += original.Value
|
||||
if err := tx.Save(oldCard).Error; err != nil {
|
||||
|
@ -152,7 +155,7 @@ func (e *Expense) BeforeUpdate(tx *gorm.DB) (err error) {
|
|||
return err
|
||||
}
|
||||
if newCard.UserID != e.UserID {
|
||||
return ERROR_EXPENSE_INVALID_USERID
|
||||
return ERROR_EXPENSE_INVALID_CARD_USERID
|
||||
}
|
||||
if newCard.Balance < e.Value {
|
||||
return ERROR_EXPENSE_CARD_INSUFFICIENT_BALANCE
|
||||
|
@ -178,7 +181,7 @@ func (e *Expense) AfterDelete(tx *gorm.DB) (err error) {
|
|||
return err
|
||||
}
|
||||
if card.UserID != e.UserID {
|
||||
return ERROR_EXPENSE_INVALID_USERID
|
||||
return ERROR_EXPENSE_INVALID_CARD_USERID
|
||||
}
|
||||
card.Balance += e.Value
|
||||
if err := tx.Save(card).Error; err != nil {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
@ -23,3 +24,26 @@ type Payment struct {
|
|||
}
|
||||
|
||||
func (p Payment) __internalBelogingToPayment() {}
|
||||
|
||||
var (
|
||||
ERROR_PAYMENT_INVALID_CARD_USERID = errors.New("Payment's `UserID` and Card's `UserID` are not equal")
|
||||
ERROR_PAYMENT_INVALID_CATEGORY_USERID = errors.New("Payment's `UserID` and Category's `UserID` are not equal")
|
||||
)
|
||||
|
||||
func (p *Payment) BeforeSave(tx *gorm.DB) error {
|
||||
paymentCard := &Card{}
|
||||
if err := tx.Find(paymentCard, p.CardID).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if paymentCard.UserID != p.UserID {
|
||||
return ERROR_PAYMENT_INVALID_CARD_USERID
|
||||
}
|
||||
paymentCategory := &Category{}
|
||||
if err := tx.Find(paymentCategory, p.CategoryID).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if paymentCategory.UserID != p.UserID {
|
||||
return ERROR_PAYMENT_INVALID_CATEGORY_USERID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -9,16 +9,33 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var itemBoughtTransform func(inp *db.ItemBought) types.ItemBought = func(inp *db.ItemBought) types.ItemBought {
|
||||
return types.ItemBought{}
|
||||
var itemBoughtTransform func(inp *db.ItemBought) types.DbItemBought = func(inp *db.ItemBought) types.DbItemBought {
|
||||
var item types.DbItem
|
||||
var price uint64 = 0
|
||||
if inp.Item != nil {
|
||||
item = itemTransform(inp.Item)
|
||||
price = inp.Item.Price
|
||||
}
|
||||
return types.DbItemBought{
|
||||
ID: inp.ID,
|
||||
ItemID: inp.ItemID,
|
||||
PaymentID: inp.PaymentID,
|
||||
TypeID: inp.TypeID,
|
||||
Price: price,
|
||||
Quantity: inp.Quantity,
|
||||
TotalCost: inp.TotalCost,
|
||||
MetricType: inp.MetricType,
|
||||
MetricValue: inp.MetricValue,
|
||||
Item: item,
|
||||
}
|
||||
}
|
||||
|
||||
var paymentTransform func(inp *db.Payment) types.Payment = func(inp *db.Payment) types.Payment {
|
||||
var items []types.ItemBought
|
||||
var paymentTransform func(inp *db.Payment) types.DbPayment = func(inp *db.Payment) types.DbPayment {
|
||||
var items []types.DbItemBought
|
||||
for _, item := range inp.Items {
|
||||
items = append(items, itemBoughtTransform(&item))
|
||||
}
|
||||
return types.Payment{
|
||||
return types.DbPayment{
|
||||
ID: inp.ID,
|
||||
CardID: inp.CardID,
|
||||
CategoryID: inp.CategoryID,
|
||||
|
@ -36,7 +53,7 @@ var paymentTransform func(inp *db.Payment) types.Payment = func(inp *db.Payment)
|
|||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param Authorization header string true "Bearer token"
|
||||
// @Param payment body types.Payment true "Payment"
|
||||
// @Param payment body types.DbPayment true "Payment"
|
||||
// @Success 200 {object} types.Message
|
||||
// @Failure 400 {object} types.ErrorResponse
|
||||
// @Failure 500 {object} types.ErrorResponse
|
||||
|
@ -49,7 +66,7 @@ func PaymentAdd(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
var updates types.Payment
|
||||
var updates types.DbPayment
|
||||
if err := c.ShouldBindJSON(&updates); err != nil {
|
||||
log.Printf("err is %v\n", err)
|
||||
c.JSON(400, types.ErrorResponse{Message: "Invalid request"})
|
||||
|
@ -63,7 +80,7 @@ func PaymentAdd(c *gin.Context) {
|
|||
if weNeedRollback {
|
||||
dbc := db.Connect()
|
||||
for _, deleteIt := range deletableIfRollback {
|
||||
if err := dbc.Debug().Delete(deleteIt).Error; err != nil {
|
||||
if err := dbc.Delete(deleteIt).Error; err != nil {
|
||||
log.Printf("ERROR: dbc.Delete: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
@ -92,7 +109,9 @@ func PaymentAdd(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
deletableIfRollback = append(deletableIfRollback, payment)
|
||||
var substractFromCard uint64 = 0
|
||||
for _, uItemBought := range updates.Items {
|
||||
var totalCost uint64 = 0
|
||||
// Creating item and adding it to rollback if itemID is set to 0
|
||||
if uItemBought.ItemID == 0 {
|
||||
newItem := &db.Item{
|
||||
|
@ -116,12 +135,13 @@ func PaymentAdd(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
deletableIfRollback = append(deletableIfRollback, newItem)
|
||||
totalCost = newItem.Price * uint64(uItemBought.Quantity)
|
||||
newItemBought := &db.ItemBought{
|
||||
ItemID: newItem.ID,
|
||||
PaymentID: payment.ID,
|
||||
TypeID: uItemBought.TypeID,
|
||||
Quantity: uItemBought.Quantity,
|
||||
TotalCost: newItem.Price * uint64(uItemBought.Quantity),
|
||||
TotalCost: totalCost,
|
||||
MetricType: uItemBought.MetricType,
|
||||
MetricValue: uItemBought.MetricValue,
|
||||
}
|
||||
|
@ -138,9 +158,104 @@ func PaymentAdd(c *gin.Context) {
|
|||
deletableIfRollback = append(deletableIfRollback, newItemBought)
|
||||
newItemBought.Item = newItem
|
||||
} else {
|
||||
// TODO: check if Item has same userID and potentially update Item
|
||||
dbItem := &db.Item{}
|
||||
if err := dbc.Find(dbItem, uItemBought.ItemID).Error; err != nil {
|
||||
c.JSON(500, types.ErrorResponse{Message: err.Error()})
|
||||
weNeedRollback = true
|
||||
return
|
||||
}
|
||||
if dbItem.UserID != userID {
|
||||
c.JSON(500, types.ErrorResponse{Message: "ItemID is not your"})
|
||||
weNeedRollback = true
|
||||
return
|
||||
}
|
||||
if dbItem.Price != uItemBought.Price {
|
||||
dbItem.Price = uItemBought.Price
|
||||
if err := dbc.Save(dbItem).Error; err != nil {
|
||||
c.JSON(500, types.ErrorResponse{Message: err.Error()})
|
||||
weNeedRollback = true
|
||||
return
|
||||
}
|
||||
}
|
||||
totalCost = dbItem.Price * uint64(uItemBought.Quantity)
|
||||
newItemBought := &db.ItemBought{
|
||||
ItemID: dbItem.ID,
|
||||
PaymentID: payment.ID,
|
||||
TypeID: uItemBought.TypeID,
|
||||
Quantity: uItemBought.Quantity,
|
||||
TotalCost: totalCost,
|
||||
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.4"})
|
||||
weNeedRollback = true
|
||||
return
|
||||
}
|
||||
deletableIfRollback = append(deletableIfRollback, newItemBought)
|
||||
newItemBought.Item = dbItem
|
||||
}
|
||||
// As totalCost is calculated either way AND db.Item.BeforeSave have db.Item.Price check
|
||||
// we can gently assume that totalCost != 0
|
||||
substractFromCard += totalCost
|
||||
}
|
||||
|
||||
// Checks for db.Card.UserID != db.Payment.UserID for payment is done in db.Payment hooks
|
||||
card := &db.Card{}
|
||||
if err := dbc.Find(card, payment.CardID).Error; err != nil {
|
||||
c.JSON(500, types.ErrorResponse{Message: err.Error()})
|
||||
weNeedRollback = true
|
||||
return
|
||||
}
|
||||
// TODO: Examine if that would be better to calculate substractFromCard
|
||||
// BEFORE update.Items loop and therefore this check can be done before
|
||||
// processing payment's ItemBought entities
|
||||
if card.Balance < substractFromCard {
|
||||
c.JSON(500, types.ErrorResponse{Message: fmt.Sprintf("Card %s (%s) has Balance lower than overall Payment cost. Rollback.", card.Name, card.LastDigits)})
|
||||
weNeedRollback = true
|
||||
return
|
||||
}
|
||||
card.Balance -= substractFromCard
|
||||
if err := dbc.Save(card).Error; err != nil {
|
||||
c.JSON(500, types.ErrorResponse{Message: err.Error()})
|
||||
weNeedRollback = true
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, types.Message{Info: fmt.Sprintf("Entity with %d ID is created successfully!", payment.ID)})
|
||||
}
|
||||
|
||||
// @Summary Get all payments for user
|
||||
// @Description Get all payments for user
|
||||
// @Tags type
|
||||
// @Produce json
|
||||
// @Param Authorization header string true "Bearer token"
|
||||
// @Success 200 {object} []types.DbPayment
|
||||
// @Failure 401 {object} types.ErrorResponse
|
||||
// @Failure 500 {object} types.ErrorResponse
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /payment/all [get]
|
||||
func PaymentGetAll(c *gin.Context) {
|
||||
userID, err := GetUserID(c)
|
||||
if err != nil {
|
||||
c.JSON(500, types.ErrorResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
dbc := db.Connect()
|
||||
var entities []*db.Payment
|
||||
if err := dbc.Preload("Items.Item").Find(&entities, db.Payment{UserID: userID}).Error; err != nil {
|
||||
c.JSON(500, types.ErrorResponse{Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
var ret []types.DbPayment
|
||||
for _, entity := range entities {
|
||||
ret = append(ret, paymentTransform(entity))
|
||||
}
|
||||
c.JSON(200, ret)
|
||||
}
|
||||
|
|
|
@ -155,7 +155,7 @@ type DbCurrency struct {
|
|||
Symbol string `json:"symbol" example:"$"`
|
||||
}
|
||||
|
||||
type Payment struct {
|
||||
type DbPayment struct {
|
||||
ID uint `json:"id" example:"1"`
|
||||
CardID uint `json:"card_id" example:"1"`
|
||||
CategoryID uint `json:"category_id" example:"1"`
|
||||
|
@ -163,10 +163,10 @@ type Payment struct {
|
|||
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:"[]"`
|
||||
Items []DbItemBought `json:"items" example:"[]"`
|
||||
}
|
||||
|
||||
type ItemBought struct {
|
||||
type DbItemBought struct {
|
||||
ID uint `json:"id" example:"1"`
|
||||
NewName string `json:"new_name" example:"itemName"`
|
||||
NewComment string `json:"new_comment" example:"itemName"`
|
||||
|
@ -178,4 +178,5 @@ type ItemBought struct {
|
|||
TotalCost uint64 `json:"total_cost" example:"2050"`
|
||||
MetricType uint8 `json:"metric_type" example:"0"`
|
||||
MetricValue uint64 `json:"metric_value" example:"100"`
|
||||
Item DbItem `json:"itme"`
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user