bulk request and approve

This commit is contained in:
2026-09-02 16:50:37 +05:30
parent e45da9f7df
commit 2401158190
3 changed files with 273 additions and 15 deletions

View File

@@ -18,22 +18,59 @@ func NewStockRequestController(stockRequestService services.StockRequestService)
return &StockRequestController{stockRequestService: stockRequestService}
}
// CreateStockRequest accepts one request or a list of them.
//
// A shop restocking after a delivery is asking for twenty things at once, and
// sending twenty HTTP requests to say so is slow, gives no single answer, and
// leaves a half-sent batch behind when the connection drops. The single-object
// form is unchanged, so every existing caller keeps working.
//
// Each row is reported individually rather than the batch failing whole: a
// request for a product that no longer exists should not discard the other
// nineteen, and the requester needs to know WHICH one it was.
func (ctl *StockRequestController) CreateStockRequest(c *fiber.Ctx) error {
var input models.StockRequest
if err := c.BodyParser(&input); err != nil {
inputs, err := parseStockRequests(c)
if err != nil {
return c.JSON(fiber.Map{"code": http.StatusBadRequest, "message": "Invalid input", "status": false})
}
if input.Status == "" {
input.Status = "Pending"
if len(inputs) == 0 {
return c.JSON(fiber.Map{"code": http.StatusBadRequest, "message": "send at least one request", "status": false})
}
err := ctl.stockRequestService.CreateStockRequest(&input)
if err != nil {
return c.JSON(fiber.Map{"code": http.StatusInternalServerError, "message": err.Error(), "status": false})
created := make([]models.StockRequest, 0, len(inputs))
failed := make([]fiber.Map, 0)
for i := range inputs {
if inputs[i].Status == "" {
inputs[i].Status = "Pending"
}
if err := ctl.stockRequestService.CreateStockRequest(&inputs[i]); err != nil {
failed = append(failed, fiber.Map{
"productid": inputs[i].Productid,
"reason": err.Error(),
})
continue
}
created = append(created, inputs[i])
}
return c.JSON(fiber.Map{"code": 200, "message": "Stock request created", "status": true, "details": input})
// A single-object caller gets the object back, exactly as before.
if len(inputs) == 1 && len(failed) == 0 {
return c.JSON(fiber.Map{"code": 200, "message": "Stock request created", "status": true, "details": created[0]})
}
if len(created) == 0 {
return c.JSON(fiber.Map{
"code": http.StatusInternalServerError, "status": false,
"message": "no requests could be created", "details": fiber.Map{"failed": failed},
})
}
return c.JSON(fiber.Map{
"code": 200, "status": true,
"message": stockBatchMessage(len(created), len(failed), "created"),
"details": fiber.Map{"created": created, "failed": failed},
})
}
func (ctl *StockRequestController) GetStockRequests(c *fiber.Ctx) error {
@@ -52,19 +89,70 @@ func (ctl *StockRequestController) GetStockRequests(c *fiber.Ctx) error {
return c.JSON(fiber.Map{"code": 200, "message": "Success", "status": true, "details": data})
}
// UpdateStockRequest decides one request or a batch of them.
//
// `requestid` for one, `requestids` for many, one status for the batch. That
// shape rather than a list of {id,status} pairs because the action a merchant
// takes is "approve these" or "reject these" — a mixed batch is two actions,
// and letting one call do both makes an accidental mass-approve possible.
//
// Approving is not a status write: the service adds the requested quantity to
// the branch’s stock. So each id is applied on its own and reported on its own.
// If the fourth of ten fails, the first three have really been received and the
// merchant has to know that, rather than being told the batch failed and
// approving it a second time.
func (ctl *StockRequestController) UpdateStockRequest(c *fiber.Ctx) error {
var input struct {
RequestID int `json:"requestid"`
Status string `json:"status"`
RequestID int `json:"requestid"`
RequestIDs []int `json:"requestids"`
Status string `json:"status"`
}
if err := c.BodyParser(&input); err != nil {
return c.JSON(fiber.Map{"code": http.StatusBadRequest, "message": "Invalid input", "status": false})
}
err := ctl.stockRequestService.UpdateStockRequest(input.RequestID, input.Status)
if err != nil {
return c.JSON(fiber.Map{"code": http.StatusInternalServerError, "message": err.Error(), "status": false})
if input.Status == "" {
return c.JSON(fiber.Map{"code": http.StatusBadRequest, "status": false,
"message": "status is required"})
}
return c.JSON(fiber.Map{"code": 200, "message": "Stock request updated", "status": true})
ids := input.RequestIDs
if input.RequestID != 0 {
ids = append([]int{input.RequestID}, ids...)
}
ids = dedupeIDs(ids)
if len(ids) == 0 {
return c.JSON(fiber.Map{"code": http.StatusBadRequest, "status": false,
"message": "send requestid, or requestids for several"})
}
updated := make([]int, 0, len(ids))
failed := make([]fiber.Map, 0)
for _, id := range ids {
if err := ctl.stockRequestService.UpdateStockRequest(id, input.Status); err != nil {
failed = append(failed, fiber.Map{"requestid": id, "reason": err.Error()})
continue
}
updated = append(updated, id)
}
// The single-id caller keeps the answer it has always had.
if len(ids) == 1 {
if len(failed) > 0 {
return c.JSON(fiber.Map{"code": http.StatusInternalServerError, "status": false,
"message": failed[0]["reason"]})
}
return c.JSON(fiber.Map{"code": 200, "message": "Stock request updated", "status": true})
}
if len(updated) == 0 {
return c.JSON(fiber.Map{"code": http.StatusInternalServerError, "status": false,
"message": "no requests could be updated", "details": fiber.Map{"failed": failed}})
}
return c.JSON(fiber.Map{
"code": 200, "status": true,
"message": stockBatchMessage(len(updated), len(failed), "updated"),
"details": fiber.Map{"updated": updated, "failed": failed},
})
}