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:
@@ -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
|
// Double check we have the required ID
|
||||||
if data.Tenantid == 0 {
|
if data.Tenantid == 0 {
|
||||||
return c.Status(http.StatusConflict).JSON(fiber.Map{
|
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) == "" {
|
if strings.TrimSpace(data.Orderdate) == "" {
|
||||||
data.Orderdate = time.Now().Format("2006-01-02 15:04:05")
|
data.Orderdate = time.Now().Format("2006-01-02 15:04:05")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user