changes on agent

This commit is contained in:
2026-08-31 12:33:10 +05:30
parent 76554d26e5
commit ffc66462fc
9 changed files with 715 additions and 25 deletions

View File

@@ -0,0 +1,132 @@
package controllers
import (
"net/http"
"strconv"
"strings"
"nearle/models"
"nearle/services"
"github.com/gofiber/fiber/v2"
)
type CatalogueUploadController struct {
service services.CatalogueUploadService
}
func NewCatalogueUploadController(service services.CatalogueUploadService) *CatalogueUploadController {
return &CatalogueUploadController{service: service}
}
// RecordUpload stores the receipt for a spreadsheet just accepted by the ingest
// service.
//
// Called the moment the drop is accepted, before any polling — that is the one
// instant the batch id is guaranteed to exist and guaranteed not to have been
// lost. Everything after it (the review wait, the run, the shelving) can be
// recovered from the id; the id itself cannot be recovered from anything.
func (ctl *CatalogueUploadController) RecordUpload(c *fiber.Ctx) error {
var input models.CatalogueUpload
if err := c.BodyParser(&input); err != nil {
return c.JSON(fiber.Map{"code": http.StatusBadRequest, "message": "Invalid input", "status": false})
}
if strings.TrimSpace(input.Batchid) == "" {
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"code": http.StatusBadRequest,
"message": "batchid is required — it is the only credential for reading the result back",
"status": false,
})
}
if input.Tenantid <= 0 {
// A receipt with no tenant belongs to nobody and would be visible to
// every store login that asks for "all". Refused rather than stored
// unscoped.
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"code": http.StatusBadRequest,
"message": "tenantid is required — a receipt has to belong to a merchant",
"status": false,
})
}
if err := ctl.service.Record(&input); err != nil {
return c.JSON(fiber.Map{"code": http.StatusInternalServerError, "message": err.Error(), "status": false})
}
return c.JSON(fiber.Map{"code": 200, "message": "Upload recorded", "status": true, "details": input})
}
// GetUploads lists receipts for whoever is asking.
//
// `tenantid=0` means every tenant and is how a Nearle Admin sees the platform.
// The console decides which to send from the session — the same rule the import
// itself follows — because a store login reading another merchant's receipts
// would see what that merchant stocks.
func (ctl *CatalogueUploadController) GetUploads(c *fiber.Ctx) error {
tenantID, _ := strconv.Atoi(c.Query("tenantid", "0"))
locationID, _ := strconv.Atoi(c.Query("locationid", "0"))
pageNo, _ := strconv.Atoi(c.Query("pageno", "1"))
pageSize, _ := strconv.Atoi(c.Query("pagesize", "50"))
data, err := ctl.service.List(tenantID, locationID, pageNo, pageSize)
if err != nil {
return c.JSON(fiber.Map{"code": http.StatusInternalServerError, "message": err.Error(), "status": false})
}
// Never null. An empty list is a legitimate answer — a shop that has never
// uploaded — and a JSON null here makes every caller guard a case that is
// really just "none yet".
if data == nil {
data = []models.CatalogueUpload{}
}
return c.JSON(fiber.Map{"code": 200, "message": "Success", "status": true, "details": data})
}
// UpdateUpload caches what the ingest service last reported.
//
// Written by the browser that is polling, because that is where the reading
// happens: the batch id is the credential and the read is anonymous, so the
// person waiting is already holding the answer. This endpoint only writes it
// down so the next person does not have to wait for it again.
func (ctl *CatalogueUploadController) UpdateUpload(c *fiber.Ctx) error {
var input models.CatalogueUpload
if err := c.BodyParser(&input); err != nil {
return c.JSON(fiber.Map{"code": http.StatusBadRequest, "message": "Invalid input", "status": false})
}
if strings.TrimSpace(input.Batchid) == "" {
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"code": http.StatusBadRequest, "message": "batchid is required", "status": false,
})
}
if err := ctl.service.UpdateStatus(&input); err != nil {
return c.JSON(fiber.Map{"code": http.StatusInternalServerError, "message": err.Error(), "status": false})
}
return c.JSON(fiber.Map{"code": 200, "message": "Upload updated", "status": true})
}
// MarkShelved records that the products reached a branch's shelf with a price
// and their opening stock.
//
// Separate from UpdateUpload on purpose. The ingest service confirms the global
// catalogue, which every merchant shares and which therefore carries no price
// and no stock — so "added" and "on sale here" are two different claims, made by
// two different systems, and a receipt that ran them together would report
// products as sellable that no customer can buy.
func (ctl *CatalogueUploadController) MarkShelved(c *fiber.Ctx) error {
var input struct {
Batchid string `json:"batchid"`
Shelved int `json:"shelved"`
Skipped int `json:"skipped"`
}
if err := c.BodyParser(&input); err != nil {
return c.JSON(fiber.Map{"code": http.StatusBadRequest, "message": "Invalid input", "status": false})
}
if strings.TrimSpace(input.Batchid) == "" {
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"code": http.StatusBadRequest, "message": "batchid is required", "status": false,
})
}
if err := ctl.service.MarkShelved(input.Batchid, input.Shelved, input.Skipped); err != nil {
return c.JSON(fiber.Map{"code": http.StatusInternalServerError, "message": err.Error(), "status": false})
}
return c.JSON(fiber.Map{"code": 200, "message": "Shelving recorded", "status": true})
}