mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-25 07:45:44 +00:00
add api summary and update status
This commit is contained in:
@@ -53,6 +53,81 @@ func (u *DailyChecklistController) GetAll(c *fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *DailyChecklistController) GetSummary(c *fiber.Ctx) error {
|
||||||
|
query := &validation.SummaryQuery{
|
||||||
|
DateFrom: c.Query("date_from"),
|
||||||
|
DateTo: c.Query("date_to"),
|
||||||
|
Category: c.Query("category"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if query.DateFrom == "" || query.DateTo == "" {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "date_from and date_to are required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if kandangParam := c.Query("kandang_id"); kandangParam != "" {
|
||||||
|
kandangID, err := strconv.ParseUint(kandangParam, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid kandang_id")
|
||||||
|
}
|
||||||
|
value := uint(kandangID)
|
||||||
|
query.KandangID = &value
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := u.DailyChecklistService.GetSummary(c, query)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type summaryResponse struct {
|
||||||
|
PerformanceOverview []dto.DailyChecklistPerformanceOverviewDTO `json:"performance_overview"`
|
||||||
|
TrackingABK []dto.DailyChecklistSummaryDTO `json:"tracking_abk"`
|
||||||
|
}
|
||||||
|
|
||||||
|
performanceMap := make(map[uint]*dto.DailyChecklistPerformanceOverviewDTO)
|
||||||
|
tracking := make([]dto.DailyChecklistSummaryDTO, len(result))
|
||||||
|
|
||||||
|
for i, summary := range result {
|
||||||
|
tracking[i] = dto.DailyChecklistSummaryDTO{
|
||||||
|
EmployeeID: summary.EmployeeID,
|
||||||
|
EmployeeName: summary.EmployeeName,
|
||||||
|
KandangID: summary.KandangID,
|
||||||
|
KandangName: summary.KandangName,
|
||||||
|
TotalActivity: summary.TotalActivity,
|
||||||
|
ActivityDone: summary.ActivityDone,
|
||||||
|
ActivityLeft: summary.ActivityLeft,
|
||||||
|
CompletionRate: summary.CompletionRate,
|
||||||
|
LastActivity: summary.LastActivity,
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := performanceMap[summary.EmployeeID]; !ok {
|
||||||
|
performanceMap[summary.EmployeeID] = &dto.DailyChecklistPerformanceOverviewDTO{
|
||||||
|
EmployeeID: summary.EmployeeID,
|
||||||
|
EmployeeName: summary.EmployeeName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
performanceMap[summary.EmployeeID].TotalActivity += summary.TotalActivity
|
||||||
|
performanceMap[summary.EmployeeID].ActivityDone += summary.ActivityDone
|
||||||
|
performanceMap[summary.EmployeeID].ActivityLeft += summary.ActivityLeft
|
||||||
|
}
|
||||||
|
|
||||||
|
performance := make([]dto.DailyChecklistPerformanceOverviewDTO, 0, len(performanceMap))
|
||||||
|
for _, v := range performanceMap {
|
||||||
|
performance = append(performance, *v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(fiber.StatusOK).
|
||||||
|
JSON(response.Success{
|
||||||
|
Code: fiber.StatusOK,
|
||||||
|
Status: "success",
|
||||||
|
Message: "Get daily checklist summary successfully",
|
||||||
|
Data: summaryResponse{
|
||||||
|
PerformanceOverview: performance,
|
||||||
|
TrackingABK: tracking,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (u *DailyChecklistController) GetOne(c *fiber.Ctx) error {
|
func (u *DailyChecklistController) GetOne(c *fiber.Ctx) error {
|
||||||
param := c.Params("idDailyChecklist")
|
param := c.Params("idDailyChecklist")
|
||||||
|
|
||||||
@@ -98,7 +173,7 @@ func (u *DailyChecklistController) CreateOne(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
func (u *DailyChecklistController) UpdateOne(c *fiber.Ctx) error {
|
func (u *DailyChecklistController) UpdateOne(c *fiber.Ctx) error {
|
||||||
req := new(validation.Update)
|
req := new(validation.Update)
|
||||||
param := c.Params("id")
|
param := c.Params("idDailyChecklist")
|
||||||
|
|
||||||
id, err := strconv.Atoi(param)
|
id, err := strconv.Atoi(param)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -39,6 +39,26 @@ type DailyChecklistDetailDTO struct {
|
|||||||
Progress float64 `json:"progress"`
|
Progress float64 `json:"progress"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DailyChecklistSummaryDTO struct {
|
||||||
|
EmployeeID uint `json:"employee_id"`
|
||||||
|
EmployeeName string `json:"employee_name"`
|
||||||
|
KandangID uint `json:"kandang_id"`
|
||||||
|
KandangName string `json:"kandang_name"`
|
||||||
|
TotalActivity int `json:"total_activity"`
|
||||||
|
ActivityDone int `json:"activity_done"`
|
||||||
|
ActivityLeft int `json:"activity_left"`
|
||||||
|
CompletionRate int `json:"completion_rate"`
|
||||||
|
LastActivity *time.Time `json:"last_activity,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DailyChecklistPerformanceOverviewDTO struct {
|
||||||
|
EmployeeID uint `json:"employee_id"`
|
||||||
|
EmployeeName string `json:"employee_name"`
|
||||||
|
TotalActivity int `json:"total_activity"`
|
||||||
|
ActivityDone int `json:"activity_done"`
|
||||||
|
ActivityLeft int `json:"activity_left"`
|
||||||
|
}
|
||||||
|
|
||||||
type DailyChecklistPhaseDTO struct {
|
type DailyChecklistPhaseDTO struct {
|
||||||
Id uint `json:"id"`
|
Id uint `json:"id"`
|
||||||
PhaseId uint `json:"phase_id"`
|
PhaseId uint `json:"phase_id"`
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ func DailyChecklistRoutes(v1 fiber.Router, u user.UserService, s dailyChecklist.
|
|||||||
|
|
||||||
route.Get("/", ctrl.GetAll)
|
route.Get("/", ctrl.GetAll)
|
||||||
|
|
||||||
|
route.Get("/summary", ctrl.GetSummary)
|
||||||
|
|
||||||
// create daily checklist
|
// create daily checklist
|
||||||
route.Post("/", ctrl.CreateOne)
|
route.Post("/", ctrl.CreateOne)
|
||||||
|
|
||||||
@@ -53,6 +55,6 @@ func DailyChecklistRoutes(v1 fiber.Router, u user.UserService, s dailyChecklist.
|
|||||||
*/
|
*/
|
||||||
route.Post("/assignment", ctrl.UpdateAssignment)
|
route.Post("/assignment", ctrl.UpdateAssignment)
|
||||||
|
|
||||||
route.Patch("/:id", ctrl.UpdateOne)
|
route.Patch("/:idDailyChecklist", ctrl.UpdateOne)
|
||||||
route.Delete("/:id", ctrl.DeleteOne)
|
route.Delete("/:id", ctrl.DeleteOne)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ type DailyChecklistService interface {
|
|||||||
UpdateAssignment(ctx *fiber.Ctx, req *validation.UpdateAssignment) error
|
UpdateAssignment(ctx *fiber.Ctx, req *validation.UpdateAssignment) error
|
||||||
GetChecklistPhaseIDs(ctx *fiber.Ctx, checklistID uint) ([]uint, error)
|
GetChecklistPhaseIDs(ctx *fiber.Ctx, checklistID uint) ([]uint, error)
|
||||||
GetDetail(ctx *fiber.Ctx, id uint) (*DailyChecklistDetail, error)
|
GetDetail(ctx *fiber.Ctx, id uint) (*DailyChecklistDetail, error)
|
||||||
|
GetSummary(ctx *fiber.Ctx, params *validation.SummaryQuery) ([]DailyChecklistSummary, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type dailyChecklistService struct {
|
type dailyChecklistService struct {
|
||||||
@@ -52,6 +53,18 @@ type DailyChecklistDetail struct {
|
|||||||
Progress float64
|
Progress float64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DailyChecklistSummary struct {
|
||||||
|
EmployeeID uint
|
||||||
|
EmployeeName string
|
||||||
|
KandangID uint
|
||||||
|
KandangName string
|
||||||
|
TotalActivity int
|
||||||
|
ActivityDone int
|
||||||
|
ActivityLeft int
|
||||||
|
CompletionRate int
|
||||||
|
LastActivity *time.Time
|
||||||
|
}
|
||||||
|
|
||||||
func NewDailyChecklistService(repo repository.DailyChecklistRepository, phaseRepo phaseRepo.PhasesRepository, validate *validator.Validate) DailyChecklistService {
|
func NewDailyChecklistService(repo repository.DailyChecklistRepository, phaseRepo phaseRepo.PhasesRepository, validate *validator.Validate) DailyChecklistService {
|
||||||
return &dailyChecklistService{
|
return &dailyChecklistService{
|
||||||
Log: utils.Log,
|
Log: utils.Log,
|
||||||
@@ -202,14 +215,12 @@ func (s dailyChecklistService) UpdateOne(c *fiber.Ctx, req *validation.Update, i
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
updateBody := make(map[string]any)
|
updateBody := map[string]any{
|
||||||
|
"status": req.Status,
|
||||||
if req.Name != nil {
|
|
||||||
updateBody["name"] = *req.Name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(updateBody) == 0 {
|
if req.RejectReason != nil {
|
||||||
return s.GetOne(c, id)
|
updateBody["reject_reason"] = *req.RejectReason
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.Repository.PatchOne(c.Context(), id, updateBody, nil); err != nil {
|
if err := s.Repository.PatchOne(c.Context(), id, updateBody, nil); err != nil {
|
||||||
@@ -579,3 +590,86 @@ func (s dailyChecklistService) AssignTasks(c *fiber.Ctx, id uint, req *validatio
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s dailyChecklistService) GetSummary(c *fiber.Ctx, params *validation.SummaryQuery) ([]DailyChecklistSummary, error) {
|
||||||
|
if err := s.Validate.Struct(params); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dateFrom, err := time.Parse("2006-01-02", params.DateFrom)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, "invalid date_from format, use YYYY-MM-DD")
|
||||||
|
}
|
||||||
|
|
||||||
|
dateTo, err := time.Parse("2006-01-02", params.DateTo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, "invalid date_to format, use YYYY-MM-DD")
|
||||||
|
}
|
||||||
|
|
||||||
|
type summaryRow struct {
|
||||||
|
EmployeeID uint
|
||||||
|
EmployeeName string
|
||||||
|
KandangID uint
|
||||||
|
KandangName string
|
||||||
|
TotalActivity int64
|
||||||
|
ActivityDone int64
|
||||||
|
ActivityLeft int64
|
||||||
|
LastActivity *time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
rows := make([]summaryRow, 0)
|
||||||
|
db := s.Repository.DB().WithContext(c.Context()).
|
||||||
|
Table("daily_checklist_activity_task_assignments AS a").
|
||||||
|
Select(`
|
||||||
|
a.employee_id,
|
||||||
|
e.name AS employee_name,
|
||||||
|
d.kandang_id,
|
||||||
|
k.name AS kandang_name,
|
||||||
|
COUNT(*) AS total_activity,
|
||||||
|
SUM(CASE WHEN a.checked THEN 1 ELSE 0 END) AS activity_done,
|
||||||
|
SUM(CASE WHEN NOT a.checked THEN 1 ELSE 0 END) AS activity_left,
|
||||||
|
MAX(a.updated_at) AS last_activity`).
|
||||||
|
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 kandangs k ON k.id = d.kandang_id").
|
||||||
|
Joins("JOIN employees e ON e.id = a.employee_id").
|
||||||
|
Where("d.date BETWEEN ? AND ?", dateFrom, dateTo)
|
||||||
|
|
||||||
|
if params.Category != "" {
|
||||||
|
db = db.Where("d.category = ?", params.Category)
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.KandangID != nil {
|
||||||
|
db = db.Where("d.kandang_id = ?", *params.KandangID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.
|
||||||
|
Group("a.employee_id, e.name, d.kandang_id, k.name").
|
||||||
|
Order("e.name ASC").
|
||||||
|
Find(&rows).Error; err != nil {
|
||||||
|
s.Log.Errorf("Failed to get daily checklist summary: %+v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
summaries := make([]DailyChecklistSummary, len(rows))
|
||||||
|
for i, row := range rows {
|
||||||
|
completionRate := 0
|
||||||
|
if row.TotalActivity > 0 {
|
||||||
|
completionRate = int(math.Round(float64(row.ActivityDone) / float64(row.TotalActivity) * 100))
|
||||||
|
}
|
||||||
|
|
||||||
|
summaries[i] = DailyChecklistSummary{
|
||||||
|
EmployeeID: row.EmployeeID,
|
||||||
|
EmployeeName: row.EmployeeName,
|
||||||
|
KandangID: row.KandangID,
|
||||||
|
KandangName: row.KandangName,
|
||||||
|
TotalActivity: int(row.TotalActivity),
|
||||||
|
ActivityDone: int(row.ActivityDone),
|
||||||
|
ActivityLeft: int(row.ActivityLeft),
|
||||||
|
CompletionRate: completionRate,
|
||||||
|
LastActivity: row.LastActivity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return summaries, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ type Create struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Update struct {
|
type Update struct {
|
||||||
Name *string `json:"name,omitempty" validate:"omitempty"`
|
Status string `json:"status" validate:"required"`
|
||||||
|
RejectReason *string `json:"reject_reason" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Query struct {
|
type Query struct {
|
||||||
@@ -31,3 +32,10 @@ type UpdateAssignment struct {
|
|||||||
Checked *bool `json:"checked,omitempty"`
|
Checked *bool `json:"checked,omitempty"`
|
||||||
Note *string `json:"note,omitempty"`
|
Note *string `json:"note,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SummaryQuery struct {
|
||||||
|
DateFrom string `query:"date_from" validate:"required"`
|
||||||
|
DateTo string `query:"date_to" validate:"required"`
|
||||||
|
Category string `query:"category" validate:"omitempty"`
|
||||||
|
KandangID *uint `query:"kandang_id" validate:"omitempty"`
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user