Merge branch 'feat/daily-checklist-upload-documents' into 'development'

[FEAT][BE]: add api upload documents daily checklist

See merge request mbugroup/lti-api!154
This commit is contained in:
Hafizh A. Y.
2026-01-12 03:39:06 +00:00
9 changed files with 219 additions and 40 deletions
+7 -6
View File
@@ -7,12 +7,13 @@ import (
) )
type Phases struct { type Phases struct {
Id uint `gorm:"primaryKey"` Id uint `gorm:"primaryKey"`
Name string `gorm:"not null"` Name string `gorm:"not null"`
IsActive bool `gorm:"not null;default:true"` IsActive bool `gorm:"not null;default:true"`
Category string `gorm:"type:category_code;not null"` Category string `gorm:"type:category_code;not null"`
CreatedAt time.Time `gorm:"autoCreateTime"` CreatedAt time.Time `gorm:"autoCreateTime"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
ActivityCount int `gorm:"-" json:"-"`
Activities []PhaseActivity `gorm:"foreignKey:PhaseId;references:Id"` Activities []PhaseActivity `gorm:"foreignKey:PhaseId;references:Id"`
} }
@@ -74,6 +74,7 @@ func (u *DailyChecklistController) GetAll(c *fiber.Ctx) error {
Name: name, Name: name,
Status: status, Status: status,
Category: item.Category, Category: item.Category,
RejectReason: item.RejectReason,
Date: item.Date, Date: item.Date,
Kandang: kandang, Kandang: kandang,
CreatedUser: nil, CreatedUser: nil,
@@ -150,6 +151,10 @@ func (u *DailyChecklistController) GetSummary(c *fiber.Ctx) error {
performanceMap[summary.EmployeeID] = &dto.DailyChecklistPerformanceOverviewDTO{ performanceMap[summary.EmployeeID] = &dto.DailyChecklistPerformanceOverviewDTO{
EmployeeID: summary.EmployeeID, EmployeeID: summary.EmployeeID,
EmployeeName: summary.EmployeeName, EmployeeName: summary.EmployeeName,
Kandang: dto.DailyChecklistReportEntityDTO{
Id: summary.KandangID,
Name: summary.KandangName,
},
} }
} }
@@ -303,12 +308,22 @@ func (u *DailyChecklistController) GetOne(c *fiber.Ctx) error {
return err return err
} }
documentDTOs := make([]dto.DailyChecklistDocumentDTO, len(detail.DocumentURLs))
for i, doc := range detail.DocumentURLs {
documentDTOs[i] = dto.DailyChecklistDocumentDTO{
Id: doc.ID,
Name: doc.Name,
Size: doc.Size,
URL: doc.URL,
}
}
return c.Status(fiber.StatusOK). return c.Status(fiber.StatusOK).
JSON(response.Success{ JSON(response.Success{
Code: fiber.StatusOK, Code: fiber.StatusOK,
Status: "success", Status: "success",
Message: "Get dailyChecklist successfully", Message: "Get dailyChecklist successfully",
Data: dto.ToDailyChecklistDetailDTO(detail.Checklist, detail.Phases, detail.Tasks, detail.AssignedEmployees, detail.TotalActivities, detail.Progress), Data: dto.ToDailyChecklistDetailDTO(detail.Checklist, detail.Phases, detail.Tasks, detail.AssignedEmployees, detail.TotalActivities, detail.Progress, documentDTOs),
}) })
} }
@@ -342,6 +357,12 @@ func (u *DailyChecklistController) UpdateOne(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "Invalid Id") return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
} }
form, err := c.MultipartForm()
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Invalid multipart form")
}
req.Documents = form.File["documents"]
if err := c.BodyParser(req); err != nil { if err := c.BodyParser(req); err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body") return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
} }
@@ -31,6 +31,7 @@ type DailyChecklistListDTO struct {
TotalPhase int `json:"total_phase"` TotalPhase int `json:"total_phase"`
TotalActivity int `json:"total_activity"` TotalActivity int `json:"total_activity"`
Progress int `json:"progress"` Progress int `json:"progress"`
RejectReason *string `json:"reject_reason"`
} }
type DailyChecklistDetailDTO struct { type DailyChecklistDetailDTO struct {
@@ -40,6 +41,14 @@ type DailyChecklistDetailDTO struct {
AssignedEmployees []employeeDTO.EmployeesRelationDTO `json:"assigned_employees"` AssignedEmployees []employeeDTO.EmployeesRelationDTO `json:"assigned_employees"`
TotalActivity int `json:"total_activity"` TotalActivity int `json:"total_activity"`
Progress float64 `json:"progress"` Progress float64 `json:"progress"`
DocumentURLs []DailyChecklistDocumentDTO `json:"document_urls"`
}
type DailyChecklistDocumentDTO struct {
Id uint `json:"id"`
Name string `json:"name"`
Size float64 `json:"size"`
URL string `json:"url"`
} }
type DailyChecklistSummaryDTO struct { type DailyChecklistSummaryDTO struct {
@@ -55,11 +64,12 @@ type DailyChecklistSummaryDTO struct {
} }
type DailyChecklistPerformanceOverviewDTO struct { type DailyChecklistPerformanceOverviewDTO struct {
EmployeeID uint `json:"employee_id"` EmployeeID uint `json:"employee_id"`
EmployeeName string `json:"employee_name"` EmployeeName string `json:"employee_name"`
TotalActivity int `json:"total_activity"` Kandang DailyChecklistReportEntityDTO `json:"kandang"`
ActivityDone int `json:"activity_done"` TotalActivity int `json:"total_activity"`
ActivityLeft int `json:"activity_left"` ActivityDone int `json:"activity_done"`
ActivityLeft int `json:"activity_left"`
} }
type DailyChecklistReportDTO struct { type DailyChecklistReportDTO struct {
@@ -165,10 +175,11 @@ func ToDailyChecklistListDTO(e entity.DailyChecklist) DailyChecklistListDTO {
TotalPhase: 0, TotalPhase: 0,
TotalActivity: 0, TotalActivity: 0,
Progress: 0, Progress: 0,
RejectReason: e.RejectReason,
} }
} }
func ToDailyChecklistDetailDTO(checklist entity.DailyChecklist, phases []entity.DailyChecklistPhase, tasks []entity.DailyChecklistActivityTask, assignedEmployees []entity.Employee, totalActivities int, progress float64) DailyChecklistDetailDTO { func ToDailyChecklistDetailDTO(checklist entity.DailyChecklist, phases []entity.DailyChecklistPhase, tasks []entity.DailyChecklistActivityTask, assignedEmployees []entity.Employee, totalActivities int, progress float64, documentURLs []DailyChecklistDocumentDTO) DailyChecklistDetailDTO {
phaseDTOs := make([]DailyChecklistPhaseDTO, 0, len(phases)) phaseDTOs := make([]DailyChecklistPhaseDTO, 0, len(phases))
for _, phase := range phases { for _, phase := range phases {
phaseDTOs = append(phaseDTOs, DailyChecklistPhaseDTO{ phaseDTOs = append(phaseDTOs, DailyChecklistPhaseDTO{
@@ -228,5 +239,6 @@ func ToDailyChecklistDetailDTO(checklist entity.DailyChecklist, phases []entity.
AssignedEmployees: assignedDTOs, AssignedEmployees: assignedDTOs,
TotalActivity: totalActivities, TotalActivity: totalActivities,
Progress: progress, Progress: progress,
DocumentURLs: documentURLs,
} }
} }
+11 -1
View File
@@ -1,10 +1,15 @@
package dailyChecklists package dailyChecklists
import ( import (
"context"
"fmt"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"gorm.io/gorm" "gorm.io/gorm"
commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
rDailyChecklist "gitlab.com/mbugroup/lti-api.git/internal/modules/daily-checklists/repositories" rDailyChecklist "gitlab.com/mbugroup/lti-api.git/internal/modules/daily-checklists/repositories"
sDailyChecklist "gitlab.com/mbugroup/lti-api.git/internal/modules/daily-checklists/services" sDailyChecklist "gitlab.com/mbugroup/lti-api.git/internal/modules/daily-checklists/services"
rPhases "gitlab.com/mbugroup/lti-api.git/internal/modules/master/phasess/repositories" rPhases "gitlab.com/mbugroup/lti-api.git/internal/modules/master/phasess/repositories"
@@ -19,8 +24,13 @@ func (DailyChecklistModule) RegisterRoutes(router fiber.Router, db *gorm.DB, val
dailyChecklistRepo := rDailyChecklist.NewDailyChecklistRepository(db) dailyChecklistRepo := rDailyChecklist.NewDailyChecklistRepository(db)
phasesRepo := rPhases.NewPhasesRepository(db) phasesRepo := rPhases.NewPhasesRepository(db)
userRepo := rUser.NewUserRepository(db) userRepo := rUser.NewUserRepository(db)
documentRepo := commonRepo.NewDocumentRepository(db)
documentSvc, err := commonSvc.NewDocumentServiceFromConfig(context.Background(), documentRepo)
if err != nil {
panic(fmt.Sprintf("failed to create document service: %v", err))
}
dailyChecklistService := sDailyChecklist.NewDailyChecklistService(dailyChecklistRepo, phasesRepo, validate) dailyChecklistService := sDailyChecklist.NewDailyChecklistService(dailyChecklistRepo, phasesRepo, validate, documentSvc)
userService := sUser.NewUserService(userRepo, validate) userService := sUser.NewUserService(userRepo, validate)
DailyChecklistRoutes(router, userService, dailyChecklistService) DailyChecklistRoutes(router, userService, dailyChecklistService)
@@ -9,6 +9,7 @@ import (
"time" "time"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities" entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
middleware "gitlab.com/mbugroup/lti-api.git/internal/middleware"
repository "gitlab.com/mbugroup/lti-api.git/internal/modules/daily-checklists/repositories" repository "gitlab.com/mbugroup/lti-api.git/internal/modules/daily-checklists/repositories"
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/daily-checklists/validations" validation "gitlab.com/mbugroup/lti-api.git/internal/modules/daily-checklists/validations"
phaseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/master/phasess/repositories" phaseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/master/phasess/repositories"
@@ -17,6 +18,7 @@ import (
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/clause" "gorm.io/gorm/clause"
) )
@@ -39,10 +41,18 @@ type DailyChecklistService interface {
} }
type dailyChecklistService struct { type dailyChecklistService struct {
Log *logrus.Logger Log *logrus.Logger
Validate *validator.Validate Validate *validator.Validate
Repository repository.DailyChecklistRepository Repository repository.DailyChecklistRepository
PhaseRepo phaseRepo.PhasesRepository PhaseRepo phaseRepo.PhasesRepository
DocumentSvc commonSvc.DocumentService
}
type DailyChecklistDocument struct {
ID uint
Name string
Size float64
URL string
} }
type DailyChecklistDetail struct { type DailyChecklistDetail struct {
@@ -52,6 +62,7 @@ type DailyChecklistDetail struct {
AssignedEmployees []entity.Employee AssignedEmployees []entity.Employee
TotalActivities int TotalActivities int
Progress float64 Progress float64
DocumentURLs []DailyChecklistDocument
} }
type DailyChecklistListItem struct { type DailyChecklistListItem struct {
@@ -60,6 +71,7 @@ type DailyChecklistListItem struct {
Date time.Time Date time.Time
Category string Category string
Status *string Status *string
RejectReason *string
CreatedAt time.Time CreatedAt time.Time
UpdatedAt time.Time UpdatedAt time.Time
Kandang entity.Kandang Kandang entity.Kandang
@@ -108,12 +120,13 @@ type DailyChecklistReportCategory struct {
Baik int Baik int
} }
func NewDailyChecklistService(repo repository.DailyChecklistRepository, phaseRepo phaseRepo.PhasesRepository, validate *validator.Validate) 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,
Validate: validate, Validate: validate,
Repository: repo, Repository: repo,
PhaseRepo: phaseRepo, PhaseRepo: phaseRepo,
DocumentSvc: documentSvc,
} }
} }
@@ -158,7 +171,7 @@ func (s dailyChecklistService) GetAll(c *fiber.Ctx, params *validation.Query) ([
if params.Search != "" { if params.Search != "" {
like := "%" + params.Search + "%" like := "%" + params.Search + "%"
db = db.Where("(k.name ILIKE ? OR dc.category ILIKE ?)", like, like) db = db.Where("(k.name ILIKE ? OR dc.category::text ILIKE ?)", like, like)
} }
countDB := db.Session(&gorm.Session{}) countDB := db.Session(&gorm.Session{})
@@ -174,6 +187,7 @@ func (s dailyChecklistService) GetAll(c *fiber.Ctx, params *validation.Query) ([
Date time.Time Date time.Time
Category string Category string
Status *string Status *string
RejectReason *string
CreatedAt time.Time CreatedAt time.Time
UpdatedAt time.Time UpdatedAt time.Time
KandangID uint KandangID uint
@@ -192,6 +206,7 @@ func (s dailyChecklistService) GetAll(c *fiber.Ctx, params *validation.Query) ([
dc.date, dc.date,
dc.category, dc.category,
dc.status, dc.status,
dc.reject_reason,
dc.created_at, dc.created_at,
dc.updated_at, dc.updated_at,
dc.kandang_id, dc.kandang_id,
@@ -265,6 +280,7 @@ func (s dailyChecklistService) GetAll(c *fiber.Ctx, params *validation.Query) ([
Date: row.Date, Date: row.Date,
Category: row.Category, Category: row.Category,
Status: row.Status, Status: row.Status,
RejectReason: row.RejectReason,
CreatedAt: row.CreatedAt, CreatedAt: row.CreatedAt,
UpdatedAt: row.UpdatedAt, UpdatedAt: row.UpdatedAt,
Kandang: kandangMap[row.KandangID], Kandang: kandangMap[row.KandangID],
@@ -345,6 +361,29 @@ func (s dailyChecklistService) GetDetail(c *fiber.Ctx, id uint) (*DailyChecklist
progress = math.Round((float64(completedAssignments) / float64(totalAssignments)) * 100) progress = math.Round((float64(completedAssignments) / float64(totalAssignments)) * 100)
} }
documentURLs := make([]DailyChecklistDocument, 0)
if s.DocumentSvc != nil {
documents, err := s.DocumentSvc.ListByTarget(c.Context(), string(utils.DocumentTypeDailyChecklist), uint64(id))
if err != nil {
s.Log.Errorf("Failed to list documents for daily checklist %d: %+v", id, err)
return nil, err
}
for _, doc := range documents {
url, err := s.DocumentSvc.PresignURL(c.Context(), doc, 0)
if err != nil {
s.Log.Errorf("Failed to presign document %d for daily checklist %d: %+v", doc.Id, id, err)
continue
}
documentURLs = append(documentURLs, DailyChecklistDocument{
ID: doc.Id,
Name: doc.Name,
Size: doc.Size,
URL: url,
})
}
}
return &DailyChecklistDetail{ return &DailyChecklistDetail{
Checklist: *checklist, Checklist: *checklist,
Phases: phases, Phases: phases,
@@ -352,6 +391,7 @@ func (s dailyChecklistService) GetDetail(c *fiber.Ctx, id uint) (*DailyChecklist
AssignedEmployees: assignedEmployees, AssignedEmployees: assignedEmployees,
TotalActivities: totalActivities, TotalActivities: totalActivities,
Progress: progress, Progress: progress,
DocumentURLs: documentURLs,
}, nil }, nil
} }
@@ -377,7 +417,7 @@ func (s *dailyChecklistService) CreateOne(c *fiber.Ctx, req *validation.Create)
err = s.Repository.DB().WithContext(c.Context()).Clauses(clause.OnConflict{ err = s.Repository.DB().WithContext(c.Context()).Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "date"}, {Name: "kandang_id"}, {Name: "category"}}, Columns: []clause.Column{{Name: "date"}, {Name: "kandang_id"}, {Name: "category"}},
DoUpdates: clause.Assignments(map[string]any{"status": status, "updated_at": time.Now()}), DoUpdates: clause.Assignments(map[string]any{"updated_at": time.Now()}),
}).Create(createBody).Error }).Create(createBody).Error
if err != nil { if err != nil {
s.Log.Errorf("Failed to upsert dailyChecklist: %+v", err) s.Log.Errorf("Failed to upsert dailyChecklist: %+v", err)
@@ -392,6 +432,22 @@ func (s dailyChecklistService) UpdateOne(c *fiber.Ctx, req *validation.Update, i
return nil, err return nil, err
} }
deletedIDs := make([]uint, 0)
if req.DeletedDocumentIDs != nil {
parts := strings.Split(*req.DeletedDocumentIDs, ",")
for _, part := range parts {
part = strings.TrimSpace(part)
if part == "" {
continue
}
parsedID, err := strconv.ParseUint(part, 10, 64)
if err != nil {
return nil, fiber.NewError(fiber.StatusBadRequest, "invalid deleted_document_ids")
}
deletedIDs = append(deletedIDs, uint(parsedID))
}
}
updateBody := map[string]any{ updateBody := map[string]any{
"status": req.Status, "status": req.Status,
} }
@@ -400,6 +456,40 @@ func (s dailyChecklistService) UpdateOne(c *fiber.Ctx, req *validation.Update, i
updateBody["reject_reason"] = *req.RejectReason updateBody["reject_reason"] = *req.RejectReason
} }
actorID, err := middleware.ActorIDFromContext(c)
if err != nil {
return &entity.DailyChecklist{}, fiber.NewError(fiber.StatusUnauthorized, "Failed to get actor ID from context")
}
if len(deletedIDs) > 0 && s.DocumentSvc != nil {
if err := s.DocumentSvc.DeleteDocuments(c.Context(), deletedIDs, true); err != nil {
s.Log.Errorf("Failed to delete daily checklist documents: %+v", err)
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to delete daily checklist documents")
}
}
if len(req.Documents) > 0 {
documentFiles := make([]commonSvc.DocumentFile, 0, len(req.Documents))
for idx, file := range req.Documents {
documentFiles = append(documentFiles, commonSvc.DocumentFile{
File: file,
Type: string(utils.DocumentTypeDailyChecklist),
Index: &idx,
})
}
_, err := s.DocumentSvc.UploadDocuments(c.Context(), commonSvc.DocumentUploadRequest{
DocumentableType: string(utils.DocumentTypeDailyChecklist),
DocumentableID: uint64(id),
CreatedBy: &actorID,
Files: documentFiles,
})
if err != nil {
s.Log.Errorf("Failed to upload daily checklist documents: %+v", err)
return &entity.DailyChecklist{}, fiber.NewError(fiber.StatusInternalServerError, "Failed to upload daily checklist documents")
}
}
if err := s.Repository.PatchOne(c.Context(), id, updateBody, nil); err != nil { if err := s.Repository.PatchOne(c.Context(), id, updateBody, nil); err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fiber.NewError(fiber.StatusNotFound, "DailyChecklist not found") return nil, fiber.NewError(fiber.StatusNotFound, "DailyChecklist not found")
@@ -869,7 +959,8 @@ func (s dailyChecklistService) GetReport(c *fiber.Ctx, params *validation.Report
Joins("JOIN areas a ON a.id = loc.area_id"). Joins("JOIN areas a ON a.id = loc.area_id").
Joins("JOIN phases p ON p.id = dcat.phase_id"). Joins("JOIN phases p ON p.id = dcat.phase_id").
Where("EXTRACT(MONTH FROM dc.date) = ?", params.Month). Where("EXTRACT(MONTH FROM dc.date) = ?", params.Month).
Where("EXTRACT(YEAR FROM dc.date) = ?", params.Year) Where("EXTRACT(YEAR FROM dc.date) = ?", params.Year).
Where("dc.status = ?", "APPROVED")
if params.AreaID != nil { if params.AreaID != nil {
db = db.Where("a.id = ?", *params.AreaID) db = db.Where("a.id = ?", *params.AreaID)
@@ -1,5 +1,9 @@
package validation package validation
import (
"mime/multipart"
)
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"`
@@ -8,8 +12,10 @@ type Create struct {
} }
type Update struct { type Update struct {
Status string `json:"status" validate:"required"` Status string `form:"status" json:"status" validate:"required"`
RejectReason *string `json:"reject_reason"` RejectReason *string `form:"reject_reason" json:"reject_reason"`
Documents []*multipart.FileHeader `form:"documents" json:"documents" validate:"omitempty,dive"`
DeletedDocumentIDs *string `form:"deleted_document_ids" json:"deleted_document_ids"`
} }
type Query struct { type Query struct {
@@ -15,12 +15,13 @@ type PhasesRelationDTO struct {
} }
type PhasesListDTO struct { type PhasesListDTO struct {
Id uint `json:"id"` Id uint `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Category string `json:"category"` Category string `json:"category"`
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
CreatedUser *userDTO.UserRelationDTO `json:"created_user"` ActivityCount int `json:"activity_count"`
CreatedAt time.Time `json:"created_at"` CreatedUser *userDTO.UserRelationDTO `json:"created_user"`
CreatedAt time.Time `json:"created_at"`
} }
type PhasesDetailDTO struct { type PhasesDetailDTO struct {
@@ -44,12 +45,13 @@ func ToPhasesListDTO(e entity.Phases) PhasesListDTO {
// } // }
return PhasesListDTO{ return PhasesListDTO{
Id: e.Id, Id: e.Id,
Name: e.Name, Name: e.Name,
Category: e.Category, Category: e.Category,
IsActive: e.IsActive, IsActive: e.IsActive,
CreatedAt: e.CreatedAt, ActivityCount: e.ActivityCount,
CreatedUser: createdUser, CreatedAt: e.CreatedAt,
CreatedUser: createdUser,
} }
} }
@@ -63,6 +63,40 @@ func (s phasesService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.
s.Log.Errorf("Failed to get phasess: %+v", err) s.Log.Errorf("Failed to get phasess: %+v", err)
return nil, 0, err return nil, 0, err
} }
if len(phasess) > 0 {
ids := make([]uint, 0, len(phasess))
for _, phase := range phasess {
ids = append(ids, phase.Id)
}
type activityCountRow struct {
PhaseID uint
Count int64
}
var rows []activityCountRow
if err := s.Repository.DB().WithContext(c.Context()).
Table("phase_activities").
Select("phase_id, COUNT(*) AS count").
Where("phase_id IN ? AND deleted_at IS NULL", ids).
Group("phase_id").
Scan(&rows).Error; err != nil {
s.Log.Errorf("Failed to count phase activities: %+v", err)
return nil, 0, err
}
countMap := make(map[uint]int64, len(rows))
for _, row := range rows {
countMap[row.PhaseID] = row.Count
}
for i := range phasess {
if count, ok := countMap[phasess[i].Id]; ok {
phasess[i].ActivityCount = int(count)
}
}
}
return phasess, total, nil return phasess, total, nil
} }
+2
View File
@@ -432,6 +432,8 @@ const (
DocumentableTypeExpense DocumentableType = "EXPENSE" DocumentableTypeExpense DocumentableType = "EXPENSE"
DocumentableTypeExpenseRealization DocumentableType = "EXPENSE_REALIZATION" DocumentableTypeExpenseRealization DocumentableType = "EXPENSE_REALIZATION"
DocumentableTypePurchaseItem DocumentableType = "PURCHASE_ITEM" DocumentableTypePurchaseItem DocumentableType = "PURCHASE_ITEM"
DocumentTypeDailyChecklist DocumentType = "DAILY_CHECKLIST_DOCUMENT"
) )
// ------------------------------------------------------------------- // -------------------------------------------------------------------