Sync stock status on restock and expose live stock on getproductbyvariant

Two gaps found while auditing the order/stock flow:

- Receiving stock via an approved stock request only updated
  products.productstatus (a global per-product flag). A location
  flagged outofstock by CreateOrder never got reactivated, since
  nothing touched productlocations.status on the way back in.
  CreateProductStock now reactivates the specific
  (tenant, location, product) row to "available" for every "in"
  entry, the counterpart to how it gets flagged out.

- getproductbyvariant returned no stock info at all, so the app could
  only find out a product was unavailable from the 409 at order time.
  It now accepts an optional locationid and, when passed, returns
  live productstock (same SUM(in)-SUM(out) formula the order check
  uses) and locationstatus per product. Omitting locationid keeps the
  old response shape.

Added MOBILE_ORDER_VERIFICATION.md as a handoff doc for the mobile
team covering the expected request/response shapes and how to verify
their integration.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Suriya
2026-07-21 18:01:39 +05:30
parent af55325a5b
commit d3a7466f4c
5 changed files with 204 additions and 13 deletions

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"nearle/models"
"nearle/repositories"
"strings"
"time"
)
@@ -23,7 +24,7 @@ type ProductService interface {
GetLocationProducts(tenantID, locationID, subcategoryID, pageno, pagesize int, keyword string) ([]models.Locationproducts, error)
GetLocationProductSummary(tenantID, locationID int) ([]models.ProductSummary, error)
FetchFilteredProducts(categoryID, subcategoryID, productID, applocationID, tenantID, locationID int, keyword, productStatus, approve string, pageno, pagesize int) ([]models.Tenantproducts, error)
GetProductByVariant(tenantid, variantid int) ([]models.Products, error)
GetProductByVariant(tenantid, variantid, locationid int) ([]models.Products, error)
GetProductsBySubcategory(params models.ProductFilter) (map[string]interface{}, error)
UpdateProductLocation(input models.Productlocations) error
CreateProductLocation(input []models.Productlocations) error
@@ -78,11 +79,22 @@ func (s *productService) CreateProductStock(stocks []models.Productstock) error
idMap := make(map[int]struct{})
var productIDs []int
for _, s := range stocks {
if s.Productid > 0 {
if _, exists := idMap[s.Productid]; !exists {
idMap[s.Productid] = struct{}{}
productIDs = append(productIDs, s.Productid)
locMap := make(map[models.ProductLocationRef]struct{})
var locRefs []models.ProductLocationRef
for _, stk := range stocks {
if stk.Productid > 0 {
if _, exists := idMap[stk.Productid]; !exists {
idMap[stk.Productid] = struct{}{}
productIDs = append(productIDs, stk.Productid)
}
}
// Only "in" entries mean stock actually arrived — an "out" entry
// (a sale) should never flip a location back to available.
if stk.Productid > 0 && stk.Locationid > 0 && stk.Tenantid > 0 && strings.EqualFold(stk.Stocktype, "in") {
ref := models.ProductLocationRef{Tenantid: stk.Tenantid, Locationid: stk.Locationid, Productid: stk.Productid}
if _, exists := locMap[ref]; !exists {
locMap[ref] = struct{}{}
locRefs = append(locRefs, ref)
}
}
}
@@ -93,6 +105,12 @@ func (s *productService) CreateProductStock(stocks []models.Productstock) error
}
}
if len(locRefs) > 0 {
if err := s.repo.ReactivateProductLocations(locRefs); err != nil {
return err
}
}
return nil
}
@@ -129,11 +147,11 @@ func (s *productService) FetchFilteredProducts(categoryID, subcategoryID, produc
return s.repo.FetchFilteredProducts(categoryID, subcategoryID, productID, applocationID, tenantID, locationID, keyword, productStatus, approve, pageno, pagesize)
}
func (s *productService) GetProductByVariant(tenantid, variantid int) ([]models.Products, error) {
func (s *productService) GetProductByVariant(tenantid, variantid, locationid int) ([]models.Products, error) {
var data []models.Products
result, err := s.repo.GetProductByVariant(tenantid, variantid)
result, err := s.repo.GetProductByVariant(tenantid, variantid, locationid)
if err != nil {
return nil, err