feat: filter improvement

This commit is contained in:
Adnan Zahir
2026-04-23 00:17:24 +07:00
parent a15fd1b174
commit e24e2ff123
13 changed files with 702 additions and 378 deletions
@@ -87,19 +87,21 @@ func (ctrl *PurchaseController) GetAll(c *fiber.Ctx) error {
func buildPurchaseQuery(c *fiber.Ctx) *validation.Query {
return &validation.Query{
Page: c.QueryInt("page", 1),
Limit: c.QueryInt("limit", 10),
Search: strings.TrimSpace(c.Query("search")),
ApprovalStatus: strings.TrimSpace(c.Query("approval_status")),
PoDate: strings.TrimSpace(c.Query("po_date")),
PoDateFrom: strings.TrimSpace(c.Query("po_date_from")),
PoDateTo: strings.TrimSpace(c.Query("po_date_to")),
CreatedFrom: strings.TrimSpace(c.Query("created_from")),
CreatedTo: strings.TrimSpace(c.Query("created_to")),
SupplierID: uint(c.QueryInt("supplier_id", 0)),
AreaID: uint(c.QueryInt("area_id", 0)),
LocationID: uint(c.QueryInt("location_id", 0)),
ProductCategoryID: strings.TrimSpace(c.Query("product_category_id")),
Page: c.QueryInt("page", 1),
Limit: c.QueryInt("limit", 10),
Search: strings.TrimSpace(c.Query("search")),
ApprovalStatus: strings.TrimSpace(c.Query("approval_status")),
PoDate: strings.TrimSpace(c.Query("po_date")),
PoDateFrom: strings.TrimSpace(c.Query("po_date_from")),
PoDateTo: strings.TrimSpace(c.Query("po_date_to")),
CreatedFrom: strings.TrimSpace(c.Query("created_from")),
CreatedTo: strings.TrimSpace(c.Query("created_to")),
SupplierID: uint(c.QueryInt("supplier_id", 0)),
AreaID: uint(c.QueryInt("area_id", 0)),
LocationID: uint(c.QueryInt("location_id", 0)),
ProjectFlockID: uint(c.QueryInt("project_flock_id", 0)),
ProjectFlockKandangID: uint(c.QueryInt("project_flock_kandang_id", 0)),
ProductCategoryID: strings.TrimSpace(c.Query("product_category_id")),
}
}
@@ -247,7 +247,7 @@ func (s *purchaseService) GetAll(c *fiber.Ctx, params *validation.Query) ([]enti
if len(productCategoryIDs) > 0 {
db = db.Where(
`EXISTS (
SELECT 1
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 IN ?
@@ -256,183 +256,9 @@ func (s *purchaseService) GetAll(c *fiber.Ctx, params *validation.Query) ([]enti
)
}
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 != "" {
like := "%" + search + "%"
db = db.Where(
`(
LOWER(COALESCE(purchases.pr_number, '')) LIKE ?
OR LOWER(COALESCE(purchases.po_number, '')) LIKE ?
OR EXISTS (
SELECT 1
FROM suppliers s
WHERE s.id = purchases.supplier_id
AND LOWER(COALESCE(s.name, '')) LIKE ?
)
OR EXISTS (
SELECT 1
FROM users u
WHERE u.id = purchases.created_by
AND LOWER(COALESCE(u.name, '')) LIKE ?
)
OR EXISTS (
SELECT 1
FROM purchase_items pi
JOIN products p ON p.id = pi.product_id
WHERE pi.purchase_id = purchases.id
AND LOWER(COALESCE(p.name, '')) LIKE ?
)
OR EXISTS (
SELECT 1
FROM purchase_items pi
JOIN warehouses w ON w.id = pi.warehouse_id
JOIN locations l ON l.id = w.location_id
WHERE pi.purchase_id = purchases.id
AND LOWER(COALESCE(l.name, '')) LIKE ?
)
OR EXISTS (
SELECT 1
FROM purchase_items pi
JOIN expense_nonstocks en ON en.id = pi.expense_nonstock_id
JOIN expenses e ON e.id = en.expense_id
WHERE pi.purchase_id = purchases.id
AND LOWER(COALESCE(e.reference_number, '')) LIKE ?
)
)`,
like,
like,
like,
like,
like,
like,
like,
)
}
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 != "" {
like := "%" + search + "%"
db = db.Where(
`(
LOWER(COALESCE(purchases.pr_number, '')) LIKE ?
OR LOWER(COALESCE(purchases.po_number, '')) LIKE ?
OR EXISTS (
SELECT 1
FROM suppliers s
WHERE s.id = purchases.supplier_id
AND LOWER(COALESCE(s.name, '')) LIKE ?
)
OR EXISTS (
SELECT 1
FROM users u
WHERE u.id = purchases.created_by
AND LOWER(COALESCE(u.name, '')) LIKE ?
)
OR EXISTS (
SELECT 1
FROM purchase_items pi
JOIN products p ON p.id = pi.product_id
WHERE pi.purchase_id = purchases.id
AND LOWER(COALESCE(p.name, '')) LIKE ?
)
OR EXISTS (
SELECT 1
FROM purchase_items pi
JOIN warehouses w ON w.id = pi.warehouse_id
JOIN locations l ON l.id = w.location_id
WHERE pi.purchase_id = purchases.id
AND LOWER(COALESCE(l.name, '')) LIKE ?
)
OR EXISTS (
SELECT 1
FROM purchase_items pi
JOIN expense_nonstocks en ON en.id = pi.expense_nonstock_id
JOIN expenses e ON e.id = en.expense_id
WHERE pi.purchase_id = purchases.id
AND LOWER(COALESCE(e.reference_number, '')) LIKE ?
)
)`,
like,
like,
like,
like,
like,
like,
like,
)
}
db = applyPurchaseProjectFlockFilter(db, params.ProjectFlockID, params.ProjectFlockKandangID)
db = applyPurchaseApprovalStatusFilter(db, approvalStatuses)
db = applyPurchaseSearchFilter(db, search)
return db.Order("created_at DESC").Order("purchases.id DESC")
})
@@ -2361,6 +2187,155 @@ func parsePoDateRangeForQuery(fromStr, toStr string) (*time.Time, *time.Time, er
return fromPtr, toPtr, nil
}
func applyPurchaseProjectFlockFilter(db *gorm.DB, projectFlockID, projectFlockKandangID uint) *gorm.DB {
if projectFlockID > 0 {
db = db.Where(
`EXISTS (
SELECT 1
FROM purchase_items pi
LEFT JOIN project_flock_kandangs pfk_explicit ON pfk_explicit.id = pi.project_flock_kandang_id
LEFT JOIN warehouses w ON w.id = pi.warehouse_id
LEFT JOIN project_flock_kandangs pfk_active ON pfk_active.kandang_id = w.kandang_id AND pfk_active.closed_at IS NULL
WHERE pi.purchase_id = purchases.id
AND COALESCE(pfk_explicit.project_flock_id, pfk_active.project_flock_id) = ?
)`,
projectFlockID,
)
}
if projectFlockKandangID > 0 {
db = db.Where(
`EXISTS (
SELECT 1
FROM purchase_items pi
LEFT JOIN warehouses w ON w.id = pi.warehouse_id
LEFT JOIN project_flock_kandangs pfk_active ON pfk_active.kandang_id = w.kandang_id AND pfk_active.closed_at IS NULL
WHERE pi.purchase_id = purchases.id
AND COALESCE(pi.project_flock_kandang_id, pfk_active.id) = ?
)`,
projectFlockKandangID,
)
}
return db
}
func applyPurchaseApprovalStatusFilter(db *gorm.DB, approvalStatuses []string) *gorm.DB {
if len(approvalStatuses) == 0 {
return db
}
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 {
return db
}
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,
)
return db.Where(approvalQuery, approvalArgs...)
}
func applyPurchaseSearchFilter(db *gorm.DB, search string) *gorm.DB {
if search == "" {
return db
}
like := "%" + search + "%"
return db.Where(
`(
LOWER(COALESCE(purchases.pr_number, '')) LIKE ?
OR LOWER(COALESCE(purchases.po_number, '')) LIKE ?
OR EXISTS (
SELECT 1
FROM suppliers s
WHERE s.id = purchases.supplier_id
AND LOWER(COALESCE(s.name, '')) LIKE ?
)
OR EXISTS (
SELECT 1
FROM users u
WHERE u.id = purchases.created_by
AND LOWER(COALESCE(u.name, '')) LIKE ?
)
OR EXISTS (
SELECT 1
FROM purchase_items pi
JOIN products p ON p.id = pi.product_id
WHERE pi.purchase_id = purchases.id
AND LOWER(COALESCE(p.name, '')) LIKE ?
)
OR EXISTS (
SELECT 1
FROM purchase_items pi
JOIN warehouses w ON w.id = pi.warehouse_id
JOIN locations l ON l.id = w.location_id
WHERE pi.purchase_id = purchases.id
AND LOWER(COALESCE(l.name, '')) LIKE ?
)
OR EXISTS (
SELECT 1
FROM purchase_items pi
LEFT JOIN project_flock_kandangs pfk_explicit ON pfk_explicit.id = pi.project_flock_kandang_id
LEFT JOIN warehouses w ON w.id = pi.warehouse_id
LEFT JOIN project_flock_kandangs pfk_active ON pfk_active.kandang_id = w.kandang_id AND pfk_active.closed_at IS NULL
LEFT JOIN project_flocks pf ON pf.id = COALESCE(pfk_explicit.project_flock_id, pfk_active.project_flock_id)
LEFT JOIN kandangs k ON k.id = COALESCE(pfk_explicit.kandang_id, pfk_active.kandang_id, w.kandang_id)
WHERE pi.purchase_id = purchases.id
AND (
LOWER(COALESCE(pf.flock_name, '')) LIKE ? OR
LOWER(COALESCE(k.name, '')) LIKE ?
)
)
OR EXISTS (
SELECT 1
FROM purchase_items pi
JOIN expense_nonstocks en ON en.id = pi.expense_nonstock_id
JOIN expenses e ON e.id = en.expense_id
WHERE pi.purchase_id = purchases.id
AND LOWER(COALESCE(e.reference_number, '')) LIKE ?
)
)`,
like,
like,
like,
like,
like,
like,
like,
like,
like,
)
}
func normalizeApprovalStatusFilter(raw string) string {
value := strings.ToLower(strings.TrimSpace(raw))
switch value {
@@ -61,17 +61,19 @@ type DeletePurchaseItemsRequest struct {
}
type Query struct {
Page int `query:"page" validate:"omitempty,number,min=1"`
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"`
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 string `query:"product_category_id" validate:"omitempty,max=500"`
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"`
Search string `query:"search" validate:"omitempty,max=100"`
CreatedFrom string `query:"created_from" validate:"omitempty,datetime=2006-01-02"`
CreatedTo string `query:"created_to" validate:"omitempty,datetime=2006-01-02"`
Page int `query:"page" validate:"omitempty,number,min=1"`
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"`
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"`
ProjectFlockID uint `query:"project_flock_id" validate:"omitempty,gt=0"`
ProjectFlockKandangID uint `query:"project_flock_kandang_id" validate:"omitempty,gt=0"`
ProductCategoryID string `query:"product_category_id" validate:"omitempty,max=500"`
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"`
Search string `query:"search" validate:"omitempty,max=100"`
CreatedFrom string `query:"created_from" validate:"omitempty,datetime=2006-01-02"`
CreatedTo string `query:"created_to" validate:"omitempty,datetime=2006-01-02"`
}