Files
zineshop/controllers/cartItemController.go
2025-04-14 23:29:24 +02:00

606 lines
14 KiB
Go

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")
}