Files
backend_jupiter/controllers/productsController.go
2026-05-25 11:45:56 +05:30

869 lines
21 KiB
Go

package controllers
import (
"nearle/db"
"nearle/domain"
"nearle/models"
"nearle/utils"
"net/http"
"strconv"
"strings"
"time"
"github.com/gofiber/fiber/v2"
)
func GetProductInfo(c *fiber.Ctx) error {
pid, _ := strconv.Atoi(c.Query("productid"))
utils.Logger.Debugf("productid: %d", pid)
return c.JSON(fiber.Map{
"code": http.StatusOK,
"message": "Success",
"status": true,
})
}
func GetProducts(c *fiber.Ctx) error {
return c.JSON(fiber.Map{
"code": http.StatusOK,
"message": "Success",
"status": true,
})
}
func GetProductCategory(c *fiber.Ctx) error {
data := domain.GetProductCategory()
return c.JSON(fiber.Map{
"code": http.StatusOK,
"message": "Success",
"status": true,
"details": data,
})
}
func GetProductSubCategory(c *fiber.Ctx) error {
categoryID, _ := strconv.Atoi(c.Query("categoryid"))
tenantID, _ := strconv.Atoi(c.Query("tenantid"))
data := domain.GetProductSubCategory(categoryID, tenantID)
return c.JSON(fiber.Map{
"code": http.StatusOK,
"message": "Success",
"status": true,
"details": data,
})
}
func CreateProductVariant(c *fiber.Ctx) error {
var input models.Productvariant
if err := c.BodyParser(&input); err != nil {
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"code": http.StatusBadRequest,
"message": "Invalid request body",
"status": false,
})
}
if err := domain.CreateProductVariant(input); err != nil {
return c.JSON(fiber.Map{
"code": http.StatusConflict,
"message": "Failed",
"status": false,
})
}
return c.JSON(fiber.Map{
"code": http.StatusOK,
"message": "Success",
"status": true,
})
}
func CreateProduct(c *fiber.Ctx) error {
var product models.Products
if err := c.BodyParser(&product); err != nil {
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"code": http.StatusBadRequest,
"message": "Invalid request body",
"status": false,
})
}
if err := domain.CreateProduct(&product); err != nil {
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
"code": http.StatusInternalServerError,
"message": "Failed to create product",
"status": false,
})
}
return c.JSON(fiber.Map{
"code": http.StatusOK,
"message": "Product created successfully",
"status": true,
})
}
func GetAllProducts(c *fiber.Ctx) error {
categoryID, _ := strconv.Atoi(c.Query("categoryid"))
subcategoryID, _ := strconv.Atoi(c.Query("subcategoryid"))
productID, _ := strconv.Atoi(c.Query("productid"))
applocationID, _ := strconv.Atoi(c.Query("applocationid"))
tenantID, _ := strconv.Atoi(c.Query("tenantid"))
locationID, _ := strconv.Atoi(c.Query("locationid"))
keyword := c.Query("keyword", "")
productStatus := c.Query("productstatus", "")
approve := c.Query("approve", "")
pageno, _ := strconv.Atoi(c.Query("pageno"))
pagesize, _ := strconv.Atoi(c.Query("pagesize"))
details, err := domain.FetchFilteredProducts(
categoryID, subcategoryID, productID, applocationID, tenantID,
locationID, keyword, productStatus, approve, pageno, pagesize,
)
if err != nil {
return c.JSON(fiber.Map{
"code": http.StatusConflict,
"message": "Failed",
"status": false,
})
}
return c.JSON(fiber.Map{
"code": http.StatusOK,
"message": "Success",
"status": true,
"data": details,
})
}
func UpdateProduct(c *fiber.Ctx) error {
var data models.Products
if err := c.BodyParser(&data); err != nil {
return err
}
err := domain.UpdateProduct(data)
if err != nil {
return c.JSON(fiber.Map{
"status": false,
"code": http.StatusConflict,
"message": err,
})
}
return c.JSON(fiber.Map{
"status": true,
"code": http.StatusAccepted,
"message": "Product update successful",
})
}
func DeleteProduct(c *fiber.Ctx) error {
// Get product ID from query
pidStr := c.Query("productid")
pid, err := strconv.Atoi(pidStr)
if err != nil || pid <= 0 {
return c.JSON(fiber.Map{
"code": http.StatusBadRequest,
"message": "Invalid product ID",
"status": false,
})
}
// Start transaction
tx := db.DB.Begin()
if tx.Error != nil {
return c.JSON(fiber.Map{
"code": http.StatusInternalServerError,
"message": "Failed to start transaction",
"status": false,
})
}
// Delete product
if err := tx.Table("products").Where("productid = ?", pid).Delete(&models.Products{}).Error; err != nil {
tx.Rollback()
return c.JSON(fiber.Map{
"code": http.StatusInternalServerError,
"message": "Error deleting product",
"status": false,
})
}
// Commit transaction
if err := tx.Commit().Error; err != nil {
return c.JSON(fiber.Map{
"code": http.StatusInternalServerError,
"message": "Failed to commit transaction",
"status": false,
})
}
// Success response
return c.JSON(fiber.Map{
"code": http.StatusOK,
"message": "Success",
"status": true,
})
}
func GetProductsBySubcategory(c *fiber.Ctx) error {
// log.Println("Handler: GetProductsBySubcategory called")
// Required query param: categoryid
categoryIDStr := c.Query("categoryid")
if categoryIDStr == "" {
utils.Error("Error: categoryid is missing")
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"code": 400,
"status": false,
"message": "categoryid is required",
"data": fiber.Map{},
})
}
categoryID, err := strconv.Atoi(categoryIDStr)
if err != nil {
utils.Logger.Errorf("Error: Invalid categoryid: %s", categoryIDStr)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"code": 400,
"status": false,
"message": "Invalid categoryid",
"data": fiber.Map{},
})
}
// Optional tenantid
tenantIDStr := c.Query("tenantid")
tenantID := 0
if tenantIDStr != "" {
tenantID, err = strconv.Atoi(tenantIDStr)
if err != nil {
utils.Logger.Errorf("Error: Invalid tenantid: %s", tenantIDStr)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"code": 400,
"status": false,
"message": "Invalid tenantid",
"data": fiber.Map{},
})
}
}
// Optional query params
applocationIDStr := c.Query("applocationid", "0")
applocationID, _ := strconv.Atoi(applocationIDStr)
productIDStr := c.Query("productid")
keyword := c.Query("keyword")
locationIDStr := c.Query("locationid")
locationID := 0
if locationIDStr != "" {
locationID, _ = strconv.Atoi(locationIDStr)
}
productID := 0
if productIDStr != "" {
productID, err = strconv.Atoi(productIDStr)
if err != nil {
utils.Logger.Errorf("Error: Invalid productid: %s", productIDStr)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"code": 400,
"status": false,
"message": "Invalid productid",
"data": fiber.Map{},
})
}
}
// Fetch subcategories
var subcategories []models.Subcategory
utils.Logger.Infof("Fetching subcategories for categoryid: %d", categoryID)
if err := db.DB.Table("productsubcategories").
Where("categoryid = ?", categoryID).
Find(&subcategories).Error; err != nil {
utils.Logger.Errorf("Error fetching subcategories: %v", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"code": 500,
"status": false,
"message": "Error fetching subcategories",
"data": fiber.Map{},
})
}
// Build product query
var allProducts []models.Products
query := db.DB.
Table("products a").
Joins("LEFT JOIN productlocations pl ON pl.productid = a.productid").
Where("a.categoryid = ?", categoryID)
if tenantID > 0 {
query = query.Where("a.tenantid = ?", tenantID)
}
if locationID > 0 {
utils.Logger.Infof("Filtering by locationid: %d", locationID)
query = query.Where("pl.locationid = ?", locationID)
} else {
utils.Info("No locationid provided or value is 0 — showing all locations")
}
if applocationID > 0 {
query = query.Where("a.applocationid = ?", applocationID)
}
if productID > 0 {
query = query.Where("a.productid = ?", productID)
}
if keyword != "" {
utils.Info("Applying keyword filter")
like := "%" + strings.ToLower(keyword) + "%"
query = query.Where(
db.DB.Where("LOWER(a.productname) LIKE ?", like).
Or("LOWER(a.unitvalue) LIKE ?", like).
Or("LOWER(CAST(a.productcost AS CHAR)) LIKE ?", like),
)
}
if err := query.Select("a.*").Find(&allProducts).Error; err != nil {
utils.Logger.Errorf("Error fetching products: %v", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"code": 500,
"status": false,
"message": "Error fetching products",
"data": fiber.Map{},
})
}
// Group products under subcategories
var details []models.SubcategoryProductResponse
var uncategorizedProducts []models.Products
for _, subcat := range subcategories {
var subcatProducts []models.Products
for _, prod := range allProducts {
if prod.Subcategoryid != nil && *prod.Subcategoryid == subcat.Subcategoryid {
subcatProducts = append(subcatProducts, prod)
}
}
if len(subcatProducts) > 0 {
details = append(details, models.SubcategoryProductResponse{
SubcategoryID: subcat.Subcategoryid,
SubcategoryName: subcat.Subcategoryname,
Products: subcatProducts,
})
}
}
for _, prod := range allProducts {
if prod.Subcategoryid == nil {
uncategorizedProducts = append(uncategorizedProducts, prod)
}
}
if len(uncategorizedProducts) > 0 {
details = append(details, models.SubcategoryProductResponse{
SubcategoryID: 0,
SubcategoryName: "Uncategorized",
Products: uncategorizedProducts,
})
}
// Tenant info block only if tenantID is passed
if tenantID > 0 {
var tenantInfo = fiber.Map{}
var tenantFound bool
for _, p := range allProducts {
if p.Tenantid != nil {
var tenant struct {
Tenantname string
Address string
Licenseno string
Primaryemail string
Primarycontact string
Locationname string
Pickuplocationid int
Suburb string
City string
Latitude string
Longitude string
Postcode string
}
err := db.DB.Raw(`
SELECT
t.tenantname,
t.address,
t.licenseno,
t.primaryemail,
t.primarycontact,
l.locationid AS pickuplocationid,
l.suburb,
l.city,
l.latitude,
l.longitude,
l.postcode,
a.locationname
FROM tenants t
LEFT JOIN tenantlocations l ON t.tenantid = l.tenantid
LEFT JOIN app_location a ON l.applocationid = a.applocationid
WHERE t.tenantid = ? AND t.applocationid = ?
LIMIT 1
`, *p.Tenantid, applocationID).Scan(&tenant).Error
if err == nil {
tenantInfo = fiber.Map{
"tenantname": tenant.Tenantname,
"address": tenant.Address,
"licenseno": tenant.Licenseno,
"primaryemail": tenant.Primaryemail,
"primarycontact": tenant.Primarycontact,
"locationname": tenant.Locationname,
"pickuplocationid": tenant.Pickuplocationid,
"suburb": tenant.Suburb,
"city": tenant.City,
"pickuplat": tenant.Latitude,
"pickuplong": tenant.Longitude,
"postcode": tenant.Postcode,
"details": details,
}
tenantFound = true
}
break
}
}
if tenantFound {
return c.Status(fiber.StatusOK).JSON(fiber.Map{
"code": 200,
"status": true,
"message": "Success",
"data": tenantInfo,
})
}
}
// Return only details if tenantid is not provided
return c.Status(fiber.StatusOK).JSON(fiber.Map{
"code": 200,
"status": true,
"message": "Success",
"data": fiber.Map{
"details": details,
},
})
}
func GetProductCount(c *fiber.Ctx) error {
tenantID, _ := strconv.Atoi(c.Query("tenantid"))
categoryID, _ := strconv.Atoi(c.Query("categoryid"))
subcategoryID, _ := strconv.Atoi(c.Query("subcategoryid"))
approve := c.Query("approve", "") // New line
result, err := domain.Getproductcount(tenantID, categoryID, subcategoryID, approve)
if err != nil {
return c.JSON(fiber.Map{
"code": http.StatusConflict,
"message": "failed",
"status": false,
})
}
return c.JSON(fiber.Map{
"code": http.StatusOK,
"message": "Success",
"status": true,
"details": result,
})
}
func GetProductByVariant(c *fiber.Ctx) error {
tenantID, _ := strconv.Atoi(c.Query("tenantid"))
variantid, _ := strconv.Atoi(c.Query("variantid"))
result, err := domain.GetproductbyVariant(tenantID, variantid)
if err != nil {
return c.JSON(fiber.Map{
"code": http.StatusConflict,
"message": "failed",
"status": false,
})
}
return c.JSON(fiber.Map{
"code": http.StatusOK,
"message": "Success",
"status": true,
"details": result,
})
}
func GetCatalougeProducts(c *fiber.Ctx) error {
tenantId, _ := strconv.Atoi(c.Query("tenantid"))
locationid, _ := strconv.Atoi(c.Query("locationid"))
subcategoryid, _ := strconv.Atoi(c.Query("subcategoryid"))
keyword := c.Query("keyword")
pageno, _ := strconv.Atoi(c.Query("pageno"))
pagesize, _ := strconv.Atoi(c.Query("pagesize"))
data := domain.GetCatalougeProducts(tenantId, locationid, subcategoryid, pageno, pagesize, keyword)
return c.JSON(fiber.Map{
"code": http.StatusOK,
"message": "Success",
"status": true,
"details": data,
})
}
func GetStockstatement(c *fiber.Ctx) error {
tenantId, _ := strconv.Atoi(c.Query("tenantid"))
locationid, _ := strconv.Atoi(c.Query("locationid"))
subcategoryid, _ := strconv.Atoi(c.Query("subcategoryid"))
pageno, _ := strconv.Atoi(c.Query("pageno"))
pagesize, _ := strconv.Atoi(c.Query("pagesize"))
keyword := c.Query("keyword")
data := domain.GetStockStatement(tenantId, locationid, subcategoryid, pageno, pagesize, keyword)
return c.JSON(fiber.Map{
"code": http.StatusOK,
"message": "Success",
"status": true,
"details": data,
})
}
func GetProductVariants(c *fiber.Ctx) error {
tenantId, _ := strconv.Atoi(c.Query("tenantid"))
subcategoryId, _ := strconv.Atoi(c.Query("subcategoryid"))
data := domain.GetProductVariants(tenantId, subcategoryId)
return c.JSON(fiber.Map{
"code": http.StatusOK,
"message": "Success",
"status": true,
"details": data,
})
}
func CreateProductLocation(c *fiber.Ctx) error {
var data []models.Productlocations
if err := c.BodyParser(&data); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"code": 400,
"message": "Invalid request body",
"status": false,
})
}
err := domain.CreateProductLocation(data)
if err != nil {
return c.JSON(fiber.Map{
"status": false,
"code": http.StatusConflict,
"message": err,
})
}
return c.JSON(fiber.Map{
"status": true,
"code": http.StatusCreated,
"message": "Success",
})
}
func UpdateProductLocation(c *fiber.Ctx) error {
var data models.Productlocations
if err := c.BodyParser(&data); err != nil {
return err
}
err := domain.UpdateProductLocation(data)
if err != nil {
return c.JSON(fiber.Map{
"status": false,
"code": http.StatusConflict,
"message": err,
})
}
return c.JSON(fiber.Map{
"status": true,
"code": http.StatusAccepted,
"message": "Product update successful",
})
}
func CreateProductStock(c *fiber.Ctx) error {
var stocks []models.Productstock
if err := c.BodyParser(&stocks); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"code": 400,
"message": "Invalid request body",
"status": false,
})
}
for i := range stocks {
stocks[i].Stockdate = time.Now()
}
// Insert all stock records
if err := db.DB.Table("productstocks").Create(&stocks).Error; err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"code": 500,
"message": "Failed to create product stocks",
"status": false,
})
}
// Collect and dedupe product IDs to update their status
idMap := make(map[int]struct{})
var productIDs []int
for _, s := range stocks {
if s.Productid <= 0 {
continue
}
if _, ok := idMap[s.Productid]; !ok {
idMap[s.Productid] = struct{}{}
productIDs = append(productIDs, s.Productid)
}
}
// Only update if we have product ids
if len(productIDs) > 0 {
if err := db.DB.Table("products").
Where("productid IN ?", productIDs).
Update("productstatus", "available").Error; err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"code": 500,
"message": "Failed to update product status",
"status": false,
})
}
}
return c.Status(fiber.StatusCreated).JSON(fiber.Map{
"code": 201,
"message": "Product stocks created successfully",
"status": true,
"details": stocks,
})
}
func UpdateProductStock(c *fiber.Ctx) error {
var stock models.Productstock
// Parse body
if err := c.BodyParser(&stock); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"code": 400,
"message": "Invalid request body",
"status": false,
})
}
// Validate required ID
if stock.Productstockid == 0 {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"code": 400,
"message": "Missing productstockid",
"status": false,
})
}
// Attempt update
if err := domain.UpdateProductStock(&stock); err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"code": 500,
"message": "Failed to update product stock",
"status": false,
})
}
return c.Status(fiber.StatusOK).JSON(fiber.Map{
"code": 200,
"message": "Product stock updated successfully",
"status": true,
})
}
func GetProductStocks(c *fiber.Ctx) error {
tenantID := c.Query("tenantid")
locationID := c.Query("locationid")
stocks, err := domain.GetProductStocks(tenantID, locationID)
if err != nil {
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
"code": 500,
"message": "Failed to fetch product stocks",
"status": false,
})
}
return c.Status(http.StatusOK).JSON(fiber.Map{
"code": 200,
"message": "Product stocks fetched successfully",
"status": true,
"data": stocks,
})
}
func GetLocationProducts(c *fiber.Ctx) error {
tenantid, _ := strconv.Atoi(c.Query("tenantid"))
locationid, _ := strconv.Atoi(c.Query("locationid"))
subcategoryid, _ := strconv.Atoi(c.Query("subcategoryid"))
keyword := c.Query("keyword")
pageno, _ := strconv.Atoi(c.Query("pageno"))
pagesize, _ := strconv.Atoi(c.Query("pagesize"))
result := domain.GetLocationProducts(tenantid, locationid, subcategoryid, pageno, pagesize, keyword)
return c.JSON(fiber.Map{
"status": true,
"code": http.StatusOK,
"message": "Success",
"details": result,
})
}
func GetSubCategoryWiseSummary(c *fiber.Ctx) error {
tenantId, _ := strconv.Atoi(c.Query("tenantid"))
locationid, _ := strconv.Atoi(c.Query("locationid"))
subcategoryid, _ := strconv.Atoi(c.Query("subcategoryid"))
data, err := domain.GetSubCategoryWiseSummary(tenantId, locationid, subcategoryid)
if err != nil {
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
"code": 500,
"status": false,
"message": "Failed to fetch subcategory-wise summary",
})
}
if data == nil {
data = []models.SubCategorySummary{}
}
return c.Status(http.StatusOK).JSON(fiber.Map{
"code": http.StatusOK,
"message": "Subcategory-wise summary fetched successfully",
"status": true,
"details": data,
})
}
func GetLocationProductSummary(c *fiber.Ctx) error {
tenantid, _ := strconv.Atoi(c.Query("tenantid"))
locationid, _ := strconv.Atoi(c.Query("locationid"))
result := domain.GetLocationProductSummary(tenantid, locationid)
return c.JSON(fiber.Map{
"status": true,
"code": http.StatusOK,
"message": "Success",
"details": result,
})
}
func GetStockStatementSummary(c *fiber.Ctx) error {
tenantId, _ := strconv.Atoi(c.Query("tenantid"))
locationid, _ := strconv.Atoi(c.Query("locationid"))
data := domain.GetStockStatementSummary(tenantId, locationid)
return c.JSON(fiber.Map{
"code": http.StatusOK,
"message": "Success",
"status": true,
"details": data,
})
}
func CreateProductDiscount(c *fiber.Ctx) error {
var data models.ProductDiscount
if err := c.BodyParser(&data); err != nil {
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"status": false,
"code": http.StatusBadRequest,
"message": "Invalid JSON body",
})
}
if len(data.Locationid) > 0 {
data.LocationidStr = strings.Join(data.Locationid, ",")
}
data.Status = "Active"
if err := domain.CreateProductDiscount(&data); err != nil {
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
"status": false,
"code": http.StatusInternalServerError,
"message": err.Error(),
})
}
return c.Status(http.StatusCreated).JSON(fiber.Map{
"status": true,
"code": http.StatusCreated,
"message": "Product discount created successfully",
"data": data,
})
}
func GetProductDiscounts(c *fiber.Ctx) error {
tid, _ := strconv.Atoi(c.Query("tenantid"))
lid := c.Query("locationid")
data := domain.GetProductDiscounts(tid, lid)
return c.JSON(fiber.Map{
"status": true,
"code": 200,
"message": "Success",
"details": data,
})
}