quite a lot of things
This commit is contained in:
231
internal/web/product.go
Normal file
231
internal/web/product.go
Normal file
@@ -0,0 +1,231 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"billit/internal/database"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// ProductHandlers holds db reference for product operations
|
||||
type ProductHandlers struct {
|
||||
db database.Service
|
||||
}
|
||||
|
||||
// NewProductHandlers creates handlers with db access
|
||||
func NewProductHandlers(db database.Service) *ProductHandlers {
|
||||
return &ProductHandlers{db: db}
|
||||
}
|
||||
|
||||
// getUserID extracts user ID from context (set by auth middleware)
|
||||
func getUserID(c echo.Context) string {
|
||||
if uid, ok := c.Get("user_id").(string); ok {
|
||||
return uid
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ProductListHandler renders the /product page with all products
|
||||
func (h *ProductHandlers) ProductListHandler(c echo.Context) error {
|
||||
userID := getUserID(c)
|
||||
products, err := h.db.GetAllProducts(userID)
|
||||
if err != nil {
|
||||
return RenderServerError(c, "Failed to load products. Please try again.")
|
||||
}
|
||||
return Render(c, ProductListPage(products))
|
||||
}
|
||||
|
||||
// ProductCreatePageHandler renders the /product/create form page
|
||||
func (h *ProductHandlers) ProductCreatePageHandler(c echo.Context) error {
|
||||
return Render(c, ProductCreatePage())
|
||||
}
|
||||
|
||||
// ProductEditPageHandler renders the /product/edit/:sku form page
|
||||
func (h *ProductHandlers) ProductEditPageHandler(c echo.Context) error {
|
||||
sku := c.Param("sku")
|
||||
userID := getUserID(c)
|
||||
|
||||
product, err := h.db.GetProductBySKU(sku, userID)
|
||||
if err != nil || product == nil {
|
||||
return RenderNotFound(c, "Product not found or you don't have access to it.")
|
||||
}
|
||||
return Render(c, ProductEditPage(*product))
|
||||
}
|
||||
|
||||
// ProductCreateHandler handles POST /product/create
|
||||
func (h *ProductHandlers) ProductCreateHandler(c echo.Context) error {
|
||||
userID := getUserID(c)
|
||||
if userID == "" {
|
||||
return c.Redirect(http.StatusFound, "/")
|
||||
}
|
||||
|
||||
sku := c.FormValue("sku")
|
||||
if sku == "" {
|
||||
return c.String(http.StatusBadRequest, "SKU is required")
|
||||
}
|
||||
|
||||
name := c.FormValue("name")
|
||||
if name == "" {
|
||||
return c.String(http.StatusBadRequest, "Name is required")
|
||||
}
|
||||
|
||||
// Check if SKU already exists for this user
|
||||
existing, _ := h.db.GetProductBySKU(sku, userID)
|
||||
if existing != nil {
|
||||
return c.String(http.StatusBadRequest, "A product with this SKU already exists")
|
||||
}
|
||||
|
||||
hsn := c.FormValue("hsn")
|
||||
baseStr := c.FormValue("base_price")
|
||||
wholesaleStr := c.FormValue("wholesale_price")
|
||||
gstStr := c.FormValue("gst_rate")
|
||||
smallQtyStr := c.FormValue("small_order_qty")
|
||||
|
||||
base, _ := strconv.ParseFloat(baseStr, 64)
|
||||
wholesale, _ := strconv.ParseFloat(wholesaleStr, 64)
|
||||
if wholesale == 0 {
|
||||
wholesale = base // default wholesale to base price
|
||||
}
|
||||
|
||||
gstRate := 0.18 // default 18%
|
||||
switch gstStr {
|
||||
case "0":
|
||||
gstRate = 0.0
|
||||
case "5":
|
||||
gstRate = 0.05
|
||||
case "12":
|
||||
gstRate = 0.12
|
||||
case "18":
|
||||
gstRate = 0.18
|
||||
case "28":
|
||||
gstRate = 0.28
|
||||
}
|
||||
|
||||
smallQty := 1
|
||||
if v, err := strconv.Atoi(smallQtyStr); err == nil && v > 0 {
|
||||
smallQty = v
|
||||
}
|
||||
|
||||
smallFeeStr := c.FormValue("small_order_fee")
|
||||
smallFee, _ := strconv.ParseFloat(smallFeeStr, 64)
|
||||
|
||||
unit := c.FormValue("unit")
|
||||
if unit == "" {
|
||||
unit = "pcs"
|
||||
}
|
||||
|
||||
product := database.Product{
|
||||
SKU: sku,
|
||||
Name: name,
|
||||
HSNCode: hsn,
|
||||
BasePrice: base,
|
||||
WholesalePrice: wholesale,
|
||||
GSTRate: gstRate,
|
||||
SmallOrderQty: smallQty,
|
||||
SmallOrderFee: smallFee,
|
||||
Unit: unit,
|
||||
}
|
||||
|
||||
if err := h.db.CreateProduct(product, userID); err != nil {
|
||||
return c.String(http.StatusInternalServerError, "failed to create product")
|
||||
}
|
||||
|
||||
// Redirect to product list
|
||||
return c.Redirect(http.StatusSeeOther, "/product")
|
||||
}
|
||||
|
||||
// ProductUpdateHandler handles POST /product/edit/:sku
|
||||
func (h *ProductHandlers) ProductUpdateHandler(c echo.Context) error {
|
||||
userID := getUserID(c)
|
||||
if userID == "" {
|
||||
return c.Redirect(http.StatusFound, "/")
|
||||
}
|
||||
|
||||
sku := c.Param("sku")
|
||||
|
||||
// Verify product belongs to user
|
||||
existing, _ := h.db.GetProductBySKU(sku, userID)
|
||||
if existing == nil {
|
||||
return c.String(http.StatusNotFound, "Product not found")
|
||||
}
|
||||
|
||||
name := c.FormValue("name")
|
||||
if name == "" {
|
||||
return c.String(http.StatusBadRequest, "Name is required")
|
||||
}
|
||||
|
||||
hsn := c.FormValue("hsn")
|
||||
baseStr := c.FormValue("base_price")
|
||||
wholesaleStr := c.FormValue("wholesale_price")
|
||||
gstStr := c.FormValue("gst_rate")
|
||||
smallQtyStr := c.FormValue("small_order_qty")
|
||||
|
||||
base, _ := strconv.ParseFloat(baseStr, 64)
|
||||
wholesale, _ := strconv.ParseFloat(wholesaleStr, 64)
|
||||
if wholesale == 0 {
|
||||
wholesale = base
|
||||
}
|
||||
|
||||
gstRate := 0.18
|
||||
switch gstStr {
|
||||
case "0":
|
||||
gstRate = 0.0
|
||||
case "5":
|
||||
gstRate = 0.05
|
||||
case "12":
|
||||
gstRate = 0.12
|
||||
case "18":
|
||||
gstRate = 0.18
|
||||
case "28":
|
||||
gstRate = 0.28
|
||||
}
|
||||
|
||||
smallQty := 1
|
||||
if v, err := strconv.Atoi(smallQtyStr); err == nil && v > 0 {
|
||||
smallQty = v
|
||||
}
|
||||
|
||||
smallFeeStr := c.FormValue("small_order_fee")
|
||||
smallFee, _ := strconv.ParseFloat(smallFeeStr, 64)
|
||||
|
||||
unit := c.FormValue("unit")
|
||||
if unit == "" {
|
||||
unit = "pcs"
|
||||
}
|
||||
|
||||
product := database.Product{
|
||||
SKU: sku,
|
||||
Name: name,
|
||||
HSNCode: hsn,
|
||||
BasePrice: base,
|
||||
WholesalePrice: wholesale,
|
||||
GSTRate: gstRate,
|
||||
SmallOrderQty: smallQty,
|
||||
SmallOrderFee: smallFee,
|
||||
Unit: unit,
|
||||
}
|
||||
|
||||
if err := h.db.UpdateProduct(product, userID); err != nil {
|
||||
return c.String(http.StatusInternalServerError, "failed to update product")
|
||||
}
|
||||
|
||||
return c.Redirect(http.StatusSeeOther, "/product")
|
||||
}
|
||||
|
||||
// ProductDeleteHandler handles DELETE /product/:sku
|
||||
func (h *ProductHandlers) ProductDeleteHandler(c echo.Context) error {
|
||||
userID := getUserID(c)
|
||||
sku := c.Param("sku")
|
||||
|
||||
if err := h.db.DeleteProduct(sku, userID); err != nil {
|
||||
return c.String(http.StatusInternalServerError, "failed to delete product")
|
||||
}
|
||||
|
||||
// For HTMX, return empty to remove the row
|
||||
if c.Request().Header.Get("HX-Request") == "true" {
|
||||
return c.NoContent(http.StatusOK)
|
||||
}
|
||||
|
||||
return c.Redirect(http.StatusSeeOther, "/product")
|
||||
}
|
||||
Reference in New Issue
Block a user