From af55325a5b71dc787f5657647c4aba3711068e4e Mon Sep 17 00:00:00 2001 From: Suriya Date: Tue, 21 Jul 2026 18:01:26 +0530 Subject: [PATCH] Stop silently dropping order line items MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every order created after orderheaderid 146119 had zero rows in orderdetails — the header saved fine but items never made it in, which also meant the stock pre-validation loop (it iterates over data.Items) never ran, so an order could go through without ever checking stock. Root cause: whichever client is sending these sends items as a sibling of "orders" rather than nested inside it, a shape neither existing parse strategy captures, so encoding/json silently dropped it. Add a third parse fallback for that sibling-items shape, and reject any order with zero items outright instead of letting it through as a phantom header-only row. Co-Authored-By: Claude Sonnet 5 --- controllers/orderController.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/controllers/orderController.go b/controllers/orderController.go index 16f25a1..a311a35 100644 --- a/controllers/orderController.go +++ b/controllers/orderController.go @@ -303,6 +303,24 @@ func (ctl *OrderController) CreateOrderv3(c *fiber.Ctx) error { } } + // 🛠️ Strategy 3: some clients send the header under "orders" but the line + // items as a SIBLING top-level "items" array rather than nested inside + // it. Strategy 2's OrderWrapper only has an "orders" field, so + // encoding/json silently drops that sibling key — the order header + // parses fine but data.Items ends up empty, which used to let the order + // go through with zero items and skip the stock check entirely (the + // pre-validation loop below iterates over data.Items). Pick it up here + // if strategies 1/2 left Items empty. + if len(data.Items) == 0 { + type ItemsWrapper struct { + Items []models.OrderDetail `json:"items"` + } + var itemsWrapper ItemsWrapper + if err := c.BodyParser(&itemsWrapper); err == nil && len(itemsWrapper.Items) > 0 { + data.Items = itemsWrapper.Items + } + } + // Double check we have the required ID if data.Tenantid == 0 { return c.Status(http.StatusConflict).JSON(fiber.Map{ @@ -312,6 +330,18 @@ func (ctl *OrderController) CreateOrderv3(c *fiber.Ctx) error { }) } + // An order with no line items has nothing to check stock against — the + // pre-validation loop in CreateOrder simply wouldn't run, silently + // creating a phantom header-only order that never deducted stock. + // Reject it outright instead. + if len(data.Items) == 0 { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "code": http.StatusBadRequest, + "message": "Order must contain at least one item", + "status": false, + }) + } + if strings.TrimSpace(data.Orderdate) == "" { data.Orderdate = time.Now().Format("2006-01-02 15:04:05") }