From 480e430289b4f2a0c192dc2058a4a479b170d045 Mon Sep 17 00:00:00 2001 From: giovanni Date: Thu, 2 Apr 2026 14:53:59 +0700 Subject: [PATCH] fix upser daily checklist status rejected; fix search list daily checklist --- ...ily_checklist_unique_for_rejected.down.sql | 9 +++ ...daily_checklist_unique_for_rejected.up.sql | 10 +++ .../services/daily-checklist.service.go | 73 +++++++++++++++---- 3 files changed, 78 insertions(+), 14 deletions(-) create mode 100644 internal/database/migrations/20260402075043_adjust_daily_checklist_unique_for_rejected.down.sql create mode 100644 internal/database/migrations/20260402075043_adjust_daily_checklist_unique_for_rejected.up.sql diff --git a/internal/database/migrations/20260402075043_adjust_daily_checklist_unique_for_rejected.down.sql b/internal/database/migrations/20260402075043_adjust_daily_checklist_unique_for_rejected.down.sql new file mode 100644 index 00000000..2ef9aecd --- /dev/null +++ b/internal/database/migrations/20260402075043_adjust_daily_checklist_unique_for_rejected.down.sql @@ -0,0 +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/20260402075043_adjust_daily_checklist_unique_for_rejected.up.sql b/internal/database/migrations/20260402075043_adjust_daily_checklist_unique_for_rejected.up.sql new file mode 100644 index 00000000..753deaef --- /dev/null +++ b/internal/database/migrations/20260402075043_adjust_daily_checklist_unique_for_rejected.up.sql @@ -0,0 +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; 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) {