mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
adjust softdelete daily checklist; add empty kandang
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
DROP INDEX IF EXISTS idx_daily_checklists_unique_non_rejected;
|
||||||
|
DROP INDEX IF EXISTS idx_daily_checklists_deleted_at;
|
||||||
|
DROP INDEX IF EXISTS idx_daily_checklists_deleted_by;
|
||||||
|
|
||||||
|
ALTER TABLE daily_checklists
|
||||||
|
DROP CONSTRAINT IF EXISTS fk_daily_checklists_deleted_by;
|
||||||
|
|
||||||
|
ALTER TABLE daily_checklists
|
||||||
|
DROP COLUMN IF EXISTS deleted_at,
|
||||||
|
DROP COLUMN IF EXISTS deleted_by;
|
||||||
|
|
||||||
|
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;
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
ALTER TABLE daily_checklists
|
||||||
|
ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ,
|
||||||
|
ADD COLUMN IF NOT EXISTS deleted_by BIGINT;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_daily_checklists_deleted_at
|
||||||
|
ON daily_checklists (deleted_at);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_daily_checklists_deleted_by
|
||||||
|
ON daily_checklists (deleted_by);
|
||||||
|
|
||||||
|
ALTER TABLE daily_checklists
|
||||||
|
DROP CONSTRAINT IF EXISTS fk_daily_checklists_deleted_by,
|
||||||
|
ADD CONSTRAINT fk_daily_checklists_deleted_by
|
||||||
|
FOREIGN KEY (deleted_by) REFERENCES users(id) ON DELETE SET NULL;
|
||||||
|
|
||||||
|
ALTER TABLE daily_checklists
|
||||||
|
DROP CONSTRAINT IF EXISTS daily_checklists_date_kandang_category_key;
|
||||||
|
|
||||||
|
DROP INDEX IF EXISTS idx_daily_checklists_unique_non_rejected;
|
||||||
|
|
||||||
|
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')
|
||||||
|
AND deleted_at IS NULL;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM daily_checklists
|
||||||
|
WHERE category::text = 'empty_kandang'
|
||||||
|
) THEN
|
||||||
|
RAISE EXCEPTION 'Cannot rollback category_code enum: daily_checklists still contains empty_kandang';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM phases
|
||||||
|
WHERE category::text = 'empty_kandang'
|
||||||
|
) THEN
|
||||||
|
RAISE EXCEPTION 'Cannot rollback category_code enum: phases still contains empty_kandang';
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
ALTER TYPE category_code RENAME TO category_code_old;
|
||||||
|
|
||||||
|
CREATE TYPE category_code AS ENUM (
|
||||||
|
'pullet_open',
|
||||||
|
'pullet_close',
|
||||||
|
'produksi_open',
|
||||||
|
'produksi_close'
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE phases
|
||||||
|
ALTER COLUMN category TYPE category_code
|
||||||
|
USING category::text::category_code;
|
||||||
|
|
||||||
|
ALTER TABLE daily_checklists
|
||||||
|
ALTER COLUMN category TYPE category_code
|
||||||
|
USING category::text::category_code;
|
||||||
|
|
||||||
|
DROP TYPE category_code_old;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_type t
|
||||||
|
JOIN pg_enum e ON t.oid = e.enumtypid
|
||||||
|
WHERE t.typname = 'category_code'
|
||||||
|
AND e.enumlabel = 'empty_kandang'
|
||||||
|
) THEN
|
||||||
|
ALTER TYPE category_code ADD VALUE 'empty_kandang';
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
package entities
|
package entities
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
type DailyChecklist struct {
|
type DailyChecklist struct {
|
||||||
Id uint `gorm:"primaryKey"`
|
Id uint `gorm:"primaryKey"`
|
||||||
@@ -14,12 +18,15 @@ type DailyChecklist struct {
|
|||||||
DocumentPath *string
|
DocumentPath *string
|
||||||
RejectReason *string
|
RejectReason *string
|
||||||
CreatedBy *uint
|
CreatedBy *uint
|
||||||
CreatedAt time.Time `gorm:"autoCreateTime"`
|
DeletedBy *uint
|
||||||
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
CreatedAt time.Time `gorm:"autoCreateTime"`
|
||||||
|
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
||||||
|
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||||
|
|
||||||
Kandang KandangGroup `gorm:"foreignKey:KandangId;references:Id"`
|
Kandang KandangGroup `gorm:"foreignKey:KandangId;references:Id"`
|
||||||
Checklist *Checklist `gorm:"foreignKey:ChecklistId;references:Id"`
|
Checklist *Checklist `gorm:"foreignKey:ChecklistId;references:Id"`
|
||||||
Creator *User `gorm:"foreignKey:CreatedBy;references:Id"`
|
Creator *User `gorm:"foreignKey:CreatedBy;references:Id"`
|
||||||
|
Deleter *User `gorm:"foreignKey:DeletedBy;references:Id"`
|
||||||
Tasks []DailyChecklistTask `gorm:"foreignKey:DailyChecklistId;references:Id"`
|
Tasks []DailyChecklistTask `gorm:"foreignKey:DailyChecklistId;references:Id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ func (r *DailyChecklistRepositoryImpl) ListScopedChecklistIDs(c *fiber.Ctx, ids
|
|||||||
Joins("JOIN kandang_groups k ON k.id = dc.kandang_id").
|
Joins("JOIN kandang_groups k ON k.id = dc.kandang_id").
|
||||||
Joins("JOIN locations loc ON loc.id = k.location_id").
|
Joins("JOIN locations loc ON loc.id = k.location_id").
|
||||||
Joins("JOIN areas a ON a.id = loc.area_id").
|
Joins("JOIN areas a ON a.id = loc.area_id").
|
||||||
Where("dc.id IN ?", ids)
|
Where("dc.id IN ?", ids).
|
||||||
|
Where("dc.deleted_at IS NULL")
|
||||||
|
|
||||||
db, err := m.ApplyLocationAreaScope(c, db, "loc.id", "a.id")
|
db, err := m.ApplyLocationAreaScope(c, db, "loc.id", "a.id")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -122,6 +122,13 @@ type DailyChecklistReportCategory struct {
|
|||||||
Baik int
|
Baik int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
dailyChecklistDateLayout = "2006-01-02"
|
||||||
|
dailyChecklistCategoryEmptyKandang = "empty_kandang"
|
||||||
|
dailyChecklistStatusRejected = "REJECTED"
|
||||||
|
dailyChecklistStatusDraft = "DRAFT"
|
||||||
|
)
|
||||||
|
|
||||||
func NewDailyChecklistService(repo repository.DailyChecklistRepository, phaseRepo phaseRepo.PhasesRepository, validate *validator.Validate, documentSvc commonSvc.DocumentService) DailyChecklistService {
|
func NewDailyChecklistService(repo repository.DailyChecklistRepository, phaseRepo phaseRepo.PhasesRepository, validate *validator.Validate, documentSvc commonSvc.DocumentService) DailyChecklistService {
|
||||||
return &dailyChecklistService{
|
return &dailyChecklistService{
|
||||||
Log: utils.Log,
|
Log: utils.Log,
|
||||||
@@ -146,7 +153,8 @@ func (s dailyChecklistService) ensureChecklistAccess(c *fiber.Ctx, checklistID u
|
|||||||
Joins("JOIN kandang_groups k ON k.id = dc.kandang_id").
|
Joins("JOIN kandang_groups k ON k.id = dc.kandang_id").
|
||||||
Joins("JOIN locations loc ON loc.id = k.location_id").
|
Joins("JOIN locations loc ON loc.id = k.location_id").
|
||||||
Joins("JOIN areas a ON a.id = loc.area_id").
|
Joins("JOIN areas a ON a.id = loc.area_id").
|
||||||
Where("dc.id = ?", checklistID)
|
Where("dc.id = ?", checklistID).
|
||||||
|
Where("dc.deleted_at IS NULL")
|
||||||
|
|
||||||
scopedDB, err := m.ApplyLocationAreaScope(c, db, "loc.id", "a.id")
|
scopedDB, err := m.ApplyLocationAreaScope(c, db, "loc.id", "a.id")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -196,7 +204,7 @@ func (s dailyChecklistService) ensureTaskAccess(c *fiber.Ctx, taskID uint) error
|
|||||||
|
|
||||||
db := s.Repository.DB().WithContext(c.Context()).
|
db := s.Repository.DB().WithContext(c.Context()).
|
||||||
Table("daily_checklist_activity_tasks t").
|
Table("daily_checklist_activity_tasks t").
|
||||||
Joins("JOIN daily_checklists dc ON dc.id = t.checklist_id").
|
Joins("JOIN daily_checklists dc ON dc.id = t.checklist_id AND dc.deleted_at IS NULL").
|
||||||
Joins("JOIN kandang_groups k ON k.id = dc.kandang_id").
|
Joins("JOIN kandang_groups k ON k.id = dc.kandang_id").
|
||||||
Joins("JOIN locations loc ON loc.id = k.location_id").
|
Joins("JOIN locations loc ON loc.id = k.location_id").
|
||||||
Joins("JOIN areas a ON a.id = loc.area_id").
|
Joins("JOIN areas a ON a.id = loc.area_id").
|
||||||
@@ -228,7 +236,8 @@ func (s dailyChecklistService) GetAll(c *fiber.Ctx, params *validation.Query) ([
|
|||||||
Table("daily_checklists dc").
|
Table("daily_checklists dc").
|
||||||
Joins("JOIN kandang_groups k ON k.id = dc.kandang_id").
|
Joins("JOIN kandang_groups k ON k.id = dc.kandang_id").
|
||||||
Joins("JOIN locations loc ON loc.id = k.location_id").
|
Joins("JOIN locations loc ON loc.id = k.location_id").
|
||||||
Joins("JOIN areas a ON a.id = loc.area_id")
|
Joins("JOIN areas a ON a.id = loc.area_id").
|
||||||
|
Where("dc.deleted_at IS NULL")
|
||||||
|
|
||||||
var scopeErr error
|
var scopeErr error
|
||||||
db, scopeErr = m.ApplyLocationAreaScope(c, db, "loc.id", "a.id")
|
db, scopeErr = m.ApplyLocationAreaScope(c, db, "loc.id", "a.id")
|
||||||
@@ -501,66 +510,39 @@ func (s *dailyChecklistService) CreateOne(c *fiber.Ctx, req *validation.Create)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
date, err := time.Parse("2006-01-02", req.Date)
|
date, err := time.Parse(dailyChecklistDateLayout, strings.TrimSpace(req.Date))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fiber.NewError(fiber.StatusBadRequest, "invalid date format, use YYYY-MM-DD")
|
return nil, fiber.NewError(fiber.StatusBadRequest, "invalid date format, use YYYY-MM-DD")
|
||||||
}
|
}
|
||||||
|
|
||||||
status := req.Status
|
status := req.Status
|
||||||
category := req.Category
|
category := req.Category
|
||||||
|
endDate := date
|
||||||
|
|
||||||
|
if req.EmptyKandang {
|
||||||
|
if strings.TrimSpace(req.EmptyKandangEndDate) == "" {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, "empty_kandang_end_date is required when empty_kandang is true")
|
||||||
|
}
|
||||||
|
|
||||||
|
endDate, err = time.Parse(dailyChecklistDateLayout, strings.TrimSpace(req.EmptyKandangEndDate))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, "invalid empty_kandang_end_date format, use YYYY-MM-DD")
|
||||||
|
}
|
||||||
|
if endDate.Before(date) {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, "empty_kandang_end_date must be greater than or equal to date")
|
||||||
|
}
|
||||||
|
|
||||||
|
category = dailyChecklistCategoryEmptyKandang
|
||||||
|
}
|
||||||
|
|
||||||
targetID := uint(0)
|
targetID := uint(0)
|
||||||
|
|
||||||
err = s.Repository.DB().WithContext(c.Context()).Transaction(func(tx *gorm.DB) error {
|
err = s.Repository.DB().WithContext(c.Context()).Transaction(func(tx *gorm.DB) error {
|
||||||
existing := new(entity.DailyChecklist)
|
if req.EmptyKandang {
|
||||||
err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
|
return s.createBulkDailyChecklists(tx, req.KandangId, date, endDate, category, status, &targetID)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil {
|
return s.createOrReuseSingleDailyChecklist(tx, req.KandangId, date, category, status, &targetID)
|
||||||
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 {
|
if err != nil {
|
||||||
s.Log.Errorf("Failed to create/upsert dailyChecklist: %+v", err)
|
s.Log.Errorf("Failed to create/upsert dailyChecklist: %+v", err)
|
||||||
@@ -570,6 +552,109 @@ func (s *dailyChecklistService) CreateOne(c *fiber.Ctx, req *validation.Create)
|
|||||||
return s.GetOne(c, targetID)
|
return s.GetOne(c, targetID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *dailyChecklistService) createOrReuseSingleDailyChecklist(tx *gorm.DB, kandangID uint, date time.Time, category, status string, targetID *uint) 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 <> ?) AND deleted_at IS NULL", date, kandangID, category, dailyChecklistStatusRejected).
|
||||||
|
Take(existing).Error
|
||||||
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = ? AND deleted_at IS NULL", date, kandangID, category, dailyChecklistStatusRejected).
|
||||||
|
Count(&rejectedCount).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if rejectedCount > 0 {
|
||||||
|
createStatus = dailyChecklistStatusDraft
|
||||||
|
}
|
||||||
|
|
||||||
|
createBody := &entity.DailyChecklist{
|
||||||
|
KandangId: 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 <> ?) AND deleted_at IS NULL", date, kandangID, category, dailyChecklistStatusRejected).
|
||||||
|
Take(existing).Error; findErr == nil {
|
||||||
|
*targetID = existing.Id
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*targetID = createBody.Id
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dailyChecklistService) createBulkDailyChecklists(tx *gorm.DB, kandangID uint, startDate, endDate time.Time, category, status string, targetID *uint) error {
|
||||||
|
var conflictCount int64
|
||||||
|
if err := tx.Model(&entity.DailyChecklist{}).
|
||||||
|
Where("kandang_id = ? AND category = ? AND date BETWEEN ? AND ? AND (status IS NULL OR status <> ?) AND deleted_at IS NULL", kandangID, category, startDate, endDate, dailyChecklistStatusRejected).
|
||||||
|
Count(&conflictCount).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if conflictCount > 0 {
|
||||||
|
return fiber.NewError(fiber.StatusConflict, "DailyChecklist already exists for at least one date in range")
|
||||||
|
}
|
||||||
|
|
||||||
|
for currentDate := startDate; !currentDate.After(endDate); currentDate = currentDate.AddDate(0, 0, 1) {
|
||||||
|
createStatus := status
|
||||||
|
var rejectedCount int64
|
||||||
|
if err := tx.Model(&entity.DailyChecklist{}).
|
||||||
|
Where("date = ? AND kandang_id = ? AND category = ? AND status = ? AND deleted_at IS NULL", currentDate, kandangID, category, dailyChecklistStatusRejected).
|
||||||
|
Count(&rejectedCount).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if rejectedCount > 0 {
|
||||||
|
createStatus = dailyChecklistStatusDraft
|
||||||
|
}
|
||||||
|
|
||||||
|
createBody := &entity.DailyChecklist{
|
||||||
|
KandangId: kandangID,
|
||||||
|
Date: currentDate,
|
||||||
|
Category: category,
|
||||||
|
Status: &createStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Create(createBody).Error; err != nil {
|
||||||
|
// Handle concurrent insert for active checklist in same date range.
|
||||||
|
var existingActiveCount int64
|
||||||
|
checkErr := tx.Model(&entity.DailyChecklist{}).
|
||||||
|
Where("date = ? AND kandang_id = ? AND category = ? AND (status IS NULL OR status <> ?) AND deleted_at IS NULL", currentDate, kandangID, category, dailyChecklistStatusRejected).
|
||||||
|
Count(&existingActiveCount).Error
|
||||||
|
if checkErr == nil && existingActiveCount > 0 {
|
||||||
|
return fiber.NewError(fiber.StatusConflict, "DailyChecklist already exists for at least one date in range")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentDate.Equal(startDate) {
|
||||||
|
*targetID = createBody.Id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s dailyChecklistService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint) (*entity.DailyChecklist, error) {
|
func (s dailyChecklistService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint) (*entity.DailyChecklist, error) {
|
||||||
if err := s.Validate.Struct(req); err != nil {
|
if err := s.Validate.Struct(req); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -712,7 +797,35 @@ func (s dailyChecklistService) DeleteOne(c *fiber.Ctx, id uint) error {
|
|||||||
if err := s.ensureChecklistAccess(c, id); err != nil {
|
if err := s.ensureChecklistAccess(c, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := s.Repository.DeleteOne(c.Context(), id); err != nil {
|
actorID, err := m.ActorIDFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusUnauthorized, "Failed to get actor ID from context")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.Repository.DB().WithContext(c.Context()).Transaction(func(tx *gorm.DB) error {
|
||||||
|
updateResult := tx.Model(&entity.DailyChecklist{}).
|
||||||
|
Where("id = ?", id).
|
||||||
|
Updates(map[string]any{
|
||||||
|
"deleted_by": actorID,
|
||||||
|
"updated_at": time.Now(),
|
||||||
|
})
|
||||||
|
if updateResult.Error != nil {
|
||||||
|
return updateResult.Error
|
||||||
|
}
|
||||||
|
if updateResult.RowsAffected == 0 {
|
||||||
|
return gorm.ErrRecordNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteResult := tx.Delete(&entity.DailyChecklist{}, id)
|
||||||
|
if deleteResult.Error != nil {
|
||||||
|
return deleteResult.Error
|
||||||
|
}
|
||||||
|
if deleteResult.RowsAffected == 0 {
|
||||||
|
return gorm.ErrRecordNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return fiber.NewError(fiber.StatusNotFound, "DailyChecklist not found")
|
return fiber.NewError(fiber.StatusNotFound, "DailyChecklist not found")
|
||||||
}
|
}
|
||||||
@@ -1152,7 +1265,7 @@ func (s dailyChecklistService) GetSummary(c *fiber.Ctx, params *validation.Summa
|
|||||||
SUM(CASE WHEN NOT a.checked THEN 1 ELSE 0 END) AS activity_left,
|
SUM(CASE WHEN NOT a.checked THEN 1 ELSE 0 END) AS activity_left,
|
||||||
MAX(a.updated_at) AS last_activity`).
|
MAX(a.updated_at) AS last_activity`).
|
||||||
Joins("JOIN daily_checklist_activity_tasks t ON t.id = a.task_id").
|
Joins("JOIN daily_checklist_activity_tasks t ON t.id = a.task_id").
|
||||||
Joins("JOIN daily_checklists d ON d.id = t.checklist_id").
|
Joins("JOIN daily_checklists d ON d.id = t.checklist_id AND d.deleted_at IS NULL").
|
||||||
Joins("JOIN kandang_groups k ON k.id = d.kandang_id").
|
Joins("JOIN kandang_groups k ON k.id = d.kandang_id").
|
||||||
Joins("JOIN employees e ON e.id = a.employee_id").
|
Joins("JOIN employees e ON e.id = a.employee_id").
|
||||||
Joins("JOIN locations loc ON loc.id = k.location_id").
|
Joins("JOIN locations loc ON loc.id = k.location_id").
|
||||||
@@ -1224,7 +1337,7 @@ func (s dailyChecklistService) GetReport(c *fiber.Ctx, params *validation.Report
|
|||||||
db := s.Repository.DB().WithContext(c.Context()).
|
db := s.Repository.DB().WithContext(c.Context()).
|
||||||
Table("daily_checklist_activity_task_assignments AS dca").
|
Table("daily_checklist_activity_task_assignments AS dca").
|
||||||
Joins("JOIN daily_checklist_activity_tasks dcat ON dcat.id = dca.task_id").
|
Joins("JOIN daily_checklist_activity_tasks dcat ON dcat.id = dca.task_id").
|
||||||
Joins("JOIN daily_checklists dc ON dc.id = dcat.checklist_id").
|
Joins("JOIN daily_checklists dc ON dc.id = dcat.checklist_id AND dc.deleted_at IS NULL").
|
||||||
Joins("JOIN employees e ON e.id = dca.employee_id").
|
Joins("JOIN employees e ON e.id = dca.employee_id").
|
||||||
Joins("JOIN kandang_groups k ON k.id = dc.kandang_id").
|
Joins("JOIN kandang_groups k ON k.id = dc.kandang_id").
|
||||||
Joins("JOIN locations loc ON loc.id = k.location_id").
|
Joins("JOIN locations loc ON loc.id = k.location_id").
|
||||||
|
|||||||
@@ -5,10 +5,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Create struct {
|
type Create struct {
|
||||||
Date string `json:"date" validate:"required"`
|
Date string `json:"date" validate:"required"`
|
||||||
KandangId uint `json:"kandang_id" validate:"required"`
|
KandangId uint `json:"kandang_id" validate:"required"`
|
||||||
Category string `json:"category" validate:"required"`
|
Category string `json:"category" validate:"required"`
|
||||||
Status string `json:"status" validate:"required"`
|
Status string `json:"status" validate:"required"`
|
||||||
|
EmptyKandang bool `json:"empty_kandang"`
|
||||||
|
EmptyKandangEndDate string `json:"empty_kandang_end_date"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Update struct {
|
type Update struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user