Added item managagement
the build was successful Details

+ Resolved quantity stuff - there now is a new table which links books or items to a quantity
This commit is contained in:
kolaente 2017-11-29 12:25:52 +01:00
parent c592de5daa
commit 3c08e18cd2
Signed by: konrad
GPG Key ID: F40E70337AB24C9B
18 changed files with 457 additions and 53 deletions

View File

@ -27,51 +27,6 @@ func (Book) TableName() string {
return "books"
}
// Quantity is the quantity for a book
type Quantity struct {
ID int64 `xorm:"int(11) autoincr not null unique pk"`
BookID int64 `xorm:"int(11) not null"`
Quantity int64 `xorm:"int(11) not null"`
Created int64 `xorm:"created"`
}
// TableName returns the name for the quantites table
func (Quantity) TableName() string {
return "books_quantities"
}
// GetQuantityByBook returns the current quantity for a book
func GetQuantityByBook(book Book) (quantity int64, err error) {
bq := Quantity{BookID: book.ID}
has, err := x.Desc("id").Get(&bq)
if err != nil {
return 0, err
}
if has {
quantity = bq.Quantity
}
return quantity, nil
}
// SetBookQuantity sets a new quantity for a book
func (book Book) setBookQuantity(quantity int64) (err error) {
// Check if the quantity already exists and only insert it if not
qty, err := GetQuantityByBook(book)
if err != nil {
return err
}
if qty != quantity {
q := Quantity{BookID: book.ID, Quantity: quantity}
_, err = x.Insert(q)
return err
}
return nil
}
// GetBookByID gets a Book by its ID
func GetBookByID(ID int64) (book Book, exists bool, err error) {
// Get the Book
@ -79,7 +34,7 @@ func GetBookByID(ID int64) (book Book, exists bool, err error) {
if has {
// Get the books quantity. We can't join it because xorm ignores the Quantity option in struct
book.Quantity, err = GetQuantityByBook(book)
book.Quantity, err = book.getQuantity()
if err != nil {
fmt.Println("Error getting quantity:", err)
}

View File

@ -75,7 +75,7 @@ func AddOrUpdateBook(book Book) (newBook Book, err error) {
}
// Set the Quantity
err = book.setBookQuantity(qty)
err = book.setQuantity(qty)
if err != nil {
return Book{}, err
}

View File

@ -18,6 +18,12 @@ func DeleteBookByID(id int64) error {
// Delete all authors associated with that book
_, err = x.Delete(&AuthorBook{BookID: id})
if err != nil {
return err
}
// Delete all quantites for this book
_, err = x.Delete(&Quantity{ItemID: id})
return err
}

View File

@ -29,7 +29,7 @@ func ListBooks(searchterm string) (books []*Book, err error) {
for i, book := range books {
// Get quantities
books[i].Quantity, err = GetQuantityByBook(*book)
books[i].Quantity, err = book.getQuantity()
if err != nil {
fmt.Println("Error getting quantity:", err)
}

31
models/item.go Normal file
View File

@ -0,0 +1,31 @@
package models
// Item holds an item
type Item struct {
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
Title string `xorm:"varchar(250)" json:"title"`
Price float64 `xorm:"double" json:"price"`
Description string `xorm:"varchar(750)" json:"description"`
Other string `xorm:"varchar(750)" json:"other"`
Created int64 `xorm:"created" json:"created"`
Updated int64 `xorm:"updated" json:"updated"`
Quantity int64 `xorm:"-" json:"quantity"`
}
// TableName returns the table name for items
func (Item) TableName() string {
return "items"
}
// GetItemByID returns an item by its ID
func GetItemByID(id int64) (item Item, exists bool, err error) {
exists, err = x.Id(id).Get(&item)
if err != nil {
return
}
item.Quantity, err = item.getQuantity()
return
}

View File

@ -0,0 +1,33 @@
package models
// AddOrUpdateItem adds or updates a item from a item struct
func AddOrUpdateItem(item Item) (newItem Item, err error) {
// save the quantity for later use
qty := item.Quantity
if item.ID == 0 {
if item.Title != "" { // Only insert it if the title is not empty
_, err = x.Insert(&item)
if err != nil {
return Item{}, err
}
}
} else {
_, err = x.ID(item.ID).Update(&item)
if err != nil {
return Item{}, err
}
}
// Set the Quantity
err = item.setQuantity(qty)
if err != nil {
return Item{}, err
}
newItem, _, err = GetItemByID(item.ID)
return newItem, err
}

23
models/items_delete.go Normal file
View File

@ -0,0 +1,23 @@
package models
import "fmt"
// DeleteItemByID deletes a item by its ID
func DeleteItemByID(id int64) error {
// Check if the id is 0
if id == 0 {
return fmt.Errorf("ID cannot be 0")
}
// Delete the item
_, err := x.Id(id).Delete(&Item{})
if err != nil {
return err
}
// Delete all quantites for this item
_, err = x.Delete(&Quantity{ItemID: id})
return err
}

28
models/items_list.go Normal file
View File

@ -0,0 +1,28 @@
package models
// ListItems returns a list with all items, filtered by an optional searchstring
func ListItems(searchterm string) (items []Item, err error) {
if searchterm == "" {
err = x.Find(&items)
} else {
err = x.
Where("title LIKE ?", "%"+searchterm+"%").
Find(&items)
}
if err != nil {
return []Item{}, err
}
// Range over all items and get their quantity.
// Yes I know how inperformant this is. But we can't use a join here because of the way xorm handels the quantity property.
for i, item := range items {
items[i].Quantity, err = item.getQuantity()
if err != nil {
return []Item{}, err
}
}
return items, nil
}

View File

@ -32,6 +32,8 @@ func SetEngine() (err error) {
x.Sync(&AuthorBook{})
x.Sync(&Status{})
x.Sync(&Quantity{})
x.Sync(&quantityRelation{})
x.Sync(&Item{})
x.ShowSQL(Config.Database.ShowQueries)

View File

@ -11,7 +11,7 @@ func AddOrUpdatePublisher(publisher Publisher) (newPublisher Publisher, err erro
}
}
} else {
_, err = x.Where("id = ?", publisher.ID).Update(&publisher)
_, err = x.ID(publisher.ID).Update(&publisher)
if err != nil {
return Publisher{}, err

160
models/quantity.go Normal file
View File

@ -0,0 +1,160 @@
package models
// Quantity is the quantity for a book
type Quantity struct {
ID int64 `xorm:"int(11) autoincr not null unique pk"`
ItemID int64 `xorm:"int(11) not null"`
Quantity int64 `xorm:"int(11) not null"`
Created int64 `xorm:"created"`
}
// TableName returns the name for the quantites table
func (Quantity) TableName() string {
return "quantities"
}
// Quantityrelations holds information about wherether the quantity we are currently dealing with is one of a book or an item
// We cannot directly link quantites to items and books because they can have the same id... which is why we need this.
type quantityRelation struct {
ID int64 `xorm:"int(11) autoincr not null unique pk"`
ItemID int64 `xorm:"int(11) not null"`
BookID int64 `xorm:"int(11) not null"`
Created int64 `xorm:"created"`
}
// TableName returns the name for the quantites table
func (quantityRelation) TableName() string {
return "quantity_relations"
}
// GetQuantity gets the latest quantity
func GetQuantity(itemID int64) (quantity int64, err error) {
// Return nothing if we dont have an item
if itemID == 0 {
return 0, nil
}
// Get the quanitty
bq := Quantity{ItemID: itemID}
has, err := x.Desc("id").Get(&bq)
if err != nil {
return 0, err
}
if has {
quantity = bq.Quantity
}
return quantity, nil
}
// SetQuantity sets the quantity for an item
func SetQuantity(itemID, quantity int64) (err error) {
// Check if the quantity already exists and only insert it if not
qty, err := GetQuantity(itemID)
if err != nil {
return err
}
if qty != quantity {
q := Quantity{ItemID: itemID, Quantity: quantity}
_, err = x.Insert(q)
return err
}
return nil
}
// ===== ITEMS =====
// Get item quantity with relation
func (item Item) getQuantity() (quantity int64, err error) {
// get the quantity relation for the item
qtyID, _, err := item.getQuantityRelation()
if err != nil {
return 0, err
}
return GetQuantity(qtyID)
}
func (item Item) getQuantityRelation() (qtyID int64, exists bool, err error) {
// get the quantity relation for the item
qty := quantityRelation{ItemID: item.ID}
has, err := x.Get(&qty)
if err != nil {
return 0, false, err
}
return qty.ID, has, nil
}
// Set item quantity with relation
func (item Item) setQuantity(quantity int64) (err error) {
// Check if the relation already exists, if not, create a new one
qtyItemID, exists, err := item.getQuantityRelation()
if err != nil {
return
}
if !exists {
rel := quantityRelation{ItemID: item.ID}
_, err := x.Insert(&rel)
if err != nil {
return err
}
qtyItemID = rel.ID
}
// Insert the new quantity
return SetQuantity(qtyItemID, quantity)
}
// ===== BOOKS =====
// Get book quantity with relation
func (book Book) getQuantity() (quantity int64, err error) {
// get the quantity relation for the item
qtyID, _, err := book.getQuantityRelation()
if err != nil {
return 0, err
}
return GetQuantity(qtyID)
}
// Get the quantity relation
func (book Book) getQuantityRelation() (qtyID int64, exists bool, err error) {
// get the quantity relation for the item
qty := quantityRelation{BookID: book.ID}
has, err := x.Get(&qty)
if err != nil {
return 0, false, err
}
return qty.ID, has, nil
}
// Set book quantity with relation
func (book Book) setQuantity(quantity int64) (err error) {
// Check if the relation already exists, if not, create a new one
qtyItemID, exists, err := book.getQuantityRelation()
if err != nil {
return
}
if !exists {
rel := quantityRelation{BookID: book.ID}
_, err := x.Insert(&rel)
if err != nil {
return err
}
qtyItemID = rel.ID
}
// Insert the new quantity
return SetQuantity(qtyItemID, quantity)
}

View File

@ -0,0 +1,58 @@
package v1
import (
"encoding/json"
"git.mowie.cc/konrad/Library/models"
"github.com/labstack/echo"
"net/http"
"strconv"
"strings"
)
type itemPayload struct {
Item models.Item `json:"item" form:"item"`
}
// ItemAddOrUpdate is the handler to add a item
func ItemAddOrUpdate(c echo.Context) error {
// Check for Request Content
itemFromString := c.FormValue("item")
var datItem models.Item
if itemFromString == "" {
b := new(itemPayload)
if err := c.Bind(b); err != nil {
return c.JSON(http.StatusBadRequest, models.Message{"No item model provided"})
}
datItem = b.Item
} else {
// Decode the JSON
dec := json.NewDecoder(strings.NewReader(itemFromString))
err := dec.Decode(&datItem)
if err != nil {
return c.JSON(http.StatusInternalServerError, models.Message{"Error decoding item: " + err.Error()})
}
}
// Check if we have an ID other than the one in the struct
id := c.Param("id")
if id != "" {
// Make int
itemID, err := strconv.ParseInt(id, 10, 64)
if err != nil {
return c.JSON(http.StatusInternalServerError, models.Message{"Could not get item id"})
}
datItem.ID = itemID
}
// Insert or update the item
newItem, err := models.AddOrUpdateItem(datItem)
if err != nil {
return c.JSON(http.StatusInternalServerError, models.Message{"Error"})
}
return c.JSON(http.StatusOK, newItem)
}

View File

@ -0,0 +1,41 @@
package v1
import (
"git.mowie.cc/konrad/Library/models"
"github.com/labstack/echo"
"net/http"
"strconv"
)
// ItemDelete is the handler to delete a item
func ItemDelete(c echo.Context) error {
id := c.Param("id")
// Make int
itemID, err := strconv.ParseInt(id, 10, 64)
if err != nil {
return c.JSON(http.StatusInternalServerError, models.Message{"Could not get item infos"})
}
// Check if the item exists
_, exists, err := models.GetItemByID(itemID)
if err != nil {
return c.JSON(http.StatusInternalServerError, models.Message{"Could get item"})
}
if !exists {
return c.JSON(http.StatusBadRequest, models.Message{"The item does not exist."})
}
// Delete it
err = models.DeleteItemByID(itemID)
if err != nil {
return c.JSON(http.StatusInternalServerError, models.Message{"Could not delete item"})
}
return c.JSON(http.StatusOK, models.Message{"success"})
}

View File

@ -0,0 +1,22 @@
package v1
import (
"git.mowie.cc/konrad/Library/models"
"github.com/labstack/echo"
"net/http"
)
// ItemsList is the handler to list Items, optionally filtered
func ItemsList(c echo.Context) error {
// Prepare the searchterm
search := c.QueryParam("s")
list, err := models.ListItems(search)
if err != nil {
return c.JSON(http.StatusInternalServerError, models.Message{"Error getting items"})
}
return c.JSON(http.StatusOK, list)
}

View File

@ -0,0 +1,38 @@
package v1
import (
"git.mowie.cc/konrad/Library/models"
"github.com/labstack/echo"
"net/http"
"strconv"
)
// ItemShow is the handler to show informations about a item
func ItemShow(c echo.Context) error {
item := c.Param("id")
if item == "" {
return c.JSON(http.StatusBadRequest, models.Message{"Item ID cannot be empty."})
}
// Make int
itemID, err := strconv.ParseInt(item, 10, 64)
if err != nil {
return c.JSON(http.StatusInternalServerError, models.Message{"Error getting item infos."})
}
// Get item Infos
itemInfos, exists, err := models.GetItemByID(itemID)
if err != nil {
return c.JSON(http.StatusInternalServerError, models.Message{"Error getting item infos."})
}
// Check if it exists
if !exists {
return c.JSON(http.StatusNotFound, models.Message{"Item not found."})
}
return c.JSON(http.StatusOK, itemInfos)
}

View File

@ -2,7 +2,6 @@ package v1
import (
"encoding/json"
"fmt"
"git.mowie.cc/konrad/Library/models"
"github.com/labstack/echo"
"net/http"
@ -23,7 +22,6 @@ func PublisherAddOrUpdate(c echo.Context) error {
if publisherFromString == "" {
b := new(publisherPayload)
if err := c.Bind(b); err != nil {
fmt.Println(err)
return c.JSON(http.StatusBadRequest, models.Message{"No publisher model provided"})
}
datPublisher = b.Publisher
@ -44,7 +42,7 @@ func PublisherAddOrUpdate(c echo.Context) error {
publisherID, err := strconv.ParseInt(id, 10, 64)
if err != nil {
return c.JSON(http.StatusInternalServerError, models.Message{"Could not get book id"})
return c.JSON(http.StatusInternalServerError, models.Message{"Could not get item id"})
}
datPublisher.ID = publisherID
}

View File

@ -74,6 +74,10 @@ func RegisterRoutes(e *echo.Echo) {
a.GET("/publishers/:id", apiv1.PublisherShow)
a.GET("/publishers/search", apiv1.PublisherSearch)
// Lookup Items
a.GET("/items", apiv1.ItemsList)
a.GET("/items/:id", apiv1.ItemShow)
// Lookup Status
a.GET("/status", apiv1.StatusListShow)
a.GET("/status/:id", apiv1.StatusByIDShow)
@ -98,6 +102,11 @@ func RegisterRoutes(e *echo.Echo) {
a.DELETE("/publishers/:id", apiv1.PublisherDelete)
a.POST("/publishers/:id", apiv1.PublisherAddOrUpdate)
// Manage Items
a.PUT("/items", apiv1.ItemAddOrUpdate)
a.DELETE("/items/:id", apiv1.ItemDelete)
a.POST("/items/:id", apiv1.ItemAddOrUpdate)
// Manage Users
/*
@ -111,7 +120,7 @@ func RegisterRoutes(e *echo.Echo) {
POST /logout - ausloggen
GET /books/:id - Buch anzeigen
POST /books/:id - |Buch bearbeiten (inkl mengen)
POST /books/:id - |Buch bearbeiten (inkl mengen)
DELETE /books/:id - |Buch löschen (+alle einträge in authors_books löschen dessen Bush dazu gehört)
GET /books/search?s=se - Suchen
GET /books/list - Auflisten