fix filter purchase ?approval_status=approved,rejected and ?product_category_id=1,2,3

This commit is contained in:
ragilap
2026-04-01 16:06:57 +07:00
parent 65409e5efa
commit 796417d56f
3 changed files with 96 additions and 35 deletions
@@ -40,7 +40,7 @@ func (ctrl *PurchaseController) GetAll(c *fiber.Ctx) error {
SupplierID: uint(c.QueryInt("supplier_id", 0)),
AreaID: uint(c.QueryInt("area_id", 0)),
LocationID: uint(c.QueryInt("location_id", 0)),
ProductCategoryID: uint(c.QueryInt("product_category_id", 0)),
ProductCategoryID: strings.TrimSpace(c.Query("product_category_id")),
}
if query.Page < 1 || query.Limit < 1 {
@@ -7,6 +7,7 @@ import (
"math"
"mime/multipart"
"sort"
"strconv"
"strings"
"time"
@@ -146,6 +147,11 @@ func (s *purchaseService) GetAll(c *fiber.Ctx, params *validation.Query) ([]enti
return nil, 0, utils.BadRequest(err.Error())
}
productCategoryIDs, err := parseUintCSVFilter(params.ProductCategoryID, "product_category_id")
if err != nil {
return nil, 0, utils.BadRequest(err.Error())
}
var poDateStart *time.Time
var poDateEnd *time.Time
@@ -165,7 +171,10 @@ func (s *purchaseService) GetAll(c *fiber.Ctx, params *validation.Query) ([]enti
}
search := strings.ToLower(strings.TrimSpace(params.Search))
approvalStatus := normalizeApprovalStatusFilter(params.ApprovalStatus)
approvalStatuses := parseStringCSVFilter(params.ApprovalStatus)
for i := range approvalStatuses {
approvalStatuses[i] = normalizeApprovalStatusFilter(approvalStatuses[i])
}
purchases, total, err := s.PurchaseRepo.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
db = s.withRelations(db)
@@ -230,46 +239,53 @@ func (s *purchaseService) GetAll(c *fiber.Ctx, params *validation.Query) ([]enti
)
}
if params.ProductCategoryID > 0 {
if len(productCategoryIDs) > 0 {
db = db.Where(
`EXISTS (
SELECT 1
FROM purchase_items pi
JOIN products p ON p.id = pi.product_id
WHERE pi.purchase_id = purchases.id AND p.product_category_id = ?
WHERE pi.purchase_id = purchases.id AND p.product_category_id IN ?
)`,
params.ProductCategoryID,
productCategoryIDs,
)
}
if approvalStatus != "" {
approvalLike := "%" + approvalStatus + "%"
db = db.Where(
`EXISTS (
SELECT 1
FROM approvals a
WHERE a.approvable_type = ?
AND a.approvable_id = purchases.id
AND a.id = (
SELECT a2.id
FROM approvals a2
WHERE a2.approvable_type = ?
AND a2.approvable_id = purchases.id
ORDER BY a2.action_at DESC, a2.id DESC
LIMIT 1
)
AND (
LOWER(COALESCE(a.step_name, '')) LIKE ?
OR LOWER(COALESCE(CAST(a.action AS TEXT), '')) LIKE ?
OR CAST(a.step_number AS TEXT) = ?
)
)`,
utils.ApprovalWorkflowPurchase.String(),
utils.ApprovalWorkflowPurchase.String(),
approvalLike,
approvalLike,
approvalStatus,
)
if len(approvalStatuses) > 0 {
approvalConditions := make([]string, 0, len(approvalStatuses))
approvalArgs := make([]any, 0, 2+(len(approvalStatuses)*3))
approvalArgs = append(approvalArgs, utils.ApprovalWorkflowPurchase.String(), utils.ApprovalWorkflowPurchase.String())
for _, status := range approvalStatuses {
if status == "" {
continue
}
like := "%" + status + "%"
approvalConditions = append(approvalConditions, `(LOWER(COALESCE(a.step_name, '')) LIKE ? OR LOWER(COALESCE(CAST(a.action AS TEXT), '')) LIKE ? OR CAST(a.step_number AS TEXT) = ?)`)
approvalArgs = append(approvalArgs, like, like, status)
}
if len(approvalConditions) > 0 {
approvalClause := strings.Join(approvalConditions, " OR ")
approvalQuery := fmt.Sprintf(
`EXISTS (
SELECT 1
FROM approvals a
WHERE a.approvable_type = ?
AND a.approvable_id = purchases.id
AND a.id = (
SELECT a2.id
FROM approvals a2
WHERE a2.approvable_type = ?
AND a2.approvable_id = purchases.id
ORDER BY a2.action_at DESC, a2.id DESC
LIMIT 1
)
AND (%s)
)`,
approvalClause,
)
db = db.Where(approvalQuery, approvalArgs...)
}
}
if search != "" {
@@ -2191,6 +2207,51 @@ func normalizeApprovalStatusFilter(raw string) string {
}
}
func parseUintCSVFilter(raw, fieldName string) ([]uint, error) {
parts := strings.Split(raw, ",")
result := make([]uint, 0, len(parts))
seen := make(map[uint]struct{}, len(parts))
for _, part := range parts {
value := strings.TrimSpace(part)
if value == "" {
continue
}
num, err := strconv.ParseUint(value, 10, 64)
if err != nil || num == 0 {
return nil, fmt.Errorf("%s contains invalid value: %s", fieldName, value)
}
u := uint(num)
if _, exists := seen[u]; exists {
continue
}
seen[u] = struct{}{}
result = append(result, u)
}
return result, nil
}
func parseStringCSVFilter(raw string) []string {
parts := strings.Split(raw, ",")
result := make([]string, 0, len(parts))
seen := make(map[string]struct{}, len(parts))
for _, part := range parts {
value := strings.ToLower(strings.TrimSpace(part))
if value == "" {
continue
}
if _, exists := seen[value]; exists {
continue
}
seen[value] = struct{}{}
result = append(result, value)
}
return result
}
func parseApprovalActionInput(raw string) (entity.ApprovalAction, error) {
value := strings.ToUpper(strings.TrimSpace(raw))
switch value {
@@ -66,9 +66,9 @@ type Query struct {
SupplierID uint `query:"supplier_id" validate:"omitempty,gt=0"`
AreaID uint `query:"area_id" validate:"omitempty,gt=0"`
LocationID uint `query:"location_id" validate:"omitempty,gt=0"`
ProductCategoryID uint `query:"product_category_id" validate:"omitempty,gt=0"`
ProductCategoryID string `query:"product_category_id" validate:"omitempty,max=500"`
Search string `query:"search" validate:"omitempty,max=100"`
ApprovalStatus string `query:"approval_status" validate:"omitempty,max=100"`
ApprovalStatus string `query:"approval_status" validate:"omitempty,max=500"`
PoDate string `query:"po_date" validate:"omitempty,datetime=2006-01-02"`
PoDateFrom string `query:"po_date_from" validate:"omitempty,datetime=2006-01-02"`
PoDateTo string `query:"po_date_to" validate:"omitempty,datetime=2006-01-02"`