mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
add api bulk update status daily checklist; change hpp real to estimate
This commit is contained in:
@@ -351,6 +351,31 @@ func (u *DailyChecklistController) CreateOne(c *fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *DailyChecklistController) BulkUpdate(c *fiber.Ctx) error {
|
||||||
|
req := new(validation.BulkStatusUpdate)
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
|
||||||
|
}
|
||||||
|
|
||||||
|
results, err := u.DailyChecklistService.BulkUpdate(c, req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
responseData := make([]dto.DailyChecklistListDTO, len(results))
|
||||||
|
for i, item := range results {
|
||||||
|
responseData[i] = dto.ToDailyChecklistListDTO(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(fiber.StatusOK).
|
||||||
|
JSON(response.Success{
|
||||||
|
Code: fiber.StatusOK,
|
||||||
|
Status: "success",
|
||||||
|
Message: "Bulk update dailyChecklist successfully",
|
||||||
|
Data: responseData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
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("idDailyChecklist")
|
param := c.Params("idDailyChecklist")
|
||||||
|
|||||||
@@ -1,13 +1,22 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||||
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DailyChecklistRepository interface {
|
type DailyChecklistRepository interface {
|
||||||
repository.BaseRepository[entity.DailyChecklist]
|
repository.BaseRepository[entity.DailyChecklist]
|
||||||
|
ListScopedChecklistIDs(c *fiber.Ctx, ids []uint) ([]uint, error)
|
||||||
|
BulkUpdateStatus(ctx context.Context, ids []uint, status string, rejectReason *string) error
|
||||||
|
ListByIDsWithKandang(ctx context.Context, ids []uint) ([]entity.DailyChecklist, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type DailyChecklistRepositoryImpl struct {
|
type DailyChecklistRepositoryImpl struct {
|
||||||
@@ -19,3 +28,70 @@ func NewDailyChecklistRepository(db *gorm.DB) DailyChecklistRepository {
|
|||||||
BaseRepositoryImpl: repository.NewBaseRepository[entity.DailyChecklist](db),
|
BaseRepositoryImpl: repository.NewBaseRepository[entity.DailyChecklist](db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *DailyChecklistRepositoryImpl) ListScopedChecklistIDs(c *fiber.Ctx, ids []uint) ([]uint, error) {
|
||||||
|
if len(ids) == 0 {
|
||||||
|
return []uint{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
db := r.DB().WithContext(c.Context()).
|
||||||
|
Table("daily_checklists dc").
|
||||||
|
Select("dc.id").
|
||||||
|
Joins("JOIN kandang_groups k ON k.id = dc.kandang_id").
|
||||||
|
Joins("JOIN locations loc ON loc.id = k.location_id").
|
||||||
|
Joins("JOIN areas a ON a.id = loc.area_id").
|
||||||
|
Where("dc.id IN ?", ids)
|
||||||
|
|
||||||
|
db, err := m.ApplyLocationAreaScope(c, db, "loc.id", "a.id")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var scopedIDs []uint
|
||||||
|
if err := db.Pluck("dc.id", &scopedIDs).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return scopedIDs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *DailyChecklistRepositoryImpl) BulkUpdateStatus(ctx context.Context, ids []uint, status string, rejectReason *string) error {
|
||||||
|
if len(ids) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBody := map[string]any{
|
||||||
|
"status": status,
|
||||||
|
"reject_reason": rejectReason,
|
||||||
|
"updated_at": time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||||
|
result := tx.Model(&entity.DailyChecklist{}).
|
||||||
|
Where("id IN ?", ids).
|
||||||
|
Updates(updateBody)
|
||||||
|
if result.Error != nil {
|
||||||
|
return result.Error
|
||||||
|
}
|
||||||
|
if result.RowsAffected != int64(len(ids)) {
|
||||||
|
return gorm.ErrRecordNotFound
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *DailyChecklistRepositoryImpl) ListByIDsWithKandang(ctx context.Context, ids []uint) ([]entity.DailyChecklist, error) {
|
||||||
|
if len(ids) == 0 {
|
||||||
|
return []entity.DailyChecklist{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var items []entity.DailyChecklist
|
||||||
|
if err := r.DB().WithContext(ctx).
|
||||||
|
Where("id IN ?", ids).
|
||||||
|
Preload("Kandang").
|
||||||
|
Find(&items).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ func DailyChecklistRoutes(v1 fiber.Router, u user.UserService, s dailyChecklist.
|
|||||||
*/
|
*/
|
||||||
route.Post("/assignment", m.RequirePermissions(m.P_DailyChecklistCreateOne), ctrl.UpdateAssignment)
|
route.Post("/assignment", m.RequirePermissions(m.P_DailyChecklistCreateOne), ctrl.UpdateAssignment)
|
||||||
|
|
||||||
|
route.Patch("/bulk-update", m.RequirePermissions(m.P_DailyChecklistCreateOne), ctrl.BulkUpdate)
|
||||||
route.Patch("/:idDailyChecklist", m.RequirePermissions(m.P_DailyChecklistCreateOne), ctrl.UpdateOne)
|
route.Patch("/:idDailyChecklist", m.RequirePermissions(m.P_DailyChecklistCreateOne), ctrl.UpdateOne)
|
||||||
route.Delete("/:idDailyChecklist", m.RequirePermissions(m.P_DailyChecklistCreateOne), ctrl.DeleteOne)
|
route.Delete("/:idDailyChecklist", m.RequirePermissions(m.P_DailyChecklistCreateOne), ctrl.DeleteOne)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ type DailyChecklistService interface {
|
|||||||
GetOne(ctx *fiber.Ctx, id uint) (*entity.DailyChecklist, error)
|
GetOne(ctx *fiber.Ctx, id uint) (*entity.DailyChecklist, error)
|
||||||
CreateOne(ctx *fiber.Ctx, req *validation.Create) (*entity.DailyChecklist, error)
|
CreateOne(ctx *fiber.Ctx, req *validation.Create) (*entity.DailyChecklist, error)
|
||||||
UpdateOne(ctx *fiber.Ctx, req *validation.Update, id uint) (*entity.DailyChecklist, error)
|
UpdateOne(ctx *fiber.Ctx, req *validation.Update, id uint) (*entity.DailyChecklist, error)
|
||||||
|
BulkUpdate(ctx *fiber.Ctx, req *validation.BulkStatusUpdate) ([]entity.DailyChecklist, error)
|
||||||
DeleteOne(ctx *fiber.Ctx, id uint) error
|
DeleteOne(ctx *fiber.Ctx, id uint) error
|
||||||
AssignPhases(ctx *fiber.Ctx, id uint, req *validation.AssignPhases) error
|
AssignPhases(ctx *fiber.Ctx, id uint, req *validation.AssignPhases) error
|
||||||
AssignTasks(ctx *fiber.Ctx, id uint, req *validation.AssignTask) error
|
AssignTasks(ctx *fiber.Ctx, id uint, req *validation.AssignTask) error
|
||||||
@@ -646,6 +647,67 @@ func (s dailyChecklistService) UpdateOne(c *fiber.Ctx, req *validation.Update, i
|
|||||||
return s.GetOne(c, id)
|
return s.GetOne(c, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s dailyChecklistService) BulkUpdate(c *fiber.Ctx, req *validation.BulkStatusUpdate) ([]entity.DailyChecklist, error) {
|
||||||
|
if err := s.Validate.Struct(req); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
status := strings.ToUpper(strings.TrimSpace(req.Status))
|
||||||
|
if status != "APPROVED" && status != "REJECTED" {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, "status must be APPROVED or REJECTED")
|
||||||
|
}
|
||||||
|
|
||||||
|
ids, err := parseChecklistIDs(req.IDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
if len(ids) == 0 {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, "ids cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
scopedIDs, err := s.Repository.ListScopedChecklistIDs(c, ids)
|
||||||
|
if err != nil {
|
||||||
|
s.Log.Errorf("Failed to validate daily checklist scope for bulk update: %+v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(scopedIDs) != len(ids) {
|
||||||
|
return nil, fiber.NewError(fiber.StatusNotFound, "DailyChecklist not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
var rejectReason *string
|
||||||
|
if status == "REJECTED" {
|
||||||
|
rejectReason = req.RejectReason
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.Repository.BulkUpdateStatus(c.Context(), ids, status, rejectReason); err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, fiber.NewError(fiber.StatusNotFound, "DailyChecklist not found")
|
||||||
|
}
|
||||||
|
s.Log.Errorf("Failed to bulk update daily checklist status: %+v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
updated, err := s.Repository.ListByIDsWithKandang(c.Context(), ids)
|
||||||
|
if err != nil {
|
||||||
|
s.Log.Errorf("Failed to fetch updated daily checklists: %+v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(updated) != len(ids) {
|
||||||
|
return nil, fiber.NewError(fiber.StatusNotFound, "DailyChecklist not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
orderByID := make(map[uint]int, len(ids))
|
||||||
|
for idx, id := range ids {
|
||||||
|
orderByID[id] = idx
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(updated, func(i, j int) bool {
|
||||||
|
return orderByID[updated[i].Id] < orderByID[updated[j].Id]
|
||||||
|
})
|
||||||
|
|
||||||
|
return updated, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s dailyChecklistService) DeleteOne(c *fiber.Ctx, id uint) error {
|
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
|
||||||
@@ -908,6 +970,33 @@ func parsePhaseIDs(raw string) ([]uint, error) {
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseChecklistIDs(raw string) ([]uint, error) {
|
||||||
|
parts := strings.Split(raw, ",")
|
||||||
|
result := make([]uint, 0, len(parts))
|
||||||
|
seen := make(map[uint]struct{})
|
||||||
|
|
||||||
|
for _, part := range parts {
|
||||||
|
value := strings.TrimSpace(part)
|
||||||
|
if value == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
num, err := strconv.ParseUint(value, 10, 64)
|
||||||
|
if err != nil || num == 0 {
|
||||||
|
return nil, errors.New("invalid daily checklist id: " + value)
|
||||||
|
}
|
||||||
|
|
||||||
|
u := uint(num)
|
||||||
|
if _, ok := seen[u]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[u] = struct{}{}
|
||||||
|
result = append(result, u)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
func parseIDs(raw string) ([]uint, error) {
|
func parseIDs(raw string) ([]uint, error) {
|
||||||
parts := strings.Split(raw, ",")
|
parts := strings.Split(raw, ",")
|
||||||
result := make([]uint, 0, len(parts))
|
result := make([]uint, 0, len(parts))
|
||||||
|
|||||||
@@ -18,6 +18,12 @@ type Update struct {
|
|||||||
DeletedDocumentIDs *string `form:"deleted_document_ids" json:"deleted_document_ids"`
|
DeletedDocumentIDs *string `form:"deleted_document_ids" json:"deleted_document_ids"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BulkStatusUpdate struct {
|
||||||
|
IDs string `json:"ids" validate:"required_strict"`
|
||||||
|
Status string `json:"status" validate:"required,oneof=APPROVED REJECTED"`
|
||||||
|
RejectReason *string `json:"reject_reason"`
|
||||||
|
}
|
||||||
|
|
||||||
type Query struct {
|
type Query struct {
|
||||||
Page int `query:"page" validate:"omitempty,number,min=1,gt=0"`
|
Page int `query:"page" validate:"omitempty,number,min=1,gt=0"`
|
||||||
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100,gt=0"`
|
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100,gt=0"`
|
||||||
|
|||||||
@@ -2366,8 +2366,8 @@ func (s *repportService) GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangRes
|
|||||||
}
|
}
|
||||||
if hppCost != nil {
|
if hppCost != nil {
|
||||||
eggPiecesFloatRemaining = hppCost.Estimation.Butir - hppCost.Real.Butir
|
eggPiecesFloatRemaining = hppCost.Estimation.Butir - hppCost.Real.Butir
|
||||||
// eggHpp = hppCost.Estimation.HargaKg
|
eggHpp = hppCost.Estimation.HargaKg
|
||||||
eggHpp = hppCost.Real.HargaKg
|
// eggHpp = hppCost.Real.HargaKg
|
||||||
eggTotalPiecesFloat = hppCost.Estimation.Butir
|
eggTotalPiecesFloat = hppCost.Estimation.Butir
|
||||||
eggWeightFloat = hppCost.Estimation.Kg
|
eggWeightFloat = hppCost.Estimation.Kg
|
||||||
if eggTotalPiecesFloat > 0 {
|
if eggTotalPiecesFloat > 0 {
|
||||||
|
|||||||
Reference in New Issue
Block a user