Merge branch 'fix/chickin-master-product' into 'dev/fifo-v2'

fix: master product and chickin

See merge request mbugroup/lti-api!358
This commit is contained in:
Hafizh A. Y.
2026-03-06 03:26:18 +00:00
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),
}
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 {
return fiber.NewError(fiber.StatusBadRequest, "page and limit must be greater than 0")
}
@@ -31,6 +31,12 @@ type productService struct {
Repository repository.ProductRepository
}
var depletionProductFlags = []string{
string(utils.FlagAyamAfkir),
string(utils.FlagAyamCulling),
string(utils.FlagAyamMati),
}
func normalizeProductFlags(raw []string) ([]string, error) {
normalized, invalid := utils.NormalizeFlagsForGroup(raw, utils.FlagGroupProduct)
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 {
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 != "" {
return db.Where("name ILIKE ?", "%"+params.Search+"%")
db = db.Where("name ILIKE ?", "%"+params.Search+"%")
}
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")
})
@@ -44,4 +44,5 @@ type Query struct {
Limit int `query:"limit" validate:"omitempty,number,min=1"`
Search string `query:"search" validate:"omitempty,max=50"`
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))
}
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
if projectFlockKandang.ProjectFlock.Category == string(utils.ProjectFlockCategoryGrowing) {
requiredFlag = utils.FlagDOC
} else if projectFlockKandang.ProjectFlock.Category == string(utils.ProjectFlockCategoryLaying) {
requiredFlag = utils.FlagPullet
} else {
return nil, fmt.Errorf("invalid flock category for chickin")
}
hasAyamFlag := false
for _, flag := range productWarehouse.Product.Flags {
if utils.CanonicalFlagType(flag.Name) == utils.FlagAyam {
hasAyamFlag = true
break
}
}
hasRequiredFlag := false
for _, flag := range productWarehouse.Product.Flags {
if utils.FlagType(flag.Name) == requiredFlag {
hasRequiredFlag = true
break
if !hasAyamFlag {
return nil, fmt.Errorf(
"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)",
chickinReq.ProductWarehouseId,
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)
if err != nil {
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Invalid ChickInDate format for product warehouse %d", chickinReq.ProductWarehouseId))