From d41f1b9495165702fc5db454ed09b143a2e9113b Mon Sep 17 00:00:00 2001 From: "Hafizh A. Y" Date: Thu, 5 Feb 2026 10:26:44 +0700 Subject: [PATCH] fix(BE): multiple filter, all search --- .../controllers/transaction.controller.go | 83 +++++++++++++------ .../services/transaction.service.go | 53 ++++++++---- .../validations/transaction.validation.go | 24 +++--- 3 files changed, 110 insertions(+), 50 deletions(-) diff --git a/internal/modules/finance/transactions/controllers/transaction.controller.go b/internal/modules/finance/transactions/controllers/transaction.controller.go index 5c25cbcd..228feeaa 100644 --- a/internal/modules/finance/transactions/controllers/transaction.controller.go +++ b/internal/modules/finance/transactions/controllers/transaction.controller.go @@ -24,46 +24,81 @@ func NewTransactionController(transactionService service.TransactionService) *Tr } func (u *TransactionController) GetAll(c *fiber.Ctx) error { - parseOptionalUint := func(key string) (*uint, error) { + parseUintListParam := func(key string) ([]uint, error) { raw := strings.TrimSpace(c.Query(key, "")) if raw == "" { return nil, nil } - parsed, err := strconv.ParseUint(raw, 10, 64) - if err != nil { - return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid "+key) + parts := strings.Split(raw, ",") + ids := make([]uint, 0, len(parts)) + for _, part := range parts { + trimmed := strings.TrimSpace(part) + if trimmed == "" { + return nil, strconv.ErrSyntax + } + parsed, err := strconv.ParseUint(trimmed, 10, 64) + if err != nil { + return nil, err + } + if parsed == 0 { + continue + } + ids = append(ids, uint(parsed)) } - if parsed == 0 { + if len(ids) == 0 { return nil, nil } - value := uint(parsed) - return &value, nil + return ids, nil } - bankId, err := parseOptionalUint("bank_id") - if err != nil { - return err + parseStringListParam := func(key string) ([]string, error) { + raw := strings.TrimSpace(c.Query(key, "")) + if raw == "" { + return nil, nil + } + parts := strings.Split(raw, ",") + values := make([]string, 0, len(parts)) + for _, part := range parts { + trimmed := strings.TrimSpace(part) + if trimmed == "" { + return nil, strconv.ErrSyntax + } + values = append(values, trimmed) + } + if len(values) == 0 { + return nil, nil + } + return values, nil } - customerId, err := parseOptionalUint("customer_id") + + bankIDs, err := parseUintListParam("bank_ids") if err != nil { - return err + return fiber.NewError(fiber.StatusBadRequest, "Invalid bank_ids") } - supplierId, err := parseOptionalUint("supplier_id") + customerIDs, err := parseUintListParam("customer_ids") if err != nil { - return err + return fiber.NewError(fiber.StatusBadRequest, "Invalid customer_ids") + } + supplierIDs, err := parseUintListParam("supplier_ids") + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, "Invalid supplier_ids") + } + transactionTypes, err := parseStringListParam("transaction_types") + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, "Invalid transaction_types") } query := &validation.Query{ - Page: c.QueryInt("page", 1), - Limit: c.QueryInt("limit", 10), - Search: c.Query("search", ""), - TransactionType: c.Query("transaction_type", ""), - BankId: bankId, - CustomerId: customerId, - SupplierId: supplierId, - SortDate: c.Query("sort_date", ""), - StartDate: c.Query("start_date", ""), - EndDate: c.Query("end_date", ""), + Page: c.QueryInt("page", 1), + Limit: c.QueryInt("limit", 10), + Search: c.Query("search", ""), + TransactionTypes: transactionTypes, + BankIDs: bankIDs, + CustomerIDs: customerIDs, + SupplierIDs: supplierIDs, + SortDate: c.Query("sort_date", ""), + StartDate: c.Query("start_date", ""), + EndDate: c.Query("end_date", ""), } if query.Page < 1 || query.Limit < 1 { diff --git a/internal/modules/finance/transactions/services/transaction.service.go b/internal/modules/finance/transactions/services/transaction.service.go index f422320f..4526b817 100644 --- a/internal/modules/finance/transactions/services/transaction.service.go +++ b/internal/modules/finance/transactions/services/transaction.service.go @@ -74,33 +74,58 @@ func (s transactionService) GetAll(c *fiber.Ctx, params *validation.Query) ([]en if params.Search != "" { like := "%" + strings.ToLower(strings.TrimSpace(params.Search)) + "%" + db = db.Joins( + "LEFT JOIN customers ON customers.id = payments.party_id AND payments.party_type = ? AND customers.deleted_at IS NULL", + string(utils.PaymentPartyCustomer), + ).Joins( + "LEFT JOIN suppliers ON suppliers.id = payments.party_id AND payments.party_type = ? AND suppliers.deleted_at IS NULL", + string(utils.PaymentPartySupplier), + ).Joins( + "LEFT JOIN banks ON banks.id = payments.bank_id AND banks.deleted_at IS NULL", + ) db = db.Where( `LOWER(payment_code) LIKE ? OR LOWER(COALESCE(reference_number, '')) LIKE ? OR LOWER(COALESCE(transaction_type, '')) LIKE ? OR - LOWER(COALESCE(notes, '')) LIKE ?`, - like, like, like, like, + LOWER(COALESCE(notes, '')) LIKE ? OR + LOWER(COALESCE(customers.name, '')) LIKE ? OR + LOWER(COALESCE(suppliers.name, '')) LIKE ? OR + LOWER(COALESCE(banks.name, '')) LIKE ?`, + like, like, like, like, like, like, like, ) } - if strings.TrimSpace(params.TransactionType) != "" { - db = db.Where("transaction_type = ?", strings.ToUpper(strings.TrimSpace(params.TransactionType))) + if len(params.TransactionTypes) > 0 { + types := make([]string, 0, len(params.TransactionTypes)) + for _, transactionType := range params.TransactionTypes { + normalized := strings.ToUpper(strings.TrimSpace(transactionType)) + if normalized == "" { + continue + } + types = append(types, normalized) + } + if len(types) > 0 { + db = db.Where("transaction_type IN ?", types) + } } - if params.BankId != nil { - db = db.Where("bank_id = ?", *params.BankId) + if len(params.BankIDs) > 0 { + db = db.Where("bank_id IN ?", params.BankIDs) } - if params.CustomerId != nil && params.SupplierId != nil { + customerIDs := params.CustomerIDs + supplierIDs := params.SupplierIDs + + if len(customerIDs) > 0 && len(supplierIDs) > 0 { db = db.Where( - "(party_type = ? AND party_id = ?) OR (party_type = ? AND party_id = ?)", - string(utils.PaymentPartyCustomer), *params.CustomerId, - string(utils.PaymentPartySupplier), *params.SupplierId, + "(party_type = ? AND party_id IN ?) OR (party_type = ? AND party_id IN ?)", + string(utils.PaymentPartyCustomer), customerIDs, + string(utils.PaymentPartySupplier), supplierIDs, ) - } else if params.CustomerId != nil { - db = db.Where("party_type = ? AND party_id = ?", string(utils.PaymentPartyCustomer), *params.CustomerId) - } else if params.SupplierId != nil { - db = db.Where("party_type = ? AND party_id = ?", string(utils.PaymentPartySupplier), *params.SupplierId) + } else if len(customerIDs) > 0 { + db = db.Where("party_type = ? AND party_id IN ?", string(utils.PaymentPartyCustomer), customerIDs) + } else if len(supplierIDs) > 0 { + db = db.Where("party_type = ? AND party_id IN ?", string(utils.PaymentPartySupplier), supplierIDs) } if startDate != nil { diff --git a/internal/modules/finance/transactions/validations/transaction.validation.go b/internal/modules/finance/transactions/validations/transaction.validation.go index f367dda1..7a71cb51 100644 --- a/internal/modules/finance/transactions/validations/transaction.validation.go +++ b/internal/modules/finance/transactions/validations/transaction.validation.go @@ -1,22 +1,22 @@ package validation type Create struct { - Name string `json:"name" validate:"required_strict,min=3"` + Name string `json:"name" validate:"required_strict,min=3"` } type Update struct { - Name *string `json:"name,omitempty" validate:"omitempty"` + Name *string `json:"name,omitempty" validate:"omitempty"` } type Query struct { - Page int `query:"page" validate:"omitempty,number,min=1,gt=0"` - Limit int `query:"limit" validate:"omitempty,number,min=1,max=100,gt=0"` - Search string `query:"search" validate:"omitempty,max=50"` - TransactionType string `query:"transaction_type" validate:"omitempty,max=50"` - BankId *uint `query:"bank_id" validate:"omitempty,number,gt=0"` - CustomerId *uint `query:"customer_id" validate:"omitempty,number,gt=0"` - SupplierId *uint `query:"supplier_id" validate:"omitempty,number,gt=0"` - SortDate string `query:"sort_date" validate:"omitempty,oneof=created_at payment_date"` - StartDate string `query:"start_date" validate:"omitempty,datetime=2006-01-02"` - EndDate string `query:"end_date" validate:"omitempty,datetime=2006-01-02"` + Page int `query:"page" validate:"omitempty,number,min=1,gt=0"` + Limit int `query:"limit" validate:"omitempty,number,min=1,max=100,gt=0"` + Search string `query:"search" validate:"omitempty,max=50"` + TransactionTypes []string `query:"transaction_types" validate:"omitempty,dive,max=50"` + BankIDs []uint `query:"bank_ids" validate:"omitempty,dive,gt=0"` + CustomerIDs []uint `query:"customer_ids" validate:"omitempty,dive,gt=0"` + SupplierIDs []uint `query:"supplier_ids" validate:"omitempty,dive,gt=0"` + SortDate string `query:"sort_date" validate:"omitempty,oneof=created_at payment_date"` + StartDate string `query:"start_date" validate:"omitempty,datetime=2006-01-02"` + EndDate string `query:"end_date" validate:"omitempty,datetime=2006-01-02"` }