mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
Merge branch 'codex/sales-at-farm-level' into 'development'
codex/fix: inconsistent stock options and availability See merge request mbugroup/lti-api!391
This commit is contained in:
+12
@@ -3,6 +3,7 @@ package controller
|
|||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/dto"
|
"gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/dto"
|
||||||
service "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/services"
|
service "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/services"
|
||||||
@@ -27,11 +28,13 @@ func (u *ProductWarehouseController) GetAll(c *fiber.Ctx) error {
|
|||||||
query := &validation.Query{
|
query := &validation.Query{
|
||||||
Page: c.QueryInt("page", 1),
|
Page: c.QueryInt("page", 1),
|
||||||
Limit: c.QueryInt("limit", 10),
|
Limit: c.QueryInt("limit", 10),
|
||||||
|
Search: c.Query("search", ""),
|
||||||
ProductId: uint(c.QueryInt("product_id", 0)),
|
ProductId: uint(c.QueryInt("product_id", 0)),
|
||||||
WarehouseId: uint(c.QueryInt("warehouse_id", 0)),
|
WarehouseId: uint(c.QueryInt("warehouse_id", 0)),
|
||||||
LocationId: uint(c.QueryInt("location_id", 0)),
|
LocationId: uint(c.QueryInt("location_id", 0)),
|
||||||
Flags: c.Query("flags", ""),
|
Flags: c.Query("flags", ""),
|
||||||
KandangId: uint(c.QueryInt("kandang_id", 0)),
|
KandangId: uint(c.QueryInt("kandang_id", 0)),
|
||||||
|
AvailableOnly: parseBoolQuery(c.Query("available_only", "")),
|
||||||
TransferContext: c.Query(utils.TransferContextKey, ""),
|
TransferContext: c.Query(utils.TransferContextKey, ""),
|
||||||
StockMode: c.Query("stock_mode", ""),
|
StockMode: c.Query("stock_mode", ""),
|
||||||
Type: c.Query("type", ""),
|
Type: c.Query("type", ""),
|
||||||
@@ -61,6 +64,15 @@ func (u *ProductWarehouseController) GetAll(c *fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseBoolQuery(raw string) bool {
|
||||||
|
switch strings.TrimSpace(strings.ToLower(raw)) {
|
||||||
|
case "1", "true", "yes", "y":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (u *ProductWarehouseController) GetOne(c *fiber.Ctx) error {
|
func (u *ProductWarehouseController) GetOne(c *fiber.Ctx) error {
|
||||||
param := c.Params("id")
|
param := c.Params("id")
|
||||||
|
|
||||||
|
|||||||
+7
-1
@@ -34,7 +34,7 @@ func TestGetAllParsesLocationID(t *testing.T) {
|
|||||||
ctrl := NewProductWarehouseController(stub)
|
ctrl := NewProductWarehouseController(stub)
|
||||||
app.Get("/product-warehouses", ctrl.GetAll)
|
app.Get("/product-warehouses", ctrl.GetAll)
|
||||||
|
|
||||||
req := httptest.NewRequest("GET", "/product-warehouses?location_id=16&kandang_id=59&limit=25", nil)
|
req := httptest.NewRequest("GET", "/product-warehouses?location_id=16&kandang_id=59&limit=25&search=tektrol&available_only=true", nil)
|
||||||
resp, err := app.Test(req)
|
resp, err := app.Test(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
@@ -54,6 +54,12 @@ func TestGetAllParsesLocationID(t *testing.T) {
|
|||||||
if stub.lastQuery.Limit != 25 {
|
if stub.lastQuery.Limit != 25 {
|
||||||
t.Fatalf("expected limit 25, got %d", stub.lastQuery.Limit)
|
t.Fatalf("expected limit 25, got %d", stub.lastQuery.Limit)
|
||||||
}
|
}
|
||||||
|
if stub.lastQuery.Search != "tektrol" {
|
||||||
|
t.Fatalf("expected search tektrol, got %s", stub.lastQuery.Search)
|
||||||
|
}
|
||||||
|
if !stub.lastQuery.AvailableOnly {
|
||||||
|
t.Fatalf("expected available_only true")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStubImplementsServiceContract(t *testing.T) {
|
func TestStubImplementsServiceContract(t *testing.T) {
|
||||||
|
|||||||
@@ -71,6 +71,13 @@ func applyWarehouseSelectionFilter(db *gorm.DB, kandangID, locationID uint) *gor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func applyAvailableOnlyFilter(db *gorm.DB, availableOnly bool) *gorm.DB {
|
||||||
|
if !availableOnly {
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
return db.Where("COALESCE(product_warehouses.qty, 0) > 0")
|
||||||
|
}
|
||||||
|
|
||||||
func (s productWarehouseService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.ProductWarehouse, int64, error) {
|
func (s productWarehouseService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.ProductWarehouse, int64, error) {
|
||||||
if err := s.Validate.Struct(params); err != nil {
|
if err := s.Validate.Struct(params); err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
@@ -151,12 +158,31 @@ func (s productWarehouseService) GetAll(c *fiber.Ctx, params *validation.Query)
|
|||||||
db = db.Where("product_id = ?", params.ProductId)
|
db = db.Where("product_id = ?", params.ProductId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
db = applyAvailableOnlyFilter(db, params.AvailableOnly)
|
||||||
|
|
||||||
db = applyWarehouseSelectionFilter(db, params.KandangId, params.LocationId)
|
db = applyWarehouseSelectionFilter(db, params.KandangId, params.LocationId)
|
||||||
|
|
||||||
if params.WarehouseId != 0 {
|
if params.WarehouseId != 0 {
|
||||||
db = db.Where("warehouse_id = ?", params.WarehouseId)
|
db = db.Where("warehouse_id = ?", params.WarehouseId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.TrimSpace(params.Search) != "" {
|
||||||
|
searchPattern := "%" + strings.TrimSpace(params.Search) + "%"
|
||||||
|
db = db.Where(
|
||||||
|
`(
|
||||||
|
EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM products p_search
|
||||||
|
WHERE p_search.id = product_warehouses.product_id
|
||||||
|
AND p_search.name ILIKE ?
|
||||||
|
)
|
||||||
|
OR w_scope.name ILIKE ?
|
||||||
|
)`,
|
||||||
|
searchPattern,
|
||||||
|
searchPattern,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if len(marketingTypes) > 0 {
|
if len(marketingTypes) > 0 {
|
||||||
flagSet := make(map[string]struct{})
|
flagSet := make(map[string]struct{})
|
||||||
for _, t := range marketingTypes {
|
for _, t := range marketingTypes {
|
||||||
|
|||||||
+21
-6
@@ -49,6 +49,20 @@ func TestApplyWarehouseSelectionFilterSupportsLocationOnlyQuery(t *testing.T) {
|
|||||||
assertUintIDs(t, ids, []uint{1, 2, 3})
|
assertUintIDs(t, ids, []uint{1, 2, 3})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestApplyAvailableOnlyFilterRemovesZeroQtyRows(t *testing.T) {
|
||||||
|
db := setupProductWarehouseServiceTestDB(t)
|
||||||
|
|
||||||
|
var ids []uint
|
||||||
|
err := applyAvailableOnlyFilter(baseProductWarehouseSelectionQuery(db), true).
|
||||||
|
Order("product_warehouses.id").
|
||||||
|
Pluck("product_warehouses.id", &ids).Error
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertUintIDs(t, ids, []uint{1, 2, 4})
|
||||||
|
}
|
||||||
|
|
||||||
func setupProductWarehouseServiceTestDB(t *testing.T) *gorm.DB {
|
func setupProductWarehouseServiceTestDB(t *testing.T) *gorm.DB {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
@@ -67,18 +81,19 @@ func setupProductWarehouseServiceTestDB(t *testing.T) *gorm.DB {
|
|||||||
)`,
|
)`,
|
||||||
`CREATE TABLE product_warehouses (
|
`CREATE TABLE product_warehouses (
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
warehouse_id INTEGER NOT NULL
|
warehouse_id INTEGER NOT NULL,
|
||||||
|
qty NUMERIC NULL
|
||||||
)`,
|
)`,
|
||||||
`INSERT INTO warehouses (id, type, location_id, kandang_id, deleted_at) VALUES
|
`INSERT INTO warehouses (id, type, location_id, kandang_id, deleted_at) VALUES
|
||||||
(1, 'KANDANG', 101, 11, NULL),
|
(1, 'KANDANG', 101, 11, NULL),
|
||||||
(2, 'LOKASI', 101, NULL, NULL),
|
(2, 'LOKASI', 101, NULL, NULL),
|
||||||
(3, 'KANDANG', 101, 12, NULL),
|
(3, 'KANDANG', 101, 12, NULL),
|
||||||
(4, 'LOKASI', 102, NULL, NULL)`,
|
(4, 'LOKASI', 102, NULL, NULL)`,
|
||||||
`INSERT INTO product_warehouses (id, warehouse_id) VALUES
|
`INSERT INTO product_warehouses (id, warehouse_id, qty) VALUES
|
||||||
(1, 1),
|
(1, 1, 10),
|
||||||
(2, 2),
|
(2, 2, 20),
|
||||||
(3, 3),
|
(3, 3, 0),
|
||||||
(4, 4)`,
|
(4, 4, 15)`,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, stmt := range statements {
|
for _, stmt := range statements {
|
||||||
|
|||||||
+2
@@ -15,11 +15,13 @@ type Update struct {
|
|||||||
type Query struct {
|
type Query struct {
|
||||||
Page int `query:"page" validate:"omitempty,number,min=1"`
|
Page int `query:"page" validate:"omitempty,number,min=1"`
|
||||||
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"`
|
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"`
|
||||||
|
Search string `query:"search" validate:"omitempty"`
|
||||||
ProductId uint `query:"product_id" validate:"omitempty,number,min=1"`
|
ProductId uint `query:"product_id" validate:"omitempty,number,min=1"`
|
||||||
WarehouseId uint `query:"warehouse_id" validate:"omitempty,number,min=1"`
|
WarehouseId uint `query:"warehouse_id" validate:"omitempty,number,min=1"`
|
||||||
LocationId uint `query:"location_id" validate:"omitempty,number,min=1"`
|
LocationId uint `query:"location_id" validate:"omitempty,number,min=1"`
|
||||||
Flags string `query:"flags" validate:"omitempty"`
|
Flags string `query:"flags" validate:"omitempty"`
|
||||||
KandangId uint `query:"kandang_id" validate:"omitempty,number,min=1"`
|
KandangId uint `query:"kandang_id" validate:"omitempty,number,min=1"`
|
||||||
|
AvailableOnly bool `query:"available_only"`
|
||||||
TransferContext string `query:"transfer_context" validate:"omitempty,oneof=inventory_transfer"`
|
TransferContext string `query:"transfer_context" validate:"omitempty,oneof=inventory_transfer"`
|
||||||
StockMode string `query:"stock_mode" validate:"omitempty,oneof=exclude_chickin"`
|
StockMode string `query:"stock_mode" validate:"omitempty,oneof=exclude_chickin"`
|
||||||
Type string `query:"type" validate:"omitempty"`
|
Type string `query:"type" validate:"omitempty"`
|
||||||
|
|||||||
Reference in New Issue
Block a user