From 36b0f97897f9d6f4fb676d4e585807b0aa0b45c7 Mon Sep 17 00:00:00 2001 From: giovanni Date: Thu, 2 Apr 2026 11:24:53 +0700 Subject: [PATCH 1/4] fix upser daily checklist status rejected; fix search list daily checklist --- ...ily_checklist_unique_for_rejected.down.sql | 5 ++ ...daily_checklist_unique_for_rejected.up.sql | 6 ++ .../services/daily-checklist.service.go | 73 +++++++++++++++---- 3 files changed, 70 insertions(+), 14 deletions(-) create mode 100644 internal/database/migrations/20260402034456_adjust_daily_checklist_unique_for_rejected.down.sql create mode 100644 internal/database/migrations/20260402034456_adjust_daily_checklist_unique_for_rejected.up.sql diff --git a/internal/database/migrations/20260402034456_adjust_daily_checklist_unique_for_rejected.down.sql b/internal/database/migrations/20260402034456_adjust_daily_checklist_unique_for_rejected.down.sql new file mode 100644 index 00000000..1994f220 --- /dev/null +++ b/internal/database/migrations/20260402034456_adjust_daily_checklist_unique_for_rejected.down.sql @@ -0,0 +1,5 @@ +DROP INDEX IF EXISTS idx_daily_checklists_unique_non_rejected; + +ALTER TABLE daily_checklists + ADD CONSTRAINT daily_checklists_date_kandang_category_key + UNIQUE (date, kandang_id, category); diff --git a/internal/database/migrations/20260402034456_adjust_daily_checklist_unique_for_rejected.up.sql b/internal/database/migrations/20260402034456_adjust_daily_checklist_unique_for_rejected.up.sql new file mode 100644 index 00000000..83ea4f41 --- /dev/null +++ b/internal/database/migrations/20260402034456_adjust_daily_checklist_unique_for_rejected.up.sql @@ -0,0 +1,6 @@ +ALTER TABLE daily_checklists + DROP CONSTRAINT IF EXISTS daily_checklists_date_kandang_category_key; + +CREATE UNIQUE INDEX IF NOT EXISTS idx_daily_checklists_unique_non_rejected + ON daily_checklists (date, kandang_id, category) + WHERE (status IS NULL OR status <> 'REJECTED'); diff --git a/internal/modules/daily-checklists/services/daily-checklist.service.go b/internal/modules/daily-checklists/services/daily-checklist.service.go index 6330e641..14937e8b 100644 --- a/internal/modules/daily-checklists/services/daily-checklist.service.go +++ b/internal/modules/daily-checklists/services/daily-checklist.service.go @@ -261,8 +261,11 @@ func (s dailyChecklistService) GetAll(c *fiber.Ctx, params *validation.Query) ([ if params.Search != "" { re := regexp.MustCompile("[^a-zA-Z0-9]") - like := re.ReplaceAll([]byte("%"+params.Search+"%"), []byte("")) - db = db.Where("(regexp_replace(k.name, '[^a-zA-Z0-9]', '', 'g') ILIKE ? OR regexp_replace(dc.category::text, '[^a-zA-Z0-9]', '', 'g') ILIKE ?)", string(like), string(like)) + normalizedSearch := re.ReplaceAllString(params.Search, "") + if normalizedSearch != "" { + like := "%" + normalizedSearch + "%" + db = db.Where("(regexp_replace(k.name, '[^a-zA-Z0-9]', '', 'g') ILIKE ? OR regexp_replace(dc.category::text, '[^a-zA-Z0-9]', '', 'g') ILIKE ?)", like, like) + } } countDB := db.Session(&gorm.Session{}) @@ -504,24 +507,66 @@ func (s *dailyChecklistService) CreateOne(c *fiber.Ctx, req *validation.Create) status := req.Status category := req.Category + targetID := uint(0) - createBody := &entity.DailyChecklist{ - KandangId: req.KandangId, - Date: date, - Category: category, - Status: &status, - } + err = s.Repository.DB().WithContext(c.Context()).Transaction(func(tx *gorm.DB) error { + existing := new(entity.DailyChecklist) + err := tx.Clauses(clause.Locking{Strength: "UPDATE"}). + Where("date = ? AND kandang_id = ? AND category = ? AND (status IS NULL OR status <> ?)", date, req.KandangId, category, "REJECTED"). + Take(existing).Error + if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return err + } - err = s.Repository.DB().WithContext(c.Context()).Clauses(clause.OnConflict{ - Columns: []clause.Column{{Name: "date"}, {Name: "kandang_id"}, {Name: "category"}}, - DoUpdates: clause.Assignments(map[string]any{"updated_at": time.Now()}), - }).Create(createBody).Error + if err == nil { + if err := tx.Model(&entity.DailyChecklist{}). + Where("id = ?", existing.Id). + Update("updated_at", time.Now()).Error; err != nil { + return err + } + + targetID = existing.Id + return nil + } + + createStatus := status + var rejectedCount int64 + if err := tx.Model(&entity.DailyChecklist{}). + Where("date = ? AND kandang_id = ? AND category = ? AND status = ?", date, req.KandangId, category, "REJECTED"). + Count(&rejectedCount).Error; err != nil { + return err + } + if rejectedCount > 0 { + createStatus = "DRAFT" + } + + createBody := &entity.DailyChecklist{ + KandangId: req.KandangId, + Date: date, + Category: category, + Status: &createStatus, + } + + if err := tx.Create(createBody).Error; err != nil { + // Handle concurrent insert for active checklist with same key. + if findErr := tx. + Where("date = ? AND kandang_id = ? AND category = ? AND (status IS NULL OR status <> ?)", date, req.KandangId, category, "REJECTED"). + Take(existing).Error; findErr == nil { + targetID = existing.Id + return nil + } + return err + } + + targetID = createBody.Id + return nil + }) if err != nil { - s.Log.Errorf("Failed to upsert dailyChecklist: %+v", err) + s.Log.Errorf("Failed to create/upsert dailyChecklist: %+v", err) return nil, err } - return s.GetOne(c, createBody.Id) + return s.GetOne(c, targetID) } func (s dailyChecklistService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint) (*entity.DailyChecklist, error) { From 88b6e2f294b01ffd2880207bb88d066f70c3451b Mon Sep 17 00:00:00 2001 From: giovanni Date: Thu, 2 Apr 2026 11:40:38 +0700 Subject: [PATCH 2/4] adjust sql migration --- ...034456_adjust_daily_checklist_unique_for_rejected.down.sql | 4 ++++ ...02034456_adjust_daily_checklist_unique_for_rejected.up.sql | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/internal/database/migrations/20260402034456_adjust_daily_checklist_unique_for_rejected.down.sql b/internal/database/migrations/20260402034456_adjust_daily_checklist_unique_for_rejected.down.sql index 1994f220..2ef9aecd 100644 --- a/internal/database/migrations/20260402034456_adjust_daily_checklist_unique_for_rejected.down.sql +++ b/internal/database/migrations/20260402034456_adjust_daily_checklist_unique_for_rejected.down.sql @@ -1,5 +1,9 @@ +BEGIN; + DROP INDEX IF EXISTS idx_daily_checklists_unique_non_rejected; ALTER TABLE daily_checklists ADD CONSTRAINT daily_checklists_date_kandang_category_key UNIQUE (date, kandang_id, category); + +COMMIT; diff --git a/internal/database/migrations/20260402034456_adjust_daily_checklist_unique_for_rejected.up.sql b/internal/database/migrations/20260402034456_adjust_daily_checklist_unique_for_rejected.up.sql index 83ea4f41..753deaef 100644 --- a/internal/database/migrations/20260402034456_adjust_daily_checklist_unique_for_rejected.up.sql +++ b/internal/database/migrations/20260402034456_adjust_daily_checklist_unique_for_rejected.up.sql @@ -1,6 +1,10 @@ +BEGIN; + ALTER TABLE daily_checklists DROP CONSTRAINT IF EXISTS daily_checklists_date_kandang_category_key; CREATE UNIQUE INDEX IF NOT EXISTS idx_daily_checklists_unique_non_rejected ON daily_checklists (date, kandang_id, category) WHERE (status IS NULL OR status <> 'REJECTED'); + +COMMIT; From 19d7cd33ca029a47f88bf0749446856779e3692f Mon Sep 17 00:00:00 2001 From: ValdiANS Date: Mon, 4 May 2026 16:27:50 +0700 Subject: [PATCH 3/4] fix: add search for Kandang Kosong --- .../daily-checklists/services/daily-checklist.service.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/modules/daily-checklists/services/daily-checklist.service.go b/internal/modules/daily-checklists/services/daily-checklist.service.go index 8e60ef8b..fdbe4c8b 100644 --- a/internal/modules/daily-checklists/services/daily-checklist.service.go +++ b/internal/modules/daily-checklists/services/daily-checklist.service.go @@ -277,7 +277,11 @@ func (s dailyChecklistService) GetAll(c *fiber.Ctx, params *validation.Query) ([ normalizedSearch := re.ReplaceAllString(params.Search, "") if normalizedSearch != "" { like := "%" + normalizedSearch + "%" - db = db.Where("(regexp_replace(k.name, '[^a-zA-Z0-9]', '', 'g') ILIKE ? OR regexp_replace(dc.category::text, '[^a-zA-Z0-9]', '', 'g') ILIKE ?)", like, like) + db = db.Where(`( + regexp_replace(k.name, '[^a-zA-Z0-9]', '', 'g') ILIKE ? OR + regexp_replace(dc.category::text, '[^a-zA-Z0-9]', '', 'g') ILIKE ? OR + (dc.category = 'empty_kandang' AND regexp_replace('Kandang Kosong', '[^a-zA-Z0-9]', '', 'g') ILIKE ?) + )`, like, like, like) } } From 48351661c590d97f85cd6f5729d7b47853b49cb5 Mon Sep 17 00:00:00 2001 From: ValdiANS Date: Mon, 4 May 2026 16:28:03 +0700 Subject: [PATCH 4/4] fix: add order_by and sort_by query to master data employee --- .../controllers/employees.controller.go | 8 +++++--- .../employees/services/employees.service.go | 16 ++++++++++++---- .../validations/employees.validation.go | 2 ++ 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/internal/modules/master/employees/controllers/employees.controller.go b/internal/modules/master/employees/controllers/employees.controller.go index 3d0901c8..9b633803 100644 --- a/internal/modules/master/employees/controllers/employees.controller.go +++ b/internal/modules/master/employees/controllers/employees.controller.go @@ -24,9 +24,11 @@ func NewEmployeesController(employeesService service.EmployeesService) *Employee func (u *EmployeesController) GetAll(c *fiber.Ctx) error { query := &validation.Query{ - Page: c.QueryInt("page", 1), - Limit: c.QueryInt("limit", 10), - Search: c.Query("search", ""), + Page: c.QueryInt("page", 1), + Limit: c.QueryInt("limit", 10), + Search: c.Query("search", ""), + OrderBy: c.Query("order_by", "desc"), + SortBy: c.Query("sort_by", "updated_at"), } if query.Page < 1 || query.Limit < 1 { diff --git a/internal/modules/master/employees/services/employees.service.go b/internal/modules/master/employees/services/employees.service.go index a7a6cef3..a1f02c96 100644 --- a/internal/modules/master/employees/services/employees.service.go +++ b/internal/modules/master/employees/services/employees.service.go @@ -2,6 +2,7 @@ package service import ( "errors" + "fmt" "strings" entity "gitlab.com/mbugroup/lti-api.git/internal/entities" @@ -126,11 +127,18 @@ func (s employeesService) GetAll(c *fiber.Ctx, params *validation.Query) ([]enti if params.IsActive != nil { db = db.Where("employees.is_active = ?", *params.IsActive) } - return db. + + db = db. Select("employees.id, employees.name, employees.is_active, employees.created_at, employees.updated_at"). - Group("employees.id, employees.name, employees.is_active, employees.created_at, employees.updated_at"). - Order("employees.created_at DESC"). - Order("employees.updated_at DESC") + Group("employees.id, employees.name, employees.is_active, employees.created_at, employees.updated_at") + + if params.OrderBy == "desc" || params.OrderBy == "" { + db = db.Order(fmt.Sprintf("employees.%s DESC", params.SortBy)) + } else { + db = db.Order(fmt.Sprintf("employees.%s ASC", params.SortBy)) + } + + return db }) if err != nil { diff --git a/internal/modules/master/employees/validations/employees.validation.go b/internal/modules/master/employees/validations/employees.validation.go index 83608071..09aa471e 100644 --- a/internal/modules/master/employees/validations/employees.validation.go +++ b/internal/modules/master/employees/validations/employees.validation.go @@ -18,4 +18,6 @@ type Query struct { Search string `query:"search" validate:"omitempty,max=50"` KandangId *uint `query:"kandang_id" validate:"omitempty"` IsActive *bool `query:"is_active" validate:"omitempty"` + SortBy string `query:"sort_by" validate:"omitempty,max=50,oneof=name created_at updated_at" default:"updated_at"` + OrderBy string `query:"order_by" validate:"omitempty,oneof=asc desc" default:"desc"` }