package controllers import ( "net/http" "strconv" "nearle/models" "nearle/services" "github.com/gofiber/fiber/v2" ) type StockRequestController struct { stockRequestService services.StockRequestService } func NewStockRequestController(stockRequestService services.StockRequestService) *StockRequestController { 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 { inputs, err := parseStockRequests(c) if err != nil { return c.JSON(fiber.Map{"code": http.StatusBadRequest, "message": "Invalid input", "status": false}) } if len(inputs) == 0 { return c.JSON(fiber.Map{"code": http.StatusBadRequest, "message": "send at least one request", "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]) } // 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 { tenantID, _ := strconv.Atoi(c.Query("tenantid", "0")) locationID, _ := strconv.Atoi(c.Query("locationid", "0")) status := c.Query("status", "") date := c.Query("date", "") pageNo, _ := strconv.Atoi(c.Query("pageno", "1")) pageSize, _ := strconv.Atoi(c.Query("pagesize", "50")) data, err := ctl.stockRequestService.GetStockRequests(tenantID, locationID, status, date, pageNo, pageSize) if err != nil { return c.JSON(fiber.Map{"code": http.StatusInternalServerError, "message": err.Error(), "status": false}) } 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"` 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}) } if input.Status == "" { return c.JSON(fiber.Map{"code": http.StatusBadRequest, "status": false, "message": "status is required"}) } 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}, }) }