fix: master product and chickin

This commit is contained in:
Hafizh A. Y
2026-03-06 10:24:43 +07:00
parent 345fe32433
commit 650f8e0fdb
6 changed files with 78 additions and 22 deletions
@@ -0,0 +1,7 @@
BEGIN;
DELETE FROM fifo_stock_v2_flag_members
WHERE flag_name = 'AYAM'
AND flag_group_code = 'AYAM';
COMMIT;
@@ -0,0 +1,13 @@
BEGIN;
INSERT INTO fifo_stock_v2_flag_members(flag_name, flag_group_code, priority, is_active, created_at, updated_at)
VALUES
('AYAM', 'AYAM', 5, TRUE, NOW(), NOW())
ON CONFLICT (flag_name) DO UPDATE
SET
flag_group_code = EXCLUDED.flag_group_code,
priority = EXCLUDED.priority,
is_active = TRUE,
updated_at = NOW();
COMMIT;
@@ -30,6 +30,14 @@ func (u *ProductController) GetAll(c *fiber.Ctx) error {
ProductCategoryID: c.QueryInt("product_category_id", 0), ProductCategoryID: c.QueryInt("product_category_id", 0),
} }
if isDepletionParam := c.Query("is_depletion", ""); isDepletionParam != "" {
value, err := strconv.ParseBool(isDepletionParam)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "invalid is_depletion value")
}
query.IsDepletion = &value
}
if query.Page < 1 || query.Limit < 1 { if query.Page < 1 || query.Limit < 1 {
return fiber.NewError(fiber.StatusBadRequest, "page and limit must be greater than 0") return fiber.NewError(fiber.StatusBadRequest, "page and limit must be greater than 0")
} }
@@ -31,6 +31,12 @@ type productService struct {
Repository repository.ProductRepository Repository repository.ProductRepository
} }
var depletionProductFlags = []string{
string(utils.FlagAyamAfkir),
string(utils.FlagAyamCulling),
string(utils.FlagAyamMati),
}
func normalizeProductFlags(raw []string) ([]string, error) { func normalizeProductFlags(raw []string) ([]string, error) {
normalized, invalid := utils.NormalizeFlagsForGroup(raw, utils.FlagGroupProduct) normalized, invalid := utils.NormalizeFlagsForGroup(raw, utils.FlagGroupProduct)
if len(invalid) > 0 { if len(invalid) > 0 {
@@ -223,12 +229,32 @@ func (s productService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity
products, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB { products, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
db = s.withRelations(db) db = s.withRelations(db)
db = db.Where("is_visible = ?", true) // Depletion master products are system products and often stored with is_visible = false.
// When requested explicitly via is_depletion=true, include hidden records.
if params.IsDepletion == nil || !*params.IsDepletion {
db = db.Where("is_visible = ?", true)
}
if params.Search != "" { if params.Search != "" {
return db.Where("name ILIKE ?", "%"+params.Search+"%") db = db.Where("name ILIKE ?", "%"+params.Search+"%")
} }
if params.ProductCategoryID != 0 { if params.ProductCategoryID != 0 {
return db.Where("product_category_id = ?", params.ProductCategoryID) db = db.Where("product_category_id = ?", params.ProductCategoryID)
}
if params.IsDepletion != nil {
existsQuery := `
EXISTS (
SELECT 1
FROM flags f
WHERE f.flagable_type = ?
AND f.flagable_id = products.id
AND UPPER(f.name) IN ?
)
`
if *params.IsDepletion {
db = db.Where(existsQuery, entity.FlagableTypeProduct, depletionProductFlags)
} else {
db = db.Where("NOT "+existsQuery, entity.FlagableTypeProduct, depletionProductFlags)
}
} }
return db.Order("created_at DESC").Order("updated_at DESC") return db.Order("created_at DESC").Order("updated_at DESC")
}) })
@@ -44,4 +44,5 @@ type Query struct {
Limit int `query:"limit" validate:"omitempty,number,min=1"` Limit int `query:"limit" validate:"omitempty,number,min=1"`
Search string `query:"search" validate:"omitempty,max=50"` Search string `query:"search" validate:"omitempty,max=50"`
ProductCategoryID int `query:"product_category_id" validate:"omitempty,number,min=1"` ProductCategoryID int `query:"product_category_id" validate:"omitempty,number,min=1"`
IsDepletion *bool `query:"is_depletion" validate:"omitempty"`
} }
@@ -189,30 +189,31 @@ func (s *chickinService) CreateOne(c *fiber.Ctx, req *validation.Create) ([]enti
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Product warehouse %d belongs to different flock. Only product warehouses with project_flock_kandang_id = NULL or = %d can be used", chickinReq.ProductWarehouseId, req.ProjectFlockKandangId)) return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Product warehouse %d belongs to different flock. Only product warehouses with project_flock_kandang_id = NULL or = %d can be used", chickinReq.ProductWarehouseId, req.ProjectFlockKandangId))
} }
if productWarehouse.Product.Id != 0 { if productWarehouse.Product.Id != 0 {
category := strings.ToUpper(strings.TrimSpace(projectFlockKandang.ProjectFlock.Category))
if category != string(utils.ProjectFlockCategoryGrowing) && category != string(utils.ProjectFlockCategoryLaying) {
return nil, fmt.Errorf("invalid flock category for chickin")
}
var requiredFlag utils.FlagType hasAyamFlag := false
if projectFlockKandang.ProjectFlock.Category == string(utils.ProjectFlockCategoryGrowing) { for _, flag := range productWarehouse.Product.Flags {
requiredFlag = utils.FlagDOC if utils.CanonicalFlagType(flag.Name) == utils.FlagAyam {
} else if projectFlockKandang.ProjectFlock.Category == string(utils.ProjectFlockCategoryLaying) { hasAyamFlag = true
requiredFlag = utils.FlagPullet break
} else { }
return nil, fmt.Errorf("invalid flock category for chickin") }
}
hasRequiredFlag := false if !hasAyamFlag {
for _, flag := range productWarehouse.Product.Flags { return nil, fmt.Errorf(
if utils.FlagType(flag.Name) == requiredFlag { "product warehouse %d cannot be used for %s chickin. Product must have AYAM flag (or legacy alias DOC/PULLET/LAYER) (product ID: %d, warehouse ID: %d)",
hasRequiredFlag = true chickinReq.ProductWarehouseId,
break projectFlockKandang.ProjectFlock.Category,
productWarehouse.Product.Id,
productWarehouse.Id,
)
} }
} }
if !hasRequiredFlag {
return nil, fmt.Errorf("product warehouse %d cannot be used for %s chickin. Product must have %s flag (product ID: %d, warehouse ID: %d)", chickinReq.ProductWarehouseId, projectFlockKandang.ProjectFlock.Category, requiredFlag, productWarehouse.Product.Id, productWarehouse.Id)
}
}
chickinDate, err := utils.ParseDateString(chickinReq.ChickInDate) chickinDate, err := utils.ParseDateString(chickinReq.ChickInDate)
if err != nil { if err != nil {
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Invalid ChickInDate format for product warehouse %d", chickinReq.ProductWarehouseId)) return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Invalid ChickInDate format for product warehouse %d", chickinReq.ProductWarehouseId))