Stop silently dropping order line items

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 <noreply@anthropic.com>
This commit is contained in:
Suriya
2026-07-21 18:01:26 +05:30
parent 5bd1f52d41
commit af55325a5b

View File

@@ -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")
}