From 5e998a54ebdb7c56d28c54c09d8d1e1a59c6a99c Mon Sep 17 00:00:00 2001 From: abhishek Date: Tue, 21 Jul 2026 16:17:51 +0530 Subject: [PATCH] stock update --- controllers/orderController.go | 8 ++- main.go | 2 +- repositories/orderRepository.go | 122 +++++++++++++++++++++++++++++++- 3 files changed, 126 insertions(+), 6 deletions(-) diff --git a/controllers/orderController.go b/controllers/orderController.go index 8395a58..16f25a1 100644 --- a/controllers/orderController.go +++ b/controllers/orderController.go @@ -322,8 +322,12 @@ func (ctl *OrderController) CreateOrderv3(c *fiber.Ctx) error { order, err := ctl.orderService.CreateOrder(data) if err != nil { log.Println("CreateOrder service error:", err) - return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ - "code": http.StatusInternalServerError, + statusCode := http.StatusInternalServerError + if strings.Contains(strings.ToLower(err.Error()), "insufficient stock") { + statusCode = http.StatusConflict + } + return c.Status(statusCode).JSON(fiber.Map{ + "code": statusCode, "message": err.Error(), "status": false, }) diff --git a/main.go b/main.go index 36ebb48..748b780 100644 --- a/main.go +++ b/main.go @@ -38,7 +38,7 @@ func main() { fmt.Println("🌐 Connecting to databases...") db.Connect() fmt.Println("✅ Database connections established!") - + // Ensure schema is updated db.DB.AutoMigrate(&models.StockRequest{}) diff --git a/repositories/orderRepository.go b/repositories/orderRepository.go index a52535b..90149d1 100644 --- a/repositories/orderRepository.go +++ b/repositories/orderRepository.go @@ -4,6 +4,7 @@ import ( "fmt" "log" "nearle/models" + "strings" "time" "gorm.io/gorm" @@ -1008,6 +1009,48 @@ func (r *orderRepository) GetOrderDetails(orderHeaderID int) ([]models.OrderDeta func (r *orderRepository) UpdateOrder(order *models.Orders) error { tx := r.db.Begin() + // Handle stock restoration on order cancellation + var existingOrder models.Orders + if err := tx.Where("orderheaderid = ?", order.Orderheaderid).First(&existingOrder).Error; err == nil { + newStatus := strings.ToLower(strings.TrimSpace(order.Orderstatus)) + oldStatus := strings.ToLower(strings.TrimSpace(existingOrder.Orderstatus)) + + if newStatus == "cancelled" && oldStatus != "cancelled" { + var items []models.OrderDetail + if err := tx.Table("orderdetails").Where("orderheaderid = ?", order.Orderheaderid).Find(&items).Error; err == nil { + for _, item := range items { + itemLocID := item.Locationid + if itemLocID == 0 { + itemLocID = existingOrder.Locationid + } + qty := int(item.Orderqty) + if qty <= 0 { + qty = 1 + } + + restoredStock := models.Productstock{ + Tenantid: existingOrder.Tenantid, + Stockdate: time.Now(), + Locationid: itemLocID, + Productid: item.Productid, + Quantity: qty, + Stocktype: "in", + Status: "Active", + } + if err := tx.Table("productstocks").Create(&restoredStock).Error; err != nil { + tx.Rollback() + return err + } + + // Update productlocation status back to available if stock is restored + tx.Table("productlocations"). + Where("productid = ? AND tenantid = ? AND locationid = ?", item.Productid, existingOrder.Tenantid, itemLocID). + Update("status", "available") + } + } + } + } + if err := tx.Where("orderheaderid = ?", order.Orderheaderid).Updates(order).Error; err != nil { tx.Rollback() return err @@ -1082,6 +1125,50 @@ func (r *orderRepository) updateSeqno(tid int, prefix string) error { func (r *orderRepository) CreateOrder(data models.Orders) (models.Orders, error) { tx := r.db.Begin() + locID := data.Locationid + if locID == 0 { + locID = data.Applocationid + } + + // 🛠️ Step 1: Pre-validate stock availability for all items before placing order + for _, item := range data.Items { + itemLocID := item.Locationid + if itemLocID == 0 { + itemLocID = locID + } + + requestedQty := int(item.Orderqty) + if requestedQty <= 0 { + requestedQty = 1 + } + + var availableStock int + stockQuery := ` + SELECT COALESCE( + SUM(CASE WHEN LOWER(stocktype) = 'in' THEN quantity ELSE 0 END) - + SUM(CASE WHEN LOWER(stocktype) = 'out' THEN quantity ELSE 0 END), + 0 + ) + FROM productstocks + WHERE productid = ? AND tenantid = ? AND locationid = ? + ` + if err := tx.Raw(stockQuery, item.Productid, data.Tenantid, itemLocID).Scan(&availableStock).Error; err != nil { + tx.Rollback() + return models.Orders{}, fmt.Errorf("failed to verify stock for product %d: %w", item.Productid, err) + } + + // If stock tracking exists and available stock is less than requested quantity, block order placement + if availableStock < requestedQty { + tx.Rollback() + pName := item.Productname + if pName == "" { + pName = fmt.Sprintf("ID %d", item.Productid) + } + return models.Orders{}, fmt.Errorf("insufficient stock for product '%s': requested %d, available %d", pName, requestedQty, availableStock) + } + } + + // 🛠️ Step 2: Create Order Header data.Orderid = r.getSequenceno(data.Tenantid, "ORD") if err := tx.Create(&data).Error; err != nil { @@ -1089,22 +1176,32 @@ func (r *orderRepository) CreateOrder(data models.Orders) (models.Orders, error) return models.Orders{}, err } + // 🛠️ Step 3: Insert Order Details & Record "out" stock deduction for _, item := range data.Items { item.Orderheaderid = data.Orderheaderid item.Tenantid = data.Tenantid - item.Locationid = data.Locationid + itemLocID := item.Locationid + if itemLocID == 0 { + itemLocID = locID + } + item.Locationid = itemLocID if err := tx.Table("orderdetails").Create(&item).Error; err != nil { tx.Rollback() return models.Orders{}, err } + qty := int(item.Orderqty) + if qty <= 0 { + qty = 1 + } + stock := models.Productstock{ Tenantid: data.Tenantid, Stockdate: time.Now(), - Locationid: data.Locationid, + Locationid: itemLocID, Productid: item.Productid, - Quantity: int(item.Orderqty), + Quantity: qty, Stocktype: "out", Status: "Active", } @@ -1112,6 +1209,25 @@ func (r *orderRepository) CreateOrder(data models.Orders) (models.Orders, error) tx.Rollback() return models.Orders{}, err } + + // Update productlocation status to 'outofstock' if remaining stock drops to 0 or below + var remainingStock int + remStockQuery := ` + SELECT COALESCE( + SUM(CASE WHEN LOWER(stocktype) = 'in' THEN quantity ELSE 0 END) - + SUM(CASE WHEN LOWER(stocktype) = 'out' THEN quantity ELSE 0 END), + 0 + ) + FROM productstocks + WHERE productid = ? AND tenantid = ? AND locationid = ? + ` + if err := tx.Raw(remStockQuery, item.Productid, data.Tenantid, itemLocID).Scan(&remainingStock).Error; err == nil { + if remainingStock <= 0 { + tx.Table("productlocations"). + Where("productid = ? AND tenantid = ? AND locationid = ?", item.Productid, data.Tenantid, itemLocID). + Update("status", "outofstock") + } + } } if err := r.updateSeqno(data.Tenantid, "ORD"); err != nil {