diff --git a/controllers/configController.go b/controllers/configController.go new file mode 100644 index 0000000..2b3e7fa --- /dev/null +++ b/controllers/configController.go @@ -0,0 +1,350 @@ +package controllers + +import ( + "fmt" + "net/http" + + "github.com/gin-gonic/gin" + + "git.dynamicdiscord.de/kalipso/zineshop/models" + //"git.dynamicdiscord.de/kalipso/zineshop/services" + "git.dynamicdiscord.de/kalipso/zineshop/repositories" +) + +type ConfigController interface { + AddConfigHandler(*gin.Context) + ConfigHandler(*gin.Context) + ConfigView(*gin.Context) + + GetAllPaper(*gin.Context) + PaperView(*gin.Context) + PaperHandler(*gin.Context) + AddPaperHandler(*gin.Context) + + InvoiceView(*gin.Context) + InvoiceHandler(*gin.Context) + + CreateTag(*gin.Context) + GetAllTags(*gin.Context) + TagView(*gin.Context) + TagHandler(*gin.Context) + AddTagHandler(*gin.Context) +} + +type configController struct{} + +func NewConfigController() ConfigController { + return &configController{} +} + +func (rc *configController) AddConfigHandler(c *gin.Context) { + key := c.PostForm("key") + value := c.PostForm("value") + + if key == "" || value == "" { + err := "Key or Value empty during config creation" + fmt.Println(err) + c.HTML(http.StatusBadRequest, "configview.html", gin.H{"error": err}) + return + } + + config := models.Config{ + Key: key, + Value: value, + } + + _, err := repositories.ConfigOptions.Create(config) + if err != nil { + data := CreateSessionData(c, gin.H{ + "error": err, + "success": "", + }) + + c.HTML(http.StatusOK, "configview.html", data) + return + } + + rc.ConfigView(c) +} + +func (rc *configController) ConfigView(c *gin.Context) { + configOptions, err := repositories.ConfigOptions.GetAll() + + if err != nil { + c.HTML(http.StatusBadRequest, "configview.html", gin.H{"data": gin.H{"error": err}}) + } + + data := CreateSessionData(c, gin.H{ + "configOptions": configOptions, + }) + + if err != nil { + c.HTML(http.StatusBadRequest, "configview.html", data) + } + + c.HTML(http.StatusOK, "configview.html", data) +} + +func (rc *configController) ConfigHandler(ctx *gin.Context) { + key := ctx.PostForm("key") + value := ctx.PostForm("value") + action := ctx.PostForm("action") + + config, err := repositories.ConfigOptions.GetById(ctx.Param("id")) + + if err != nil { + fmt.Println(err) + ctx.HTML(http.StatusBadRequest, "configview.html", gin.H{"error": err}) + return + } + + if action == "update" { + config.Key = key + config.Value = value + config, err = repositories.ConfigOptions.Update(config) + + if err != nil { + fmt.Println(err) + ctx.HTML(http.StatusBadRequest, "configview.html", gin.H{"error": err}) + return + } + } + + if action == "delete" { + repositories.ConfigOptions.DeleteById(ctx.Param("id")) + } + + rc.ConfigView(ctx) +} + +func (rc *configController) PaperHandler(ctx *gin.Context) { + newPaper, err := models.NewPaper(ctx) + action := ctx.PostForm("action") + + if err != nil { + fmt.Println(err) + ctx.HTML(http.StatusBadRequest, "paperview.html", gin.H{"error": err}) + return + } + + paper, err := repositories.Papers.GetById(ctx.Param("id")) + + if err != nil { + fmt.Println(err) + ctx.HTML(http.StatusBadRequest, "paperview.html", gin.H{"error": err}) + return + } + + if action == "update" { + paper.Name = newPaper.Name + paper.Brand = newPaper.Brand + paper.Size = newPaper.Size + paper.Weight = newPaper.Weight + paper.Price = newPaper.Price + paper, err = repositories.Papers.Update(paper) + + if err != nil { + fmt.Println(err) + ctx.HTML(http.StatusBadRequest, "paperview.html", gin.H{"error": err}) + return + } + } + + if action == "delete" { + repositories.Papers.DeleteById(ctx.Param("id")) + } + + rc.PaperView(ctx) +} + +func (rc *configController) AddPaperHandler(c *gin.Context) { + paper, err := models.NewPaper(c) + + if err != nil { + fmt.Println(err) + c.HTML(http.StatusBadRequest, "paperview.html", gin.H{"error": err}) + return + } + + _, err = repositories.Papers.Create(paper) + if err != nil { + data := CreateSessionData(c, gin.H{ + "error": err, + "success": "", + }) + + c.HTML(http.StatusOK, "paperview.html", data) + return + } + + rc.PaperView(c) +} + +func (rc *configController) PaperView(c *gin.Context) { + papers, err := repositories.Papers.GetAll() + + if err != nil { + c.HTML(http.StatusBadRequest, "paperview.html", gin.H{"data": gin.H{"error": err}}) + } + + data := CreateSessionData(c, gin.H{ + "paper": papers, + }) + + if err != nil { + c.HTML(http.StatusBadRequest, "paperview.html", data) + } + + c.HTML(http.StatusOK, "paperview.html", data) +} + +func (rc *configController) GetAllPaper(c *gin.Context) { + papers, err := repositories.Papers.GetAll() + + if err != nil { + ReplyError(c, fmt.Errorf("Could not query Papers")) + return + } + + c.JSON(http.StatusOK, papers) +} + +////////////////////////////////////////////////////////////////////// + +func (rc *configController) InvoiceView(c *gin.Context) { + invoices, err := repositories.Invoices.GetAllNewestFirst() + + if err != nil { + c.HTML(http.StatusBadRequest, "invoiceview.html", gin.H{"data": gin.H{"error": err}}) + } + + data := CreateSessionData(c, gin.H{ + "invoices": invoices, + }) + + if err != nil { + c.HTML(http.StatusBadRequest, "invoiceview.html", data) + } + + c.HTML(http.StatusOK, "invoiceview.html", data) +} + +func (rc *configController) InvoiceHandler(ctx *gin.Context) { + action := ctx.PostForm("action") + if action == "delete" { + repositories.Invoices.DeleteById(ctx.Param("id")) + } + + rc.InvoiceView(ctx) +} + +////////////////////////////////////////////////////////////////////// + +func (rc *configController) TagHandler(ctx *gin.Context) { + name := ctx.PostForm("name") + color := ctx.PostForm("color") + action := ctx.PostForm("action") + + tag, err := repositories.Tags.GetById(ctx.Param("id")) + + if err != nil { + fmt.Println(err) + ctx.HTML(http.StatusBadRequest, "tagview.html", gin.H{"error": err}) + return + } + + if action == "update" { + tag.Name = name + tag.Color = color + tag, err = repositories.Tags.Update(tag) + + if err != nil { + fmt.Println(err) + ctx.HTML(http.StatusBadRequest, "tagview.html", gin.H{"error": err}) + return + } + } + + if action == "delete" { + repositories.Tags.DeleteById(ctx.Param("id")) + } + + rc.TagView(ctx) +} + +func (rc *configController) AddTagHandler(c *gin.Context) { + tag, err := models.NewTag(c) + + if err != nil { + fmt.Println(err) + c.HTML(http.StatusBadRequest, "tagview.html", gin.H{"error": err}) + return + } + + _, err = repositories.Tags.Create(tag) + if err != nil { + data := CreateSessionData(c, gin.H{ + "error": err, + "success": "", + }) + + c.HTML(http.StatusOK, "tagview.html", data) + return + } + + rc.TagView(c) +} + +func (rc *configController) TagView(c *gin.Context) { + tags, err := repositories.Tags.GetAll() + + if err != nil { + c.HTML(http.StatusBadRequest, "tagview.html", gin.H{"data": gin.H{"error": err}}) + } + + data := CreateSessionData(c, gin.H{ + "tags": tags, + }) + + if err != nil { + c.HTML(http.StatusBadRequest, "tagview.html", data) + } + + c.HTML(http.StatusOK, "tagview.html", data) +} + +func (rc *configController) CreateTag(c *gin.Context) { + tag, err := models.NewTagByJson(c) + + if err != nil { + ReplyError(c, err) + } + + _, err = repositories.Tags.Create(tag) + if err != nil { + ReplyError(c, fmt.Errorf("tag creation failed: %s", err)) + return + } + + //userID := user.(models.User).ID + //rc.DB.Model(&models.shopItem{}).Where("id = ?"), room.ID).Association("Admins").Append(&models.User{ID: userID}) + + //if result.Error != nil { + // ReplyError(c, fmt.Errorf("shopItem creation failed: %s", result.Error)) + // return + //} + + ReplyOK(c, fmt.Sprintf("tag '%s' was created", tag.Name)) + +} + +func (rc *configController) GetAllTags(c *gin.Context) { + tags, err := repositories.Tags.GetAll() + + if err != nil { + ReplyError(c, fmt.Errorf("Could not query Tags")) + return + } + + c.JSON(http.StatusOK, tags) +} diff --git a/controllers/printController.go b/controllers/printController.go index f917cd2..d8c6bb5 100644 --- a/controllers/printController.go +++ b/controllers/printController.go @@ -63,7 +63,14 @@ func (rc *printController) PrintCartView(c *gin.Context) { return } + paper, err := repositories.Papers.GetAll() + if err != nil { + c.HTML(http.StatusBadRequest, "error.html", gin.H{"data": gin.H{"error": err}}) + return + } + data := CreateSessionData(c, gin.H{ + "paper": paper, "cartItems": cartItems, }) @@ -78,9 +85,16 @@ func (rc *printController) PrintOrderView(c *gin.Context) { return } + paper, err := repositories.Papers.GetAll() + if err != nil { + c.HTML(http.StatusBadRequest, "error.html", gin.H{"data": gin.H{"error": err}}) + return + } + cartItems := order.CartItems data := CreateSessionData(c, gin.H{ + "paper": paper, "cartItems": cartItems, }) @@ -90,6 +104,7 @@ func (rc *printController) PrintOrderView(c *gin.Context) { func (rc *printController) PrintHandler(c *gin.Context) { variantIds := c.PostFormArray("variant-id[]") variantAmounts := c.PostFormArray("variant-amount[]") + variantPapertypes := c.PostFormArray("variant-papertype[]") variantCoverPages := c.PostFormArray("variant-coverpage[]") if len(variantIds) != len(variantAmounts) || len(variantIds) != len(variantCoverPages) { @@ -98,6 +113,7 @@ func (rc *printController) PrintHandler(c *gin.Context) { } var printJobs []models.PrintJob + priceTotal := 0.0 for idx := range variantIds { variant, err := repositories.ShopItems.GetVariantById(variantIds[idx]) @@ -113,9 +129,21 @@ func (rc *printController) PrintHandler(c *gin.Context) { return } - coverPage := false - if variantCoverPages[idx] == "1" { - coverPage = true + paperType, err := repositories.Papers.GetById(fmt.Sprintf("%v", variantPapertypes[idx])) + if err != nil { + c.HTML(http.StatusBadRequest, "error.html", gin.H{"data": gin.H{"error": err}}) + return + } + + var coverPage *models.Paper + if variantCoverPages[idx] != "0" { + coverPageTmp, err := repositories.Papers.GetById(fmt.Sprintf("%v", variantCoverPages[idx])) + if err != nil { + c.HTML(http.StatusBadRequest, "error.html", gin.H{"data": gin.H{"error": err}}) + return + } + + coverPage = &coverPageTmp } variantAmount, err := strconv.Atoi(variantAmounts[idx]) @@ -124,23 +152,56 @@ func (rc *printController) PrintHandler(c *gin.Context) { return } - printJob, err := models.NewPrintJob(shopItem, variant, coverPage, uint(variantAmount)) + fmt.Println("Printing Costs:") + + printJob, err := models.NewPrintJob(shopItem, variant, paperType, coverPage, uint(variantAmount)) if err != nil { c.HTML(http.StatusBadRequest, "error.html", gin.H{"data": gin.H{"error": err}}) return } - + printJob.CalculatePrintCosts() + priceTotal += printJob.PriceTotal printJobs = append(printJobs, printJob) } + invoice := models.Invoice{ + PrintJobs: printJobs, + PricePerClick: 0.002604, + PartCosts: 0.0067, + PriceTotal: priceTotal, + } + invoice, err := repositories.Invoices.Create(invoice) + + if err != nil { + c.HTML(http.StatusBadRequest, "error.html", gin.H{"data": gin.H{"error": err}}) + return + } + executeJobs := func() { for _, printJob := range printJobs { - printJob.Execute() + err := printJob.Execute() + + if err == nil { + return + } + + printJob.ShopItem.WasPrinted = true + _, err = repositories.ShopItems.Update(printJob.ShopItem) + + if err != nil { + fmt.Printf("Error: %s\n", err) + } + } } go executeJobs() - c.HTML(http.StatusOK, "index.html", nil) + fmt.Println(invoice) + data := CreateSessionData(c, gin.H{ + "invoice": invoice, + }) + + c.HTML(http.StatusOK, "printstarted.html", data) } diff --git a/controllers/shopItemController.go b/controllers/shopItemController.go index a0da554..7649709 100644 --- a/controllers/shopItemController.go +++ b/controllers/shopItemController.go @@ -29,15 +29,10 @@ type ShopItemController interface { AddItemHandler(*gin.Context) AddItemsView(*gin.Context) AddItemsHandler(*gin.Context) - CreateTag(*gin.Context) - GetAllTags(*gin.Context) EditItemView(*gin.Context) EditItemHandler(*gin.Context) DeleteItemView(*gin.Context) DeleteItemHandler(*gin.Context) - TagView(*gin.Context) - TagHandler(*gin.Context) - AddTagHandler(*gin.Context) } type shopItemController struct{} @@ -166,6 +161,7 @@ func (rc *shopItemController) NewShopItemFromForm(ctx *gin.Context) (models.Shop Pdf: dstPdf, Variants: variants, PrintMode: printMode, + WasPrinted: false, } fmt.Println("Creating Shopitem: ", shopItem) @@ -405,6 +401,7 @@ func (rc *shopItemController) AddItemsHandler(c *gin.Context) { Pdf: dstPdf, Variants: variants, PrintMode: "CreateBooklet", + WasPrinted: false, } _, err = repositories.ShopItems.Create(shopItem) @@ -542,6 +539,7 @@ func (rc *shopItemController) EditItemHandler(c *gin.Context) { newShopItem.Variants = shopItem.Variants newShopItem.BasePrice = shopItem.BasePrice newShopItem.IsPublic = shopItem.IsPublic + newShopItem.WasPrinted = false if len(shopItem.Tags) != 0 { newShopItem.Tags = shopItem.Tags @@ -621,115 +619,6 @@ func (rc *shopItemController) DeleteItemHandler(c *gin.Context) { c.HTML(http.StatusOK, "index.html", data) } -func (rc *shopItemController) TagHandler(ctx *gin.Context) { - name := ctx.PostForm("name") - color := ctx.PostForm("color") - action := ctx.PostForm("action") - - tag, err := repositories.Tags.GetById(ctx.Param("id")) - - if err != nil { - fmt.Println(err) - ctx.HTML(http.StatusBadRequest, "tagview.html", gin.H{"error": err}) - return - } - - if action == "update" { - tag.Name = name - tag.Color = color - tag, err = repositories.Tags.Update(tag) - - if err != nil { - fmt.Println(err) - ctx.HTML(http.StatusBadRequest, "tagview.html", gin.H{"error": err}) - return - } - } - - if action == "delete" { - repositories.Tags.DeleteById(ctx.Param("id")) - } - - rc.TagView(ctx) -} - -func (rc *shopItemController) AddTagHandler(c *gin.Context) { - tag, err := models.NewTag(c) - - if err != nil { - fmt.Println(err) - c.HTML(http.StatusBadRequest, "tagview.html", gin.H{"error": err}) - return - } - - _, err = repositories.Tags.Create(tag) - if err != nil { - data := CreateSessionData(c, gin.H{ - "error": err, - "success": "", - }) - - c.HTML(http.StatusOK, "tagview.html", data) - return - } - - rc.TagView(c) -} - -func (rc *shopItemController) TagView(c *gin.Context) { - tags, err := repositories.Tags.GetAll() - - if err != nil { - c.HTML(http.StatusBadRequest, "tagview.html", gin.H{"data": gin.H{"error": err}}) - } - - data := CreateSessionData(c, gin.H{ - "tags": tags, - }) - - if err != nil { - c.HTML(http.StatusBadRequest, "tagview.html", data) - } - - c.HTML(http.StatusOK, "tagview.html", data) -} - -func (rc *shopItemController) CreateTag(c *gin.Context) { - tag, err := models.NewTagByJson(c) - - if err != nil { - ReplyError(c, err) - } - - _, err = repositories.Tags.Create(tag) - if err != nil { - ReplyError(c, fmt.Errorf("shopItem creation failed: %s", err)) - return - } - - //userID := user.(models.User).ID - //rc.DB.Model(&models.shopItem{}).Where("id = ?"), room.ID).Association("Admins").Append(&models.User{ID: userID}) - - //if result.Error != nil { - // ReplyError(c, fmt.Errorf("shopItem creation failed: %s", result.Error)) - // return - //} - - ReplyOK(c, fmt.Sprintf("tag '%s' was created", tag.Name)) - -} - -func (rc *shopItemController) GetAllTags(c *gin.Context) { - tags, err := repositories.Tags.GetAll() - - if err != nil { - ReplyError(c, fmt.Errorf("Could not query Tags")) - return - } - - c.JSON(http.StatusOK, tags) -} - func ReplyError(ctx *gin.Context, err error) { ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) } diff --git a/go.mod b/go.mod index 5dd1a6c..3345190 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,8 @@ require ( github.com/gin-gonic/gin v1.10.0 github.com/golang-jwt/jwt/v5 v5.2.1 github.com/joho/godotenv v1.5.1 - golang.org/x/crypto v0.23.0 + github.com/pdfcpu/pdfcpu v0.11.0 + golang.org/x/crypto v0.38.0 gorm.io/driver/sqlite v1.5.7 gorm.io/gorm v1.25.12 ) @@ -22,22 +23,30 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.20.0 // indirect github.com/goccy/go-json v0.10.2 // indirect + github.com/hhrutter/lzw v1.0.0 // indirect + github.com/hhrutter/pkcs7 v0.2.0 // indirect + github.com/hhrutter/tiff v1.0.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect golang.org/x/arch v0.8.0 // indirect + golang.org/x/image v0.27.0 // indirect golang.org/x/net v0.25.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/text v0.25.0 // indirect google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 030a4a2..69c7fbd 100644 --- a/go.sum +++ b/go.sum @@ -30,6 +30,12 @@ github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVI github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/hhrutter/lzw v1.0.0 h1:laL89Llp86W3rRs83LvKbwYRx6INE8gDn0XNb1oXtm0= +github.com/hhrutter/lzw v1.0.0/go.mod h1:2HC6DJSn/n6iAZfgM3Pg+cP1KxeWc3ezG8bBqW5+WEo= +github.com/hhrutter/pkcs7 v0.2.0 h1:i4HN2XMbGQpZRnKBLsUwO3dSckzgX142TNqY/KfXg+I= +github.com/hhrutter/pkcs7 v0.2.0/go.mod h1:aEzKz0+ZAlz7YaEMY47jDHL14hVWD6iXt0AgqgAvWgE= +github.com/hhrutter/tiff v1.0.2 h1:7H3FQQpKu/i5WaSChoD1nnJbGx4MxU5TlNqqpxw55z8= +github.com/hhrutter/tiff v1.0.2/go.mod h1:pcOeuK5loFUE7Y/WnzGw20YxUdnqjY1P0Jlcieb/cCw= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= @@ -46,6 +52,8 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -53,10 +61,17 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pdfcpu/pdfcpu v0.11.0 h1:mL18Y3hSHzSezmnrzA21TqlayBOXuAx7BUzzZyroLGM= +github.com/pdfcpu/pdfcpu v0.11.0/go.mod h1:F1ca4GIVFdPtmgvIdvXAycAm88noyNxZwzr9CpTy+Mw= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -76,22 +91,26 @@ github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZ golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w= +golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index d603f48..d5f7734 100644 --- a/main.go +++ b/main.go @@ -19,6 +19,7 @@ var ( userController controllers.UserController = controllers.UserController{} cartItemController controllers.CartItemController = controllers.NewCartItemController() printController controllers.PrintController = controllers.NewPrintController() + configController controllers.ConfigController = controllers.NewConfigController() authValidator middlewares.AuthValidator = middlewares.AuthValidator{} ) @@ -67,10 +68,23 @@ func main() { viewRoutes.GET("/cart/print", authValidator.RequireAdmin, printController.PrintCartView) viewRoutes.POST("/print", authValidator.RequireAdmin, printController.PrintHandler) - viewRoutes.GET("/tags", authValidator.RequireAdmin, shopItemController.TagView) - viewRoutes.POST("/tags/:id", authValidator.RequireAdmin, shopItemController.TagHandler) + viewRoutes.GET("/config", authValidator.RequireAdmin, configController.ConfigView) + viewRoutes.POST("/config/:id", authValidator.RequireAdmin, configController.ConfigHandler) + viewRoutes.POST("/config", authValidator.RequireAdmin, configController.AddConfigHandler) + + viewRoutes.GET("/tags", authValidator.RequireAdmin, configController.TagView) + viewRoutes.POST("/tags/:id", authValidator.RequireAdmin, configController.TagHandler) viewRoutes.GET("/tags/:id", userController.TagView) - viewRoutes.POST("/tags", authValidator.RequireAdmin, shopItemController.AddTagHandler) + viewRoutes.POST("/tags", authValidator.RequireAdmin, configController.AddTagHandler) + + viewRoutes.GET("/paper", authValidator.RequireAdmin, configController.PaperView) + viewRoutes.POST("/paper/:id", authValidator.RequireAdmin, configController.PaperHandler) + viewRoutes.GET("/paper/:id", userController.TagView) + viewRoutes.POST("/paper", authValidator.RequireAdmin, configController.AddPaperHandler) + + viewRoutes.GET("/invoice", authValidator.RequireAdmin, configController.InvoiceView) + viewRoutes.POST("/invoice/:id", authValidator.RequireAdmin, configController.InvoiceHandler) + viewRoutes.GET("/cart", authValidator.RequireAuth, cartItemController.CartItemView) viewRoutes.POST("/cart", authValidator.RequireAuth, cartItemController.AddItemHandler) viewRoutes.POST("/cart/delete", authValidator.RequireAuth, cartItemController.DeleteItemHandler) diff --git a/models/config.go b/models/config.go new file mode 100644 index 0000000..59c8a1d --- /dev/null +++ b/models/config.go @@ -0,0 +1,11 @@ +package models + +import ( + "gorm.io/gorm" +) + +type Config struct { + gorm.Model + Key string `json:"key" binding:"required" gorm:"unique;not null"` + Value string `json:"value" binding:"required"` +} diff --git a/models/paper.go b/models/paper.go new file mode 100644 index 0000000..ab374c5 --- /dev/null +++ b/models/paper.go @@ -0,0 +1,83 @@ +package models + +import ( + "fmt" + "github.com/gin-gonic/gin" + "gorm.io/gorm" + "strconv" + "strings" +) + +type PaperSize string + +const ( + A3 PaperSize = "A3" + A4 PaperSize = "A4" + A5 PaperSize = "A5" + SRA3 PaperSize = "SRA3" +) + +func ParseSize(s string) (c PaperSize, err error) { + s = strings.ToUpper(s) + if s == "A3" { + return A3, nil + } else if s == "A4" { + return A4, nil + } else if s == "A5" { + return A5, nil + } else if s == "SRA3" { + return SRA3, nil + } + + return c, fmt.Errorf("Cannot parse category %s", s) +} + +type Paper struct { + gorm.Model + Name string `json:"name" binding:"required" gorm:"not null"` + Brand string `json:"brand" binding:"required"` + Size PaperSize `json:"size" binding:"required"` + Weight int `json:"weight" binding:"required"` + Price float64 `json:"price" binding:"required"` +} + +func NewPaper(ctx *gin.Context) (Paper, error) { + name := ctx.PostForm("name") + brand := ctx.PostForm("brand") + sizeTmp := ctx.PostForm("size") + weightTmp := ctx.PostForm("weight") + priceTmp := ctx.PostForm("price") + + size, err := ParseSize(sizeTmp) + + if err != nil { + return Paper{}, fmt.Errorf("Couldnt parse Size") + } + + weight, err := strconv.Atoi(weightTmp) + + if err != nil { + return Paper{}, fmt.Errorf("Couldnt parse Weight") + } + + price, err := strconv.ParseFloat(priceTmp, 64) + + if err != nil { + return Paper{}, fmt.Errorf("Couldnt parse Price") + } + + if name == "" || brand == "" { + return Paper{}, fmt.Errorf("Name or brand empty") + } + + // Convert the price string to float64 + tag := Paper{ + Name: name, + Brand: brand, + Size: size, + Weight: weight, + Price: price, + } + + return tag, nil +} diff --git a/models/printer.go b/models/printer.go index 994f08e..98885d1 100644 --- a/models/printer.go +++ b/models/printer.go @@ -2,6 +2,9 @@ package models import ( "fmt" + "git.dynamicdiscord.de/kalipso/zineshop/utils" + "gorm.io/gorm" + "math" "os/exec" "strings" ) @@ -18,12 +21,36 @@ const ( TriFold PrintOption = "-o Fold=TriFold -o Binding=TopBinding" ) -type PrintJob struct { +type OldPrintJob struct { Pdf string Amount uint Options []PrintOption } +type Invoice struct { + gorm.Model + PrintJobs []PrintJob + PricePerClick float64 + PartCosts float64 + PriceTotal float64 +} + +type PrintJob struct { + gorm.Model + ShopItemID uint + ShopItem ShopItem + VariantID uint + Variant ItemVariant + PaperTypeId uint + PaperType Paper `gorm:"foreignKey:PaperTypeId"` + CoverPaperTypeId *uint + CoverPaperType *Paper `gorm:"foreignKey:CoverPaperTypeId"` + Amount uint + PricePerPiece float64 + PriceTotal float64 + InvoiceID uint +} + func GetPrintMode(mode string) PrintOption { if mode == "LongEdge" { return LongEdge @@ -40,7 +67,7 @@ func GetPrintMode(mode string) PrintOption { return CreateBooklet } -func NewPrintJob(shopItem ShopItem, variant ItemVariant, coverPage bool, amount uint) (PrintJob, error) { +func NewPrintJob(shopItem ShopItem, variant ItemVariant, paperType Paper, coverPaperType *Paper, amount uint) (PrintJob, error) { if shopItem.Pdf == "" { return PrintJob{}, fmt.Errorf("ShopItem has no PDF assigned") } @@ -49,32 +76,44 @@ func NewPrintJob(shopItem ShopItem, variant ItemVariant, coverPage bool, amount return PrintJob{}, fmt.Errorf("Amount to big. This is denied for security reasons") } - var result PrintJob - result.Pdf = shopItem.Pdf - result.Amount = amount - - if variant.Name == "Colored" { - result.Options = append(result.Options, Colored) + result := PrintJob{ + ShopItem: shopItem, + Variant: variant, + PaperType: paperType, + CoverPaperType: coverPaperType, + Amount: amount, } - if coverPage { - result.Options = append(result.Options, CoverPage) - } - - result.Options = append(result.Options, GetPrintMode(shopItem.PrintMode)) - return result, nil } +func (p *PrintJob) IsColored() bool { + return p.Variant.Name == "Colored" +} + +func (p *PrintJob) GeneratePrintOptions() []PrintOption { + var result []PrintOption + if p.Variant.Name == "Colored" { + result = append(result, Colored) + } + + if p.CoverPaperType != nil { + result = append(result, CoverPage) + } + + result = append(result, GetPrintMode(p.ShopItem.PrintMode)) + return result +} + func (p *PrintJob) Execute() error { baseCommand := "lp -d KonicaBooklet" baseCommand += fmt.Sprintf(" -n %v ", p.Amount) - for _, option := range p.Options { + for _, option := range p.GeneratePrintOptions() { baseCommand += fmt.Sprintf(" %v ", option) } - baseCommand += fmt.Sprintf(" -- %s", p.Pdf) + baseCommand += fmt.Sprintf(" -- %s", p.ShopItem.Pdf) parts := strings.Fields(baseCommand) @@ -91,3 +130,51 @@ func (p *PrintJob) Execute() error { fmt.Printf("Output:\n%s\n", output) return nil } + +func (p *PrintJob) CalculatePrintCosts() (float64, error) { + pageCount := utils.CountPagesAtPath(p.ShopItem.Pdf) + + if pageCount == 0 { + fmt.Println("Pagecount of 0 - something is wrong here.") + return 0, fmt.Errorf("Cant calculate price, pdf seems to be empty") + } + + printMode := GetPrintMode(p.ShopItem.PrintMode) + + //Get actual pagecount depending on printmode + actualPageCount := pageCount + fmt.Println("PagCount: ", actualPageCount) + + if printMode == CreateBooklet { + dividedCount := float64(pageCount) / 4.0 + actualPageCount = int(math.Ceil(dividedCount)) + } + + if printMode == LongEdge || printMode == ShortEdge { + dividedCount := float64(pageCount) / 2.0 + actualPageCount = int(math.Ceil(dividedCount)) + } + + PPC := 0.002604 + partCost := 0.0067 + if p.IsColored() { + partCost = 0.0478 + } + + printingCosts := float64(actualPageCount-1) * p.PaperType.Price + + if p.CoverPaperType != nil { + printingCosts += p.CoverPaperType.Price + } else { + printingCosts += p.PaperType.Price + } + + printingCosts += float64(actualPageCount/2) * PPC + printingCosts += partCost * float64(actualPageCount) + + fmt.Printf("Printing Costs per Zine: %v\n", printingCosts) + fmt.Printf("Printing Costs Total: %v\n", printingCosts*float64(p.Amount)) + p.PricePerPiece = printingCosts + p.PriceTotal = printingCosts * float64(p.Amount) + return printingCosts, nil +} diff --git a/models/shopItem.go b/models/shopItem.go index 55e49de..8146878 100644 --- a/models/shopItem.go +++ b/models/shopItem.go @@ -43,7 +43,7 @@ type ItemVariant struct { type ShopItem struct { gorm.Model Name string `json:"name" binding:"required" gorm:"unique;not null"` - Abstract string `json:"Abstract" binding:"required"` + Abstract string `json:"abstract" binding:"required"` Description string `json:"description" binding:"required"` Category Category `json:"category"` Variants []ItemVariant `json:"variant"` @@ -53,4 +53,5 @@ type ShopItem struct { Image string Pdf string PrintMode string `json:"printMode" gorm:"default:CreateBooklet"` + WasPrinted bool `gorm:"default:false"` } diff --git a/repositories/InvoiceRepository.go b/repositories/InvoiceRepository.go new file mode 100644 index 0000000..843882c --- /dev/null +++ b/repositories/InvoiceRepository.go @@ -0,0 +1,100 @@ +package repositories + +import ( + "strconv" + + "gorm.io/gorm" + + "git.dynamicdiscord.de/kalipso/zineshop/models" +) + +type InvoiceRepository interface { + Create(models.Invoice) (models.Invoice, error) + GetAll() ([]models.Invoice, error) + GetAllSorted(string) ([]models.Invoice, error) + GetAllNewestFirst() ([]models.Invoice, error) + GetAllNewestLast() ([]models.Invoice, error) + GetById(string) (models.Invoice, error) + //GetByInvoiceId(string) (models.Invoice, error) + Update(models.Invoice) (models.Invoice, error) + DeleteById(string) error +} + +type GORMInvoiceRepository struct { + DB *gorm.DB +} + +func NewGORMInvoiceRepository(db *gorm.DB) InvoiceRepository { + return &GORMInvoiceRepository{ + DB: db, + } +} + +func (t *GORMInvoiceRepository) Create(invoice models.Invoice) (models.Invoice, error) { + result := t.DB.Create(&invoice) + + if result.Error != nil { + return models.Invoice{}, result.Error + } + + return invoice, nil +} + +func (t *GORMInvoiceRepository) GetAll() ([]models.Invoice, error) { + var invoice []models.Invoice + result := t.DB.Preload("PrintJobs.ShopItem").Preload("PrintJobs.Variant").Preload("PrintJobs.PaperType").Preload("PrintJobs.CoverPaperType").Preload("PrintJobs").Find(&invoice) + + return invoice, result.Error +} + +func (t *GORMInvoiceRepository) GetAllSorted(sortString string) ([]models.Invoice, error) { + var invoices []models.Invoice + result := t.DB.Preload("PrintJobs.ShopItem").Preload("PrintJobs.Variant").Preload("PrintJobs.PaperType").Preload("PrintJobs.CoverPaperType").Preload("PrintJobs").Order(sortString).Find(&invoices) + + return invoices, result.Error +} + +func (r *GORMInvoiceRepository) GetAllNewestFirst() ([]models.Invoice, error) { + return r.GetAllSorted("created_at desc") +} + +func (r *GORMInvoiceRepository) GetAllNewestLast() ([]models.Invoice, error) { + return r.GetAllSorted("created_at asc") +} + +func (t *GORMInvoiceRepository) GetById(id string) (models.Invoice, error) { + invoiceId, err := strconv.Atoi(id) + + if err != nil { + return models.Invoice{}, err + } + + var invoice models.Invoice + result := t.DB.Preload("PrintJobs").First(&invoice, uint(invoiceId)) + + if result.Error != nil { + return models.Invoice{}, result.Error + } + + return invoice, nil +} + +func (t *GORMInvoiceRepository) Update(invoice models.Invoice) (models.Invoice, error) { + result := t.DB.Save(&invoice) + if result.Error != nil { + return models.Invoice{}, result.Error + } + + return invoice, nil +} + +func (t *GORMInvoiceRepository) DeleteById(id string) error { + invoiceId, err := strconv.Atoi(id) + + if err != nil { + return err + } + + result := t.DB.Delete(&models.Invoice{}, invoiceId) + return result.Error +} diff --git a/repositories/configRepository.go b/repositories/configRepository.go new file mode 100644 index 0000000..443a272 --- /dev/null +++ b/repositories/configRepository.go @@ -0,0 +1,81 @@ +package repositories + +import ( + "strconv" + + "gorm.io/gorm" + + "git.dynamicdiscord.de/kalipso/zineshop/models" +) + +type ConfigRepository interface { + Create(models.Config) (models.Config, error) + GetAll() ([]models.Config, error) + GetById(string) (models.Config, error) + Update(models.Config) (models.Config, error) + DeleteById(string) error +} + +type GORMConfigRepository struct { + DB *gorm.DB +} + +func NewGORMConfigRepository(db *gorm.DB) ConfigRepository { + return &GORMConfigRepository{ + DB: db, + } +} + +func (t *GORMConfigRepository) Create(config models.Config) (models.Config, error) { + result := t.DB.Create(&config) + + if result.Error != nil { + return models.Config{}, result.Error + } + + return config, nil +} + +func (t *GORMConfigRepository) GetAll() ([]models.Config, error) { + var configs []models.Config + result := t.DB.Find(&configs) + + return configs, result.Error +} + +func (t *GORMConfigRepository) GetById(id string) (models.Config, error) { + configId, err := strconv.Atoi(id) + + if err != nil { + return models.Config{}, err + } + + var config models.Config + result := t.DB.First(&config, uint(configId)) + + if result.Error != nil { + return models.Config{}, result.Error + } + + return config, nil +} + +func (t *GORMConfigRepository) Update(config models.Config) (models.Config, error) { + result := t.DB.Save(&config) + if result.Error != nil { + return models.Config{}, result.Error + } + + return config, nil +} + +func (t *GORMConfigRepository) DeleteById(id string) error { + configId, err := strconv.Atoi(id) + + if err != nil { + return err + } + + result := t.DB.Delete(&models.Config{}, configId) + return result.Error +} diff --git a/repositories/paperRepository.go b/repositories/paperRepository.go new file mode 100644 index 0000000..a0a2c93 --- /dev/null +++ b/repositories/paperRepository.go @@ -0,0 +1,82 @@ +package repositories + +import ( + "strconv" + + "gorm.io/gorm" + + "git.dynamicdiscord.de/kalipso/zineshop/models" +) + +type PaperRepository interface { + Create(models.Paper) (models.Paper, error) + GetAll() ([]models.Paper, error) + GetById(string) (models.Paper, error) + //GetByShopItemId(string) (models.Paper, error) + Update(models.Paper) (models.Paper, error) + DeleteById(string) error +} + +type GORMPaperRepository struct { + DB *gorm.DB +} + +func NewGORMPaperRepository(db *gorm.DB) PaperRepository { + return &GORMPaperRepository{ + DB: db, + } +} + +func (t *GORMPaperRepository) Create(tag models.Paper) (models.Paper, error) { + result := t.DB.Create(&tag) + + if result.Error != nil { + return models.Paper{}, result.Error + } + + return tag, nil +} + +func (t *GORMPaperRepository) GetAll() ([]models.Paper, error) { + var tags []models.Paper + result := t.DB.Find(&tags) + + return tags, result.Error +} + +func (t *GORMPaperRepository) GetById(id string) (models.Paper, error) { + tagId, err := strconv.Atoi(id) + + if err != nil { + return models.Paper{}, err + } + + var tag models.Paper + result := t.DB.First(&tag, uint(tagId)) + + if result.Error != nil { + return models.Paper{}, result.Error + } + + return tag, nil +} + +func (t *GORMPaperRepository) Update(tag models.Paper) (models.Paper, error) { + result := t.DB.Save(&tag) + if result.Error != nil { + return models.Paper{}, result.Error + } + + return tag, nil +} + +func (t *GORMPaperRepository) DeleteById(id string) error { + tagId, err := strconv.Atoi(id) + + if err != nil { + return err + } + + result := t.DB.Delete(&models.Paper{}, tagId) + return result.Error +} diff --git a/repositories/printJobRepository.go b/repositories/printJobRepository.go new file mode 100644 index 0000000..028472e --- /dev/null +++ b/repositories/printJobRepository.go @@ -0,0 +1,82 @@ +package repositories + +import ( + "strconv" + + "gorm.io/gorm" + + "git.dynamicdiscord.de/kalipso/zineshop/models" +) + +type PrintJobRepository interface { + Create(models.PrintJob) (models.PrintJob, error) + GetAll() ([]models.PrintJob, error) + GetById(string) (models.PrintJob, error) + //GetByShopItemId(string) (models.PrintJob, error) + Update(models.PrintJob) (models.PrintJob, error) + DeleteById(string) error +} + +type GORMPrintJobRepository struct { + DB *gorm.DB +} + +func NewGORMPrintJobRepository(db *gorm.DB) PrintJobRepository { + return &GORMPrintJobRepository{ + DB: db, + } +} + +func (t *GORMPrintJobRepository) Create(printJob models.PrintJob) (models.PrintJob, error) { + result := t.DB.Create(&printJob) + + if result.Error != nil { + return models.PrintJob{}, result.Error + } + + return printJob, nil +} + +func (t *GORMPrintJobRepository) GetAll() ([]models.PrintJob, error) { + var printJobs []models.PrintJob + result := t.DB.Preload("ShopItem").Preload("Variant").Preload("PaperType").Preload("CoverPaperType").Find(&printJobs) + + return printJobs, result.Error +} + +func (t *GORMPrintJobRepository) GetById(id string) (models.PrintJob, error) { + printJobId, err := strconv.Atoi(id) + + if err != nil { + return models.PrintJob{}, err + } + + var printJob models.PrintJob + result := t.DB.Preload("ShopItem").Preload("Variant").Preload("PaperType").Preload("CoverPaperType").First(&printJob, uint(printJobId)) + + if result.Error != nil { + return models.PrintJob{}, result.Error + } + + return printJob, nil +} + +func (t *GORMPrintJobRepository) Update(printJob models.PrintJob) (models.PrintJob, error) { + result := t.DB.Save(&printJob) + if result.Error != nil { + return models.PrintJob{}, result.Error + } + + return printJob, nil +} + +func (t *GORMPrintJobRepository) DeleteById(id string) error { + printJobId, err := strconv.Atoi(id) + + if err != nil { + return err + } + + result := t.DB.Delete(&models.PrintJob{}, printJobId) + return result.Error +} diff --git a/repositories/repository.go b/repositories/repository.go index e3e81ff..9fe7591 100644 --- a/repositories/repository.go +++ b/repositories/repository.go @@ -9,12 +9,16 @@ import ( ) var ( - ShopItems ShopItemRepository - Users UserRepository - Tags TagRepository - CartItems CartItemRepository - Orders OrderRepository - Tokens RegisterTokenRepository + ShopItems ShopItemRepository + Users UserRepository + Tags TagRepository + CartItems CartItemRepository + Orders OrderRepository + Tokens RegisterTokenRepository + ConfigOptions ConfigRepository + Papers PaperRepository + PrintJobs PrintJobRepository + Invoices InvoiceRepository ) func InitRepositories() { @@ -29,6 +33,10 @@ func InitRepositories() { &models.Tag{}, &models.CartItem{}, &models.Order{}, + &models.Config{}, + &models.Paper{}, + &models.PrintJob{}, + &models.Invoice{}, &models.RegisterToken{}) if err != nil { @@ -41,4 +49,8 @@ func InitRepositories() { CartItems = NewGORMCartItemRepository(db) Orders = NewGORMOrderRepository(db) Tokens = NewGORMRegisterTokenRepository(db) + ConfigOptions = NewGORMConfigRepository(db) + Papers = NewGORMPaperRepository(db) + PrintJobs = NewGORMPrintJobRepository(db) + Invoices = NewGORMInvoiceRepository(db) } diff --git a/static/output.css b/static/output.css index ce1c154..4294069 100644 --- a/static/output.css +++ b/static/output.css @@ -591,6 +591,10 @@ video { grid-column: span 3 / span 3; } +.-m-1\.5 { + margin: -0.375rem; +} + .m-2 { margin: 0.5rem; } @@ -678,10 +682,18 @@ video { display: block; } +.inline-block { + display: inline-block; +} + .flex { display: flex; } +.inline-flex { + display: inline-flex; +} + .table { display: table; } @@ -758,6 +770,10 @@ video { width: 100%; } +.min-w-full { + min-width: 100%; +} + .max-w-2xl { max-width: 42rem; } @@ -766,6 +782,10 @@ video { max-width: 48rem; } +.max-w-4xl { + max-width: 56rem; +} + .max-w-6xl { max-width: 72rem; } @@ -810,6 +830,14 @@ video { grid-template-columns: repeat(12, minmax(0, 1fr)); } +.grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); +} + .grid-cols-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); } @@ -818,6 +846,10 @@ video { flex-direction: column; } +.flex-wrap { + flex-wrap: wrap; +} + .items-center { align-items: center; } @@ -830,6 +862,10 @@ video { justify-content: space-between; } +.gap-2 { + gap: 0.5rem; +} + .gap-4 { gap: 1rem; } @@ -892,6 +928,10 @@ video { border-color: rgb(229 231 235 / var(--tw-divide-opacity, 1)); } +.overflow-hidden { + overflow: hidden; +} + .overflow-x-auto { overflow-x: auto; } @@ -900,6 +940,19 @@ video { white-space: nowrap; } +.break-normal { + overflow-wrap: normal; + word-break: normal; +} + +.break-words { + overflow-wrap: break-word; +} + +.break-all { + word-break: break-all; +} + .rounded { border-radius: 0.25rem; } @@ -974,6 +1027,11 @@ video { background-color: rgb(219 234 254 / var(--tw-bg-opacity, 1)); } +.bg-blue-500 { + --tw-bg-opacity: 1; + background-color: rgb(59 130 246 / var(--tw-bg-opacity, 1)); +} + .bg-blue-600 { --tw-bg-opacity: 1; background-color: rgb(37 99 235 / var(--tw-bg-opacity, 1)); @@ -1049,6 +1107,16 @@ video { background-color: rgb(220 252 231 / var(--tw-bg-opacity, 1)); } +.bg-green-50 { + --tw-bg-opacity: 1; + background-color: rgb(240 253 244 / var(--tw-bg-opacity, 1)); +} + +.bg-green-500 { + --tw-bg-opacity: 1; + background-color: rgb(34 197 94 / var(--tw-bg-opacity, 1)); +} + .bg-green-600 { --tw-bg-opacity: 1; background-color: rgb(22 163 74 / var(--tw-bg-opacity, 1)); @@ -1134,6 +1202,16 @@ video { background-color: rgb(254 226 226 / var(--tw-bg-opacity, 1)); } +.bg-red-50 { + --tw-bg-opacity: 1; + background-color: rgb(254 242 242 / var(--tw-bg-opacity, 1)); +} + +.bg-red-500 { + --tw-bg-opacity: 1; + background-color: rgb(239 68 68 / var(--tw-bg-opacity, 1)); +} + .bg-red-800 { --tw-bg-opacity: 1; background-color: rgb(153 27 27 / var(--tw-bg-opacity, 1)); @@ -1242,6 +1320,10 @@ video { object-fit: cover; } +.p-1\.5 { + padding: 0.375rem; +} + .p-2 { padding: 0.5rem; } @@ -1289,6 +1371,11 @@ video { padding-bottom: 0.125rem; } +.py-1 { + padding-top: 0.25rem; + padding-bottom: 0.25rem; +} + .py-1\.5 { padding-top: 0.375rem; padding-bottom: 0.375rem; @@ -1319,6 +1406,11 @@ video { padding-bottom: 6rem; } +.py-3 { + padding-top: 0.75rem; + padding-bottom: 0.75rem; +} + .py-4 { padding-top: 1rem; padding-bottom: 1rem; @@ -1365,6 +1457,18 @@ video { text-align: right; } +.text-start { + text-align: start; +} + +.text-end { + text-align: end; +} + +.align-middle { + vertical-align: middle; +} + .text-2xl { font-size: 1.5rem; line-height: 2rem; @@ -1436,6 +1540,10 @@ video { font-weight: 600; } +.uppercase { + text-transform: uppercase; +} + .leading-10 { line-height: 2.5rem; } @@ -1547,6 +1655,11 @@ video { color: rgb(34 197 94 / var(--tw-text-opacity, 1)); } +.text-green-700 { + --tw-text-opacity: 1; + color: rgb(21 128 61 / var(--tw-text-opacity, 1)); +} + .text-green-800 { --tw-text-opacity: 1; color: rgb(22 101 52 / var(--tw-text-opacity, 1)); @@ -1627,6 +1740,11 @@ video { color: rgb(239 68 68 / var(--tw-text-opacity, 1)); } +.text-red-700 { + --tw-text-opacity: 1; + color: rgb(185 28 28 / var(--tw-text-opacity, 1)); +} + .text-red-800 { --tw-text-opacity: 1; color: rgb(153 27 27 / var(--tw-text-opacity, 1)); @@ -1722,27 +1840,22 @@ video { -moz-osx-font-smoothing: grayscale; } -.shadow-sm { - --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); - --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.shadow-xl { - --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - .shadow-2xl { --tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25); --tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color); box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); } -.shadow-red-900 { - --tw-shadow-color: #7f1d1d; - --tw-shadow: var(--tw-shadow-colored); +.shadow-md { + --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.shadow-sm { + --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); } .outline { @@ -1761,6 +1874,24 @@ video { outline-color: #d1d5db; } +.ring-1 { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +.ring-inset { + --tw-ring-inset: inset; +} + +.ring-green-600\/20 { + --tw-ring-color: rgb(22 163 74 / 0.2); +} + +.ring-red-600\/10 { + --tw-ring-color: rgb(220 38 38 / 0.1); +} + .filter { filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); } @@ -1849,6 +1980,11 @@ video { background-color: rgb(67 56 202 / var(--tw-bg-opacity, 1)); } +.hover\:bg-red-700:hover { + --tw-bg-opacity: 1; + background-color: rgb(185 28 28 / var(--tw-bg-opacity, 1)); +} + .hover\:bg-red-900:hover { --tw-bg-opacity: 1; background-color: rgb(127 29 29 / var(--tw-bg-opacity, 1)); @@ -2102,6 +2238,10 @@ video { max-width: 80rem; } + .lg\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + .lg\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } @@ -2126,12 +2266,20 @@ video { } @media (min-width: 1280px) { + .xl\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + .xl\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } } @media (min-width: 1536px) { + .\32xl\:grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + .\32xl\:px-0 { padding-left: 0px; padding-right: 0px; @@ -2148,6 +2296,11 @@ video { border-color: rgb(31 41 55 / var(--tw-divide-opacity, 1)); } + .dark\:divide-neutral-700 > :not([hidden]) ~ :not([hidden]) { + --tw-divide-opacity: 1; + border-color: rgb(64 64 64 / var(--tw-divide-opacity, 1)); + } + .dark\:border-gray-600 { --tw-border-opacity: 1; border-color: rgb(75 85 99 / var(--tw-border-opacity, 1)); @@ -2203,6 +2356,16 @@ video { color: rgb(17 24 39 / var(--tw-text-opacity, 1)); } + .dark\:text-neutral-200 { + --tw-text-opacity: 1; + color: rgb(229 229 229 / var(--tw-text-opacity, 1)); + } + + .dark\:text-neutral-500 { + --tw-text-opacity: 1; + color: rgb(115 115 115 / var(--tw-text-opacity, 1)); + } + .dark\:text-white { --tw-text-opacity: 1; color: rgb(255 255 255 / var(--tw-text-opacity, 1)); diff --git a/utils/utils.go b/utils/utils.go index 66c4d20..2299e37 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -3,6 +3,8 @@ package utils import ( "crypto/rand" "encoding/hex" + "fmt" + "github.com/pdfcpu/pdfcpu/pkg/api" ) func GenerateSessionId(length int) string { @@ -17,3 +19,14 @@ func GenerateSessionId(length int) string { func GenerateToken() string { return GenerateSessionId(16) } + +func CountPagesAtPath(path string) (pages int) { + ctx, err := api.ReadContextFile(path) + if err != nil { + fmt.Println("Error reading PDF:", err) + return + } + + pages = ctx.PageCount + return +} diff --git a/views/configview.html b/views/configview.html new file mode 100644 index 0000000..a29ee6a --- /dev/null +++ b/views/configview.html @@ -0,0 +1,33 @@ +{{ template "header.html" . }} + + +
+ | + | Name | +Paper | +Amount | +Price | +
|---|---|---|---|---|
|
+
+ |
+
+
+
+ {{ .ShopItem.Name }} +{{ .Variant.Name }} + |
+
+
+
+ {{ .PaperType.Brand }} - {{.PaperType.Name }}: {{ .PaperType.Size }} {{ .PaperType.Weight }}g + {{ if .CoverPaperType }} +{{ .CoverPaperType.Brand }} - {{.CoverPaperType.Name }}: {{ .CoverPaperType.Size }} {{ .CoverPaperType.Weight }}g + {{ end }} + |
+ {{ .Amount }} | +{{ .PriceTotal }} | +
| + TOTAL + | ++ | + | + | {{ .PriceTotal }} | +
+
+
+ Make sure you put paper in that tray when selecting this option.
Print Order: The Zines will be printed from top to bottom as seen in this list.
@@ -33,14 +33,30 @@
+
+ {{ if .ShopItem.WasPrinted }}
+ Tested
+ {{ else }}
+ NOT TESTED
+ {{ end }}
{{ .ShopItem.Name }} - {{ .ItemVariant.Name }}