stock update
This commit is contained in:
@@ -322,8 +322,12 @@ func (ctl *OrderController) CreateOrderv3(c *fiber.Ctx) error {
|
|||||||
order, err := ctl.orderService.CreateOrder(data)
|
order, err := ctl.orderService.CreateOrder(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("CreateOrder service error:", err)
|
log.Println("CreateOrder service error:", err)
|
||||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
|
statusCode := http.StatusInternalServerError
|
||||||
"code": 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(),
|
"message": err.Error(),
|
||||||
"status": false,
|
"status": false,
|
||||||
})
|
})
|
||||||
|
|||||||
2
main.go
2
main.go
@@ -38,7 +38,7 @@ func main() {
|
|||||||
fmt.Println("🌐 Connecting to databases...")
|
fmt.Println("🌐 Connecting to databases...")
|
||||||
db.Connect()
|
db.Connect()
|
||||||
fmt.Println("✅ Database connections established!")
|
fmt.Println("✅ Database connections established!")
|
||||||
|
|
||||||
// Ensure schema is updated
|
// Ensure schema is updated
|
||||||
db.DB.AutoMigrate(&models.StockRequest{})
|
db.DB.AutoMigrate(&models.StockRequest{})
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"nearle/models"
|
"nearle/models"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
@@ -1008,6 +1009,48 @@ func (r *orderRepository) GetOrderDetails(orderHeaderID int) ([]models.OrderDeta
|
|||||||
func (r *orderRepository) UpdateOrder(order *models.Orders) error {
|
func (r *orderRepository) UpdateOrder(order *models.Orders) error {
|
||||||
tx := r.db.Begin()
|
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 {
|
if err := tx.Where("orderheaderid = ?", order.Orderheaderid).Updates(order).Error; err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return err
|
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) {
|
func (r *orderRepository) CreateOrder(data models.Orders) (models.Orders, error) {
|
||||||
tx := r.db.Begin()
|
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")
|
data.Orderid = r.getSequenceno(data.Tenantid, "ORD")
|
||||||
|
|
||||||
if err := tx.Create(&data).Error; err != nil {
|
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
|
return models.Orders{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 🛠️ Step 3: Insert Order Details & Record "out" stock deduction
|
||||||
for _, item := range data.Items {
|
for _, item := range data.Items {
|
||||||
item.Orderheaderid = data.Orderheaderid
|
item.Orderheaderid = data.Orderheaderid
|
||||||
item.Tenantid = data.Tenantid
|
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 {
|
if err := tx.Table("orderdetails").Create(&item).Error; err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return models.Orders{}, err
|
return models.Orders{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qty := int(item.Orderqty)
|
||||||
|
if qty <= 0 {
|
||||||
|
qty = 1
|
||||||
|
}
|
||||||
|
|
||||||
stock := models.Productstock{
|
stock := models.Productstock{
|
||||||
Tenantid: data.Tenantid,
|
Tenantid: data.Tenantid,
|
||||||
Stockdate: time.Now(),
|
Stockdate: time.Now(),
|
||||||
Locationid: data.Locationid,
|
Locationid: itemLocID,
|
||||||
Productid: item.Productid,
|
Productid: item.Productid,
|
||||||
Quantity: int(item.Orderqty),
|
Quantity: qty,
|
||||||
Stocktype: "out",
|
Stocktype: "out",
|
||||||
Status: "Active",
|
Status: "Active",
|
||||||
}
|
}
|
||||||
@@ -1112,6 +1209,25 @@ func (r *orderRepository) CreateOrder(data models.Orders) (models.Orders, error)
|
|||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return models.Orders{}, err
|
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 {
|
if err := r.updateSeqno(data.Tenantid, "ORD"); err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user