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 := api.Group("/payment", middleware.AuthMiddleware())
|
||||||
{
|
{
|
||||||
paymentRoutes.POST("/add", handlers.PaymentAdd)
|
paymentRoutes.POST("/add", handlers.PaymentAdd)
|
||||||
|
paymentRoutes.GET("/all", handlers.PaymentGetAll)
|
||||||
}
|
}
|
||||||
currencyRoutes := api.Group("/currency", middleware.AuthMiddleware())
|
currencyRoutes := api.Group("/currency", middleware.AuthMiddleware())
|
||||||
{
|
{
|
||||||
|
|
|
@ -34,6 +34,7 @@ type Helper_ExpenseBulk struct {
|
||||||
UserID uint
|
UserID uint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// {{{ Helper_ExpenseBulk.CreateExpenseFromChild I'm not proud of this code
|
||||||
func (he *Helper_ExpenseBulk) CreateExpenseFromChild(c Expense) *Expense {
|
func (he *Helper_ExpenseBulk) CreateExpenseFromChild(c Expense) *Expense {
|
||||||
var cardID uint
|
var cardID uint
|
||||||
var typeID uint
|
var typeID uint
|
||||||
|
@ -80,6 +81,8 @@ func (he *Helper_ExpenseBulk) CreateExpenseFromChild(c Expense) *Expense {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// }}}
|
||||||
|
|
||||||
// Implements db.UserIdentifiable:1
|
// Implements db.UserIdentifiable:1
|
||||||
func (e Expense) GetID() uint {
|
func (e Expense) GetID() uint {
|
||||||
return e.ID
|
return e.ID
|
||||||
|
@ -96,7 +99,7 @@ func (e *Expense) SetUserID(id uint) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
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_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")
|
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
|
return err
|
||||||
}
|
}
|
||||||
if card.UserID != e.UserID {
|
if card.UserID != e.UserID {
|
||||||
return ERROR_EXPENSE_INVALID_USERID
|
return ERROR_EXPENSE_INVALID_CARD_USERID
|
||||||
}
|
}
|
||||||
if card.Balance < e.Value {
|
if card.Balance < e.Value {
|
||||||
return ERROR_EXPENSE_CARD_INSUFFICIENT_BALANCE
|
return ERROR_EXPENSE_CARD_INSUFFICIENT_BALANCE
|
||||||
|
@ -138,7 +141,7 @@ func (e *Expense) BeforeUpdate(tx *gorm.DB) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if oldCard.UserID != e.UserID {
|
if oldCard.UserID != e.UserID {
|
||||||
return ERROR_EXPENSE_INVALID_USERID
|
return ERROR_EXPENSE_INVALID_CARD_USERID
|
||||||
}
|
}
|
||||||
oldCard.Balance += original.Value
|
oldCard.Balance += original.Value
|
||||||
if err := tx.Save(oldCard).Error; err != nil {
|
if err := tx.Save(oldCard).Error; err != nil {
|
||||||
|
@ -152,7 +155,7 @@ func (e *Expense) BeforeUpdate(tx *gorm.DB) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if newCard.UserID != e.UserID {
|
if newCard.UserID != e.UserID {
|
||||||
return ERROR_EXPENSE_INVALID_USERID
|
return ERROR_EXPENSE_INVALID_CARD_USERID
|
||||||
}
|
}
|
||||||
if newCard.Balance < e.Value {
|
if newCard.Balance < e.Value {
|
||||||
return ERROR_EXPENSE_CARD_INSUFFICIENT_BALANCE
|
return ERROR_EXPENSE_CARD_INSUFFICIENT_BALANCE
|
||||||
|
@ -178,7 +181,7 @@ func (e *Expense) AfterDelete(tx *gorm.DB) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if card.UserID != e.UserID {
|
if card.UserID != e.UserID {
|
||||||
return ERROR_EXPENSE_INVALID_USERID
|
return ERROR_EXPENSE_INVALID_CARD_USERID
|
||||||
}
|
}
|
||||||
card.Balance += e.Value
|
card.Balance += e.Value
|
||||||
if err := tx.Save(card).Error; err != nil {
|
if err := tx.Save(card).Error; err != nil {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package db
|
package db
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
@ -23,3 +24,26 @@ type Payment struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Payment) __internalBelogingToPayment() {}
|
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"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
var itemBoughtTransform func(inp *db.ItemBought) types.ItemBought = func(inp *db.ItemBought) types.ItemBought {
|
var itemBoughtTransform func(inp *db.ItemBought) types.DbItemBought = func(inp *db.ItemBought) types.DbItemBought {
|
||||||
return types.ItemBought{}
|
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 paymentTransform func(inp *db.Payment) types.DbPayment = func(inp *db.Payment) types.DbPayment {
|
||||||
var items []types.ItemBought
|
var items []types.DbItemBought
|
||||||
for _, item := range inp.Items {
|
for _, item := range inp.Items {
|
||||||
items = append(items, itemBoughtTransform(&item))
|
items = append(items, itemBoughtTransform(&item))
|
||||||
}
|
}
|
||||||
return types.Payment{
|
return types.DbPayment{
|
||||||
ID: inp.ID,
|
ID: inp.ID,
|
||||||
CardID: inp.CardID,
|
CardID: inp.CardID,
|
||||||
CategoryID: inp.CategoryID,
|
CategoryID: inp.CategoryID,
|
||||||
|
@ -36,7 +53,7 @@ var paymentTransform func(inp *db.Payment) types.Payment = func(inp *db.Payment)
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param Authorization header string true "Bearer token"
|
// @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
|
// @Success 200 {object} types.Message
|
||||||
// @Failure 400 {object} types.ErrorResponse
|
// @Failure 400 {object} types.ErrorResponse
|
||||||
// @Failure 500 {object} types.ErrorResponse
|
// @Failure 500 {object} types.ErrorResponse
|
||||||
|
@ -49,7 +66,7 @@ func PaymentAdd(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var updates types.Payment
|
var updates types.DbPayment
|
||||||
if err := c.ShouldBindJSON(&updates); err != nil {
|
if err := c.ShouldBindJSON(&updates); err != nil {
|
||||||
log.Printf("err is %v\n", err)
|
log.Printf("err is %v\n", err)
|
||||||
c.JSON(400, types.ErrorResponse{Message: "Invalid request"})
|
c.JSON(400, types.ErrorResponse{Message: "Invalid request"})
|
||||||
|
@ -63,7 +80,7 @@ func PaymentAdd(c *gin.Context) {
|
||||||
if weNeedRollback {
|
if weNeedRollback {
|
||||||
dbc := db.Connect()
|
dbc := db.Connect()
|
||||||
for _, deleteIt := range deletableIfRollback {
|
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)
|
log.Printf("ERROR: dbc.Delete: %v\n", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -92,7 +109,9 @@ func PaymentAdd(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
deletableIfRollback = append(deletableIfRollback, payment)
|
deletableIfRollback = append(deletableIfRollback, payment)
|
||||||
|
var substractFromCard uint64 = 0
|
||||||
for _, uItemBought := range updates.Items {
|
for _, uItemBought := range updates.Items {
|
||||||
|
var totalCost uint64 = 0
|
||||||
// Creating item and adding it to rollback if itemID is set to 0
|
// Creating item and adding it to rollback if itemID is set to 0
|
||||||
if uItemBought.ItemID == 0 {
|
if uItemBought.ItemID == 0 {
|
||||||
newItem := &db.Item{
|
newItem := &db.Item{
|
||||||
|
@ -116,12 +135,13 @@ func PaymentAdd(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
deletableIfRollback = append(deletableIfRollback, newItem)
|
deletableIfRollback = append(deletableIfRollback, newItem)
|
||||||
|
totalCost = newItem.Price * uint64(uItemBought.Quantity)
|
||||||
newItemBought := &db.ItemBought{
|
newItemBought := &db.ItemBought{
|
||||||
ItemID: newItem.ID,
|
ItemID: newItem.ID,
|
||||||
PaymentID: payment.ID,
|
PaymentID: payment.ID,
|
||||||
TypeID: uItemBought.TypeID,
|
TypeID: uItemBought.TypeID,
|
||||||
Quantity: uItemBought.Quantity,
|
Quantity: uItemBought.Quantity,
|
||||||
TotalCost: newItem.Price * uint64(uItemBought.Quantity),
|
TotalCost: totalCost,
|
||||||
MetricType: uItemBought.MetricType,
|
MetricType: uItemBought.MetricType,
|
||||||
MetricValue: uItemBought.MetricValue,
|
MetricValue: uItemBought.MetricValue,
|
||||||
}
|
}
|
||||||
|
@ -138,9 +158,104 @@ func PaymentAdd(c *gin.Context) {
|
||||||
deletableIfRollback = append(deletableIfRollback, newItemBought)
|
deletableIfRollback = append(deletableIfRollback, newItemBought)
|
||||||
newItemBought.Item = newItem
|
newItemBought.Item = newItem
|
||||||
} else {
|
} 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)})
|
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:"$"`
|
Symbol string `json:"symbol" example:"$"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Payment struct {
|
type DbPayment struct {
|
||||||
ID uint `json:"id" example:"1"`
|
ID uint `json:"id" example:"1"`
|
||||||
CardID uint `json:"card_id" example:"1"`
|
CardID uint `json:"card_id" example:"1"`
|
||||||
CategoryID uint `json:"category_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$"`
|
Description string `json:"descr" example:"i bought some title for 20$"`
|
||||||
Note string `json:"note" example:"no i did not hit domain"`
|
Note string `json:"note" example:"no i did not hit domain"`
|
||||||
Date time.Time `json:"date" example:"29/11/2001 12:00"`
|
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"`
|
ID uint `json:"id" example:"1"`
|
||||||
NewName string `json:"new_name" example:"itemName"`
|
NewName string `json:"new_name" example:"itemName"`
|
||||||
NewComment string `json:"new_comment" example:"itemName"`
|
NewComment string `json:"new_comment" example:"itemName"`
|
||||||
|
@ -178,4 +178,5 @@ type ItemBought struct {
|
||||||
TotalCost uint64 `json:"total_cost" example:"2050"`
|
TotalCost uint64 `json:"total_cost" example:"2050"`
|
||||||
MetricType uint8 `json:"metric_type" example:"0"`
|
MetricType uint8 `json:"metric_type" example:"0"`
|
||||||
MetricValue uint64 `json:"metric_value" example:"100"`
|
MetricValue uint64 `json:"metric_value" example:"100"`
|
||||||
|
Item DbItem `json:"itme"`
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user