mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
add api upload documents daily checklist
This commit is contained in:
@@ -7,12 +7,13 @@ import (
|
||||
)
|
||||
|
||||
type Phases struct {
|
||||
Id uint `gorm:"primaryKey"`
|
||||
Name string `gorm:"not null"`
|
||||
IsActive bool `gorm:"not null;default:true"`
|
||||
Category string `gorm:"type:category_code;not null"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
Id uint `gorm:"primaryKey"`
|
||||
Name string `gorm:"not null"`
|
||||
IsActive bool `gorm:"not null;default:true"`
|
||||
Category string `gorm:"type:category_code;not null"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
ActivityCount int `gorm:"-" json:"-"`
|
||||
|
||||
Activities []PhaseActivity `gorm:"foreignKey:PhaseId;references:Id"`
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@ func (u *DailyChecklistController) GetAll(c *fiber.Ctx) error {
|
||||
Name: name,
|
||||
Status: status,
|
||||
Category: item.Category,
|
||||
RejectReason: item.RejectReason,
|
||||
Date: item.Date,
|
||||
Kandang: kandang,
|
||||
CreatedUser: nil,
|
||||
@@ -303,12 +304,22 @@ func (u *DailyChecklistController) GetOne(c *fiber.Ctx) error {
|
||||
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).
|
||||
JSON(response.Success{
|
||||
Code: fiber.StatusOK,
|
||||
Status: "success",
|
||||
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 +353,12 @@ func (u *DailyChecklistController) UpdateOne(c *fiber.Ctx) error {
|
||||
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 {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ type DailyChecklistListDTO struct {
|
||||
TotalPhase int `json:"total_phase"`
|
||||
TotalActivity int `json:"total_activity"`
|
||||
Progress int `json:"progress"`
|
||||
RejectReason *string `json:"reject_reason"`
|
||||
}
|
||||
|
||||
type DailyChecklistDetailDTO struct {
|
||||
@@ -40,6 +41,14 @@ type DailyChecklistDetailDTO struct {
|
||||
AssignedEmployees []employeeDTO.EmployeesRelationDTO `json:"assigned_employees"`
|
||||
TotalActivity int `json:"total_activity"`
|
||||
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 {
|
||||
@@ -165,10 +174,11 @@ func ToDailyChecklistListDTO(e entity.DailyChecklist) DailyChecklistListDTO {
|
||||
TotalPhase: 0,
|
||||
TotalActivity: 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))
|
||||
for _, phase := range phases {
|
||||
phaseDTOs = append(phaseDTOs, DailyChecklistPhaseDTO{
|
||||
@@ -228,5 +238,6 @@ func ToDailyChecklistDetailDTO(checklist entity.DailyChecklist, phases []entity.
|
||||
AssignedEmployees: assignedDTOs,
|
||||
TotalActivity: totalActivities,
|
||||
Progress: progress,
|
||||
DocumentURLs: documentURLs,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package dailyChecklists
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"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"
|
||||
sDailyChecklist "gitlab.com/mbugroup/lti-api.git/internal/modules/daily-checklists/services"
|
||||
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)
|
||||
phasesRepo := rPhases.NewPhasesRepository(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)
|
||||
|
||||
DailyChecklistRoutes(router, userService, dailyChecklistService)
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
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"
|
||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/daily-checklists/validations"
|
||||
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/gofiber/fiber/v2"
|
||||
"github.com/sirupsen/logrus"
|
||||
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
@@ -39,10 +41,18 @@ type DailyChecklistService interface {
|
||||
}
|
||||
|
||||
type dailyChecklistService struct {
|
||||
Log *logrus.Logger
|
||||
Validate *validator.Validate
|
||||
Repository repository.DailyChecklistRepository
|
||||
PhaseRepo phaseRepo.PhasesRepository
|
||||
Log *logrus.Logger
|
||||
Validate *validator.Validate
|
||||
Repository repository.DailyChecklistRepository
|
||||
PhaseRepo phaseRepo.PhasesRepository
|
||||
DocumentSvc commonSvc.DocumentService
|
||||
}
|
||||
|
||||
type DailyChecklistDocument struct {
|
||||
ID uint
|
||||
Name string
|
||||
Size float64
|
||||
URL string
|
||||
}
|
||||
|
||||
type DailyChecklistDetail struct {
|
||||
@@ -52,6 +62,7 @@ type DailyChecklistDetail struct {
|
||||
AssignedEmployees []entity.Employee
|
||||
TotalActivities int
|
||||
Progress float64
|
||||
DocumentURLs []DailyChecklistDocument
|
||||
}
|
||||
|
||||
type DailyChecklistListItem struct {
|
||||
@@ -60,6 +71,7 @@ type DailyChecklistListItem struct {
|
||||
Date time.Time
|
||||
Category string
|
||||
Status *string
|
||||
RejectReason *string
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
Kandang entity.Kandang
|
||||
@@ -108,12 +120,13 @@ type DailyChecklistReportCategory struct {
|
||||
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{
|
||||
Log: utils.Log,
|
||||
Validate: validate,
|
||||
Repository: repo,
|
||||
PhaseRepo: phaseRepo,
|
||||
Log: utils.Log,
|
||||
Validate: validate,
|
||||
Repository: repo,
|
||||
PhaseRepo: phaseRepo,
|
||||
DocumentSvc: documentSvc,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +171,7 @@ func (s dailyChecklistService) GetAll(c *fiber.Ctx, params *validation.Query) ([
|
||||
|
||||
if 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{})
|
||||
@@ -174,6 +187,7 @@ func (s dailyChecklistService) GetAll(c *fiber.Ctx, params *validation.Query) ([
|
||||
Date time.Time
|
||||
Category string
|
||||
Status *string
|
||||
RejectReason *string
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
KandangID uint
|
||||
@@ -192,6 +206,7 @@ func (s dailyChecklistService) GetAll(c *fiber.Ctx, params *validation.Query) ([
|
||||
dc.date,
|
||||
dc.category,
|
||||
dc.status,
|
||||
dc.reject_reason,
|
||||
dc.created_at,
|
||||
dc.updated_at,
|
||||
dc.kandang_id,
|
||||
@@ -265,6 +280,7 @@ func (s dailyChecklistService) GetAll(c *fiber.Ctx, params *validation.Query) ([
|
||||
Date: row.Date,
|
||||
Category: row.Category,
|
||||
Status: row.Status,
|
||||
RejectReason: row.RejectReason,
|
||||
CreatedAt: row.CreatedAt,
|
||||
UpdatedAt: row.UpdatedAt,
|
||||
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)
|
||||
}
|
||||
|
||||
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{
|
||||
Checklist: *checklist,
|
||||
Phases: phases,
|
||||
@@ -352,6 +391,7 @@ func (s dailyChecklistService) GetDetail(c *fiber.Ctx, id uint) (*DailyChecklist
|
||||
AssignedEmployees: assignedEmployees,
|
||||
TotalActivities: totalActivities,
|
||||
Progress: progress,
|
||||
DocumentURLs: documentURLs,
|
||||
}, 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{
|
||||
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
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
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{
|
||||
"status": req.Status,
|
||||
}
|
||||
@@ -400,6 +456,40 @@ func (s dailyChecklistService) UpdateOne(c *fiber.Ctx, req *validation.Update, i
|
||||
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 errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
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 phases p ON p.id = dcat.phase_id").
|
||||
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 {
|
||||
db = db.Where("a.id = ?", *params.AreaID)
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package validation
|
||||
|
||||
import (
|
||||
"mime/multipart"
|
||||
)
|
||||
|
||||
type Create struct {
|
||||
Date string `json:"date" validate:"required"`
|
||||
KandangId uint `json:"kandang_id" validate:"required"`
|
||||
@@ -8,8 +12,10 @@ type Create struct {
|
||||
}
|
||||
|
||||
type Update struct {
|
||||
Status string `json:"status" validate:"required"`
|
||||
RejectReason *string `json:"reject_reason"`
|
||||
Status string `form:"status" json:"status" validate:"required"`
|
||||
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 {
|
||||
|
||||
@@ -15,12 +15,13 @@ type PhasesRelationDTO struct {
|
||||
}
|
||||
|
||||
type PhasesListDTO struct {
|
||||
Id uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Category string `json:"category"`
|
||||
IsActive bool `json:"is_active"`
|
||||
CreatedUser *userDTO.UserRelationDTO `json:"created_user"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Id uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Category string `json:"category"`
|
||||
IsActive bool `json:"is_active"`
|
||||
ActivityCount int `json:"activity_count"`
|
||||
CreatedUser *userDTO.UserRelationDTO `json:"created_user"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
type PhasesDetailDTO struct {
|
||||
@@ -44,12 +45,13 @@ func ToPhasesListDTO(e entity.Phases) PhasesListDTO {
|
||||
// }
|
||||
|
||||
return PhasesListDTO{
|
||||
Id: e.Id,
|
||||
Name: e.Name,
|
||||
Category: e.Category,
|
||||
IsActive: e.IsActive,
|
||||
CreatedAt: e.CreatedAt,
|
||||
CreatedUser: createdUser,
|
||||
Id: e.Id,
|
||||
Name: e.Name,
|
||||
Category: e.Category,
|
||||
IsActive: e.IsActive,
|
||||
ActivityCount: e.ActivityCount,
|
||||
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)
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -432,6 +432,8 @@ const (
|
||||
DocumentableTypeExpense DocumentableType = "EXPENSE"
|
||||
DocumentableTypeExpenseRealization DocumentableType = "EXPENSE_REALIZATION"
|
||||
DocumentableTypePurchaseItem DocumentableType = "PURCHASE_ITEM"
|
||||
|
||||
DocumentTypeDailyChecklist DocumentType = "DAILY_CHECKLIST_DOCUMENT"
|
||||
)
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user