package controllers import ( "errors" "fmt" "net/http" "strconv" "strings" "github.com/gin-gonic/gin" "gorm.io/gorm" "git.dynamicdiscord.de/kalipso/zineshop/models" //"git.dynamicdiscord.de/kalipso/zineshop/services" "git.dynamicdiscord.de/kalipso/zineshop/repositories" "git.dynamicdiscord.de/kalipso/zineshop/utils" ) type CartItemController interface { //CRUDController CartItemView(*gin.Context) AddItemHandler(*gin.Context) DeleteItemHandler(*gin.Context) EditItemHandler(*gin.Context) CheckoutView(*gin.Context) CheckoutHandler(*gin.Context) OrderView(*gin.Context) OrderHandler(*gin.Context) OrdersView(*gin.Context) OrdersHandler(*gin.Context) } type cartItemController struct{} func NewCartItemController() CartItemController { return &cartItemController{} } // getSetCookieValue retrieves the value of a cookie from the Set-Cookie header func getSetCookieValue(c *gin.Context, cookieName string) string { // Check the Set-Cookie headers cookies := c.Writer.Header()["Set-Cookie"] for _, cookie := range cookies { if strings.HasPrefix(cookie, cookieName+"=") { // Extract the cookie value parts := strings.SplitN(cookie, ";", 2) if len(parts) > 0 { return strings.TrimPrefix(parts[0], cookieName+"=") } } } return "" // Return empty string if cookie is not found } func GetSessionId(ctx *gin.Context) string { sessionId, err := ctx.Cookie("session_id") if err != nil { //we need to check if we already set cookie in the response so that we dont do this multiple times responseCookie := getSetCookieValue(ctx, "session_id") if len(responseCookie) != 0 { return responseCookie } sessionId = utils.GenerateSessionId(16) ctx.SetCookie("session_id", sessionId, 3600, "/", "", false, true) } return sessionId } func (rc *cartItemController) NewCartItemFromForm(ctx *gin.Context) (models.CartItem, error) { sessionId := GetSessionId(ctx) shopItemIdStr := ctx.PostForm("ShopItemId") shopItemId, err := strconv.Atoi(shopItemIdStr) itemVariantIdStr := ctx.PostForm("ItemVariantId") itemVariantId, err := strconv.Atoi(itemVariantIdStr) if err != nil { return models.CartItem{}, err } quantity := 1 shopItem, err := repositories.ShopItems.GetById(shopItemIdStr) if err != nil { return models.CartItem{}, err } itemVariant, err := repositories.ShopItems.GetVariantById(itemVariantIdStr) cartItem := models.CartItem{ SessionId: sessionId, ShopItemId: uint(shopItemId), ShopItem: shopItem, ItemVariantId: uint(itemVariantId), ItemVariant: itemVariant, Quantity: quantity, } return cartItem, nil } func (rc *cartItemController) NewAddressFromForm(ctx *gin.Context) (models.AddressInfo, error) { firstName := ctx.PostForm("firstName") lastName := ctx.PostForm("lastName") address := ctx.PostForm("address") postalCode := ctx.PostForm("postalCode") city := ctx.PostForm("city") country := ctx.PostForm("country") if firstName == "" { return models.AddressInfo{}, fmt.Errorf("first name missing.") } if lastName == "" { return models.AddressInfo{}, fmt.Errorf("Last name missing.") } if address == "" { return models.AddressInfo{}, fmt.Errorf("address missing.") } if postalCode == "" { return models.AddressInfo{}, fmt.Errorf("postalCode missing.") } if city == "" { return models.AddressInfo{}, fmt.Errorf("city missing.") } if country == "" { return models.AddressInfo{}, fmt.Errorf("country missing.") } return models.AddressInfo{ FirstName: firstName, LastName: lastName, Address: address, PostalCode: postalCode, City: city, Country: country, }, nil } func (rc *cartItemController) NewOrderFromForm(ctx *gin.Context) (models.Order, error) { sessionId := GetSessionId(ctx) status := models.OrderStatus("AwaitingConfirmation") token := utils.GenerateToken() email := ctx.PostForm("email") comment := ctx.PostForm("comment") firstName := ctx.PostForm("firstName") lastName := ctx.PostForm("lastName") address := ctx.PostForm("address") postalCode := ctx.PostForm("postalCode") city := ctx.PostForm("city") country := ctx.PostForm("country") shippingStr := ctx.PostForm("shippingMethod") //address, err := rc.NewAddressFromForm(ctx) //if shippingStr != "pickup" { // if err != nil { // return models.Order{}, fmt.Errorf("Invalid address information.") // } //} shipping, err := models.GetShippingMethod(shippingStr) if err != nil { return models.Order{}, fmt.Errorf("Invalid shipping method.") } cartItems, err := repositories.CartItems.GetAllBySession(sessionId) fmt.Println(sessionId) fmt.Println(cartItems) if err != nil { return models.Order{}, err } order := models.Order{ SessionId: sessionId, Status: status, Token: token, Email: email, Comment: comment, FirstName: firstName, LastName: lastName, Address: address, PostalCode: postalCode, City: city, Country: country, Shipping: shipping.Id, CartItems: cartItems, } return order, nil } func (rc *cartItemController) Create(c *gin.Context) { cartItem, err := rc.NewCartItemFromForm(c) if err != nil { ReplyError(c, fmt.Errorf("cartItem creation failed: %s", err)) return } _, err = repositories.CartItems.Create(cartItem) if err != nil { ReplyError(c, fmt.Errorf("cartItem creation failed: %s", err)) return } ReplyOK(c, "cartItem was created") } func (rc *cartItemController) Update(c *gin.Context) { cartItem, err := rc.NewCartItemFromForm(c) if err != nil { ReplyError(c, err) return } _, err = repositories.CartItems.Update(cartItem) if err != nil { ReplyError(c, fmt.Errorf("cartItem creation failed: %s", err)) return } ReplyOK(c, "cartItem was updated") } //func (rc *cartItemController) Delete(c *gin.Context) { // err := repositories.CartItems.DeleteBySessionId(c.Param("id")) // // if err != nil { // ReplyError(c, fmt.Errorf("cartItem deletion failed: %s", err)) // return // } // // ReplyOK(c, "cartItem was deleted") //} func (rc *cartItemController) CartItemView(c *gin.Context) { sessionId := GetSessionId(c) cartItems, err := repositories.CartItems.GetAllBySession(sessionId) //cartItems, err := repositories.CartItems.GetAll() if err != nil { c.HTML(http.StatusBadRequest, "cart.html", gin.H{"data": gin.H{"error": err}}) } priceTotal := 0.0 for _, cartItem := range cartItems { priceTotal += (float64(cartItem.Quantity) * cartItem.ItemVariant.Price) } fmt.Println("PRICE TOTAL", priceTotal) data := CreateSessionData(c, gin.H{ "cartItems": cartItems, "priceTotal": fmt.Sprintf("%.2f", priceTotal), //round 2 decimals "shipping": models.GetShippingMethods(), }) c.HTML(http.StatusOK, "cart.html", data) } func (rc *cartItemController) AddItemHandler(c *gin.Context) { cartItem, err := rc.NewCartItemFromForm(c) if err != nil { fmt.Println(err) c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": err}) return } _, err = repositories.CartItems.Create(cartItem) if err != nil { data := CreateSessionData(c, gin.H{ "error": err, "success": "", }) c.HTML(http.StatusOK, "cart.html", data) return } rc.CartItemView(c) } func (rc *cartItemController) DeleteItemHandler(c *gin.Context) { err := repositories.CartItems.DeleteById(c.PostForm("id")) if err != nil { fmt.Println(err) data := CreateSessionData(c, gin.H{ "error": err, "success": "", }) c.HTML(http.StatusOK, "index.html", data) } c.Redirect(http.StatusFound, "/cart") } func (rc *cartItemController) EditItemHandler(c *gin.Context) { cartItemId := c.PostForm("id") cartItem, err := repositories.CartItems.GetById(cartItemId) if err != nil { fmt.Println(err) c.Redirect(http.StatusFound, "/cart") return } action := c.PostForm("action") if action == "setAmount" { amountStr := c.PostForm("amount") amount, err := strconv.Atoi(amountStr) if err != nil { c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": err}) return } if amount < 0 { c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": "amount cant be negative"}) return } if amount > 500 { c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": "amount cant be over 500"}) return } cartItem.Quantity = amount } if action == "increase" { cartItem.Quantity += 1 } if action == "decrease" { cartItem.Quantity -= 1 if cartItem.Quantity == 0 { cartItem.Quantity = 1 } } _, err = repositories.CartItems.Update(cartItem) if err != nil { fmt.Println(err) } c.Redirect(http.StatusFound, "/cart") } func (rc *cartItemController) CheckoutView(c *gin.Context) { shippingMethod := c.Query("shippingMethod") if shippingMethod == "" { rc.CartItemView(c) return } c.HTML(http.StatusOK, "checkout.html", gin.H{ "askAddress": (shippingMethod != "pickup"), "shippingMethod": shippingMethod, }) } func (rc *cartItemController) CheckoutHandler(c *gin.Context) { order, err := rc.NewOrderFromForm(c) if err != nil { fmt.Println(err) c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": err}) return } existingOrder, err := repositories.Orders.GetBySession(order.SessionId) if errors.Is(err, gorm.ErrRecordNotFound) { fmt.Println("Creating Order") createdOrder, err := repositories.Orders.Create(order) if err != nil { data := CreateSessionData(c, gin.H{ "error": err, "success": "", }) c.HTML(http.StatusOK, "error.html", data) return } for _, cartItem := range order.CartItems { cartItem.OrderID = createdOrder.ID repositories.CartItems.Update(cartItem) } } else if err == nil { fmt.Println("Updating Order") order.ID = existingOrder.ID order.CreatedAt = existingOrder.CreatedAt _, err := repositories.Orders.Update(order) if err != nil { fmt.Println(err) } } shipping, err := models.GetShippingMethod(order.Shipping) if err != nil { data := CreateSessionData(c, gin.H{ "error": err, "success": "", }) c.HTML(http.StatusOK, "error.html", data) return } priceProducts, priceTotal, err := order.CalculatePrices() if err != nil { data := CreateSessionData(c, gin.H{ "error": err, "success": "", }) c.HTML(http.StatusOK, "error.html", data) return } data := CreateSessionData(c, gin.H{ "error": "", "success": "", "order": order, "askAddress": (order.Shipping != "pickup"), "isPreview": true, "shipping": shipping, "priceProducts": fmt.Sprintf("%.2f", priceProducts), //round 2 decimals "priceTotal": fmt.Sprintf("%.2f", priceTotal), //round 2 decimals }) fmt.Println(order) c.HTML(http.StatusOK, "orderpreview.html", data) } func (rc *cartItemController) OrderView(c *gin.Context) { orderToken := c.Param("token") order, err := repositories.Orders.GetByToken(orderToken) if err != nil { c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": "Order does not exist."}) return } shipping, err := models.GetShippingMethod(order.Shipping) if err != nil { c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": "Could not get shipping method"}) return } priceProducts, priceTotal, err := order.CalculatePrices() if err != nil { c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": "Could not calculate final prices"}) return } fmt.Printf("Order: %v\n", order) fmt.Printf("PriceTotal: %v\n", priceTotal) fmt.Printf("Amount Items: %v\n", len(order.CartItems)) for _, item := range order.CartItems { fmt.Printf("Cartitem: %v", item) } c.HTML(http.StatusOK, "order.html", CreateSessionData(c, gin.H{ "error": "", "success": "", "order": order, "shipping": shipping, "priceProducts": fmt.Sprintf("%.2f", priceProducts), //round 2 decimals "priceTotal": fmt.Sprintf("%.2f", priceTotal), //round 2 decimals })) } func (rc *cartItemController) OrderHandler(c *gin.Context) { confirmation := c.PostForm("confirm-order") if confirmation == "" { c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": "Something went wrong, try again later"}) return } if confirmation != "true" { c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": "Order was not confirmed."}) return } sessionId := GetSessionId(c) order, err := repositories.Orders.GetBySession(sessionId) if err != nil { c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": "Something went wrong, try again later"}) return } order.Status = models.AwaitingPayment err = order.Validate() if err != nil { c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": err}) return } for idx := range order.CartItems { order.CartItems[idx].SessionId = "0" repositories.CartItems.Update(order.CartItems[idx]) } _, err = repositories.Orders.Update(order) if err != nil { c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": err}) return } //TODO: cartItemRepository delete all by session - otherwise items stay in cart after completing order.. c.Redirect(http.StatusFound, fmt.Sprintf("/order/%s", order.Token)) } func (rc *cartItemController) OrdersView(c *gin.Context) { orders, err := repositories.Orders.GetAll() if err != nil { c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": "Orders doe not exist."}) return } c.HTML(http.StatusOK, "editorders.html", CreateSessionData(c, gin.H{ "error": "", "success": "", "orders": orders, })) } func (rc *cartItemController) OrdersHandler(c *gin.Context) { token := c.Param("token") if token == "" { c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": "EmptyToken"}) return } action := c.PostForm("action") if action == "update" { order, err := repositories.Orders.GetByToken(token) if err != nil { c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": "No order with given token found"}) return } status := c.PostForm("order-status") //TODO validate status if status == "" { c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": "Invalid Order Status"}) return } order.Status = models.OrderStatus(status) _, err = repositories.Orders.Update(order) if err != nil { c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": err}) return } } if action == "delete" { fmt.Println("Deleting Order ", token) err := repositories.Orders.DeleteByToken(token) if err != nil { c.HTML(http.StatusBadRequest, "error.html", gin.H{"error": err}) return } } c.Redirect(http.StatusFound, "/orders") }