product on shelf
This commit is contained in:
@@ -130,3 +130,30 @@ func (ctl *CatalogueUploadController) MarkShelved(c *fiber.Ctx) error {
|
||||
}
|
||||
return c.JSON(fiber.Map{"code": 200, "message": "Shelving recorded", "status": true})
|
||||
}
|
||||
|
||||
// AttachSheet supplies the prices and opening stock for a receipt that has
|
||||
// none, so its products can still be put on a shelf.
|
||||
//
|
||||
// Needed because the receipt and the sheet used to part company. The ingest
|
||||
// service holds neither price nor stock — its catalogue is shared by every
|
||||
// merchant — so both live only in the spreadsheet, and a receipt written before
|
||||
// those rows were stored has no way to finish. Handing the file over again is
|
||||
// the only way back, and this is where it lands.
|
||||
func (ctl *CatalogueUploadController) AttachSheet(c *fiber.Ctx) error {
|
||||
var input struct {
|
||||
Batchid string `json:"batchid"`
|
||||
Sheetrows string `json:"sheetrows"`
|
||||
}
|
||||
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) == "" || strings.TrimSpace(input.Sheetrows) == "" {
|
||||
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
|
||||
"code": http.StatusBadRequest, "message": "batchid and sheetrows are both required", "status": false,
|
||||
})
|
||||
}
|
||||
if err := ctl.service.AttachSheet(input.Batchid, input.Sheetrows); err != nil {
|
||||
return c.JSON(fiber.Map{"code": http.StatusInternalServerError, "message": err.Error(), "status": false})
|
||||
}
|
||||
return c.JSON(fiber.Map{"code": 200, "message": "Sheet attached", "status": true})
|
||||
}
|
||||
|
||||
13
main.go
13
main.go
@@ -156,6 +156,19 @@ func main() {
|
||||
ON catalogueuploads (tenantid, locationid, created DESC)`).Error; err != nil {
|
||||
log.Println("⚠️ could not add catalogueuploads scope index:", err)
|
||||
}
|
||||
// The sheet's own prices and opening stock, kept so shelving can happen
|
||||
// after the browser that uploaded it is gone.
|
||||
//
|
||||
// Added separately from the CREATE above because the table already exists in
|
||||
// production without it. The ingest service holds none of this — its
|
||||
// catalogue is shared by every merchant and carries no price and no stock —
|
||||
// so before this column the join could only be made in the tab that did the
|
||||
// upload, and that tab is normally long closed by the time their admin
|
||||
// releases the drop and the run finishes.
|
||||
if err := db.DB.Exec(
|
||||
`ALTER TABLE catalogueuploads ADD COLUMN IF NOT EXISTS sheetrows jsonb`).Error; err != nil {
|
||||
log.Fatal("could not add catalogueuploads.sheetrows:", err)
|
||||
}
|
||||
|
||||
f := facade.NewFacade(db.DB, db.CatalogueDB)
|
||||
|
||||
|
||||
@@ -59,6 +59,24 @@ type CatalogueUpload struct {
|
||||
// the service reports, so a drop that never runs still says how big it was.
|
||||
Rowcount int `json:"rowcount" gorm:"column:rowcount"`
|
||||
|
||||
// The sheet itself, as the console parsed it: SKU, price and opening stock
|
||||
// per row, as JSON.
|
||||
//
|
||||
// Stored because the ingest service cannot hold it and nothing else can.
|
||||
// Their pipeline writes the GLOBAL catalogue, which every merchant shares
|
||||
// and which therefore carries no price and no stock; both live only in the
|
||||
// sheet. Shelving is the step that joins the two, and it used to be possible
|
||||
// only in the browser tab that did the upload, because that tab was the only
|
||||
// place the parsed rows existed.
|
||||
//
|
||||
// That failed in the ordinary case rather than a rare one. A drop waits for
|
||||
// their admin to release it and the run then takes minutes, so by the time
|
||||
// there is anything to shelve the tab is usually gone — and the products sit
|
||||
// in the global catalogue, unpriced and unstocked, with no way left to
|
||||
// finish. Keeping the rows here is what lets the Uploads page complete it
|
||||
// days later.
|
||||
Sheetrows string `json:"sheetrows" gorm:"column:sheetrows;type:jsonb"`
|
||||
|
||||
// ── Cached from the ingest service, by whoever last polled ──────────────
|
||||
|
||||
Laststatus string `json:"laststatus" gorm:"column:laststatus;default:pending"`
|
||||
|
||||
@@ -15,6 +15,7 @@ type CatalogueUploadRepository interface {
|
||||
List(tenantID, locationID, pageNo, pageSize int) ([]models.CatalogueUpload, error)
|
||||
UpdateStatus(upload *models.CatalogueUpload) error
|
||||
MarkShelved(batchID string, shelved, skipped int) error
|
||||
AttachSheet(batchID, sheetRows string) error
|
||||
}
|
||||
|
||||
type catalogueUploadRepository struct {
|
||||
@@ -148,3 +149,29 @@ func (r *catalogueUploadRepository) MarkShelved(batchID string, shelved, skipped
|
||||
"shelvedat": gorm.Expr("CURRENT_TIMESTAMP"),
|
||||
}).Error
|
||||
}
|
||||
|
||||
// AttachSheet stores the sheet's rows against a receipt that has none.
|
||||
//
|
||||
// The rescue path for a receipt filed before the rows were kept, and for one
|
||||
// whose upload predates this column. The prices and opening stock exist only in
|
||||
// the spreadsheet; if the receipt was written without them, the only way back is
|
||||
// for somebody to hand the file over again.
|
||||
//
|
||||
// Deliberately not part of UpdateStatus. That call is made by any browser
|
||||
// polling the ingest service and must never touch what the console owns; this
|
||||
// one is a person supplying missing data, and the two should not be able to
|
||||
// happen by accident in each other's place.
|
||||
func (r *catalogueUploadRepository) AttachSheet(batchID, sheetRows string) error {
|
||||
if strings.TrimSpace(batchID) == "" {
|
||||
return errors.New("batchid is required")
|
||||
}
|
||||
if strings.TrimSpace(sheetRows) == "" {
|
||||
// Empty would be stored as `[]` and read back as "this sheet had no
|
||||
// rows", which is a different claim from "nobody has supplied them
|
||||
// yet" — and the second is what makes the shelve button appear.
|
||||
return errors.New("sheetrows is required")
|
||||
}
|
||||
return r.db.Model(&models.CatalogueUpload{}).
|
||||
Where("batchid = ?", batchID).
|
||||
Update("sheetrows", sheetRows).Error
|
||||
}
|
||||
|
||||
@@ -29,5 +29,9 @@ func RegisterUploadRoutes(api fiber.Router, f *facade.Facade) {
|
||||
// The other half: priced, shelved and stocked at a branch. Kept apart from
|
||||
// /update because the ingest service confirms the shared global catalogue
|
||||
// and knows nothing about a shop's shelf.
|
||||
// The rescue path: hand the spreadsheet over again for a receipt whose
|
||||
// prices and opening stock were never stored, so it can still be shelved.
|
||||
uploads.Put("/sheet", f.CatalogueUploadController.AttachSheet)
|
||||
|
||||
uploads.Put("/shelved", f.CatalogueUploadController.MarkShelved)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"nearle/models"
|
||||
"nearle/repositories"
|
||||
)
|
||||
@@ -23,6 +25,7 @@ type CatalogueUploadService interface {
|
||||
List(tenantID, locationID, pageNo, pageSize int) ([]models.CatalogueUpload, error)
|
||||
UpdateStatus(upload *models.CatalogueUpload) error
|
||||
MarkShelved(batchID string, shelved, skipped int) error
|
||||
AttachSheet(batchID, sheetRows string) error
|
||||
}
|
||||
|
||||
type catalogueUploadService struct {
|
||||
@@ -40,6 +43,13 @@ func (s *catalogueUploadService) Record(upload *models.CatalogueUpload) error {
|
||||
// the pipeline had it when it is in fact waiting for a person.
|
||||
upload.Laststatus = "pending"
|
||||
}
|
||||
if strings.TrimSpace(upload.Sheetrows) == "" {
|
||||
// `sheetrows` is jsonb, and Postgres rejects an empty string for it —
|
||||
// "invalid input syntax for type json" — which would fail the whole
|
||||
// receipt over a field nobody supplied. An empty ARRAY is the honest
|
||||
// value: no rows were sent, so there is nothing to shelve from later.
|
||||
upload.Sheetrows = "[]"
|
||||
}
|
||||
return s.repo.Record(upload)
|
||||
}
|
||||
|
||||
@@ -54,3 +64,7 @@ func (s *catalogueUploadService) UpdateStatus(upload *models.CatalogueUpload) er
|
||||
func (s *catalogueUploadService) MarkShelved(batchID string, shelved, skipped int) error {
|
||||
return s.repo.MarkShelved(batchID, shelved, skipped)
|
||||
}
|
||||
|
||||
func (s *catalogueUploadService) AttachSheet(batchID, sheetRows string) error {
|
||||
return s.repo.AttachSheet(batchID, sheetRows)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user