mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 05:21:57 +00:00
FIX[BE]: period without autoincrement
This commit is contained in:
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS project_flocks_flock_period_unique;
|
||||
@@ -0,0 +1,3 @@
|
||||
CREATE UNIQUE INDEX project_flocks_flock_period_unique
|
||||
ON project_flocks (flock_id, period)
|
||||
WHERE deleted_at IS NULL;
|
||||
@@ -8,12 +8,12 @@ import (
|
||||
|
||||
type ProjectFlock struct {
|
||||
Id uint `gorm:"primaryKey"`
|
||||
FlockId uint `gorm:"not null"`
|
||||
FlockId uint `gorm:"not null;uniqueIndex:idx_project_flocks_flock_period,priority:1"`
|
||||
AreaId uint `gorm:"not null"`
|
||||
ProductCategoryId uint `gorm:"not null"`
|
||||
FcrId uint `gorm:"not null"`
|
||||
LocationId uint `gorm:"not null"`
|
||||
Period int `gorm:"not null"`
|
||||
Period int `gorm:"not null;uniqueIndex:idx_project_flocks_flock_period,priority:2"`
|
||||
CreatedBy uint `gorm:"not null"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Recording struct {
|
||||
Id uint `gorm:"primaryKey"`
|
||||
Name string `gorm:"not null;uniqueIndex:idx_name,where:deleted_at IS NULL"`
|
||||
CreatedBy uint `gorm:"not null"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
|
||||
CreatedUser User `gorm:"foreignKey:CreatedBy;references:Id"`
|
||||
}
|
||||
@@ -57,7 +57,6 @@ func (r *ProjectflockRepositoryImpl) GetMaxPeriodByFlock(ctx context.Context, fl
|
||||
var max int
|
||||
if err := r.DB().WithContext(ctx).
|
||||
Model(&entity.ProjectFlock{}).
|
||||
Unscoped().
|
||||
Where("flock_id = ?", flockID).
|
||||
Select("COALESCE(MAX(period), 0)").
|
||||
Scan(&max).Error; err != nil {
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
type ProjectflockService interface {
|
||||
@@ -30,12 +31,12 @@ type projectflockService struct {
|
||||
Log *logrus.Logger
|
||||
Validate *validator.Validate
|
||||
Repository repository.ProjectflockRepository
|
||||
FlockRepo flockRepository.FlockRepository
|
||||
FlockRepo flockRepository.FlockRepository
|
||||
KandangRepo kandangRepository.KandangRepository
|
||||
}
|
||||
|
||||
type FlockPeriodSummary struct {
|
||||
Flock entity.Flock
|
||||
Flock entity.Flock
|
||||
NextPeriod int
|
||||
}
|
||||
|
||||
@@ -49,7 +50,7 @@ func NewProjectflockService(
|
||||
Log: utils.Log,
|
||||
Validate: validate,
|
||||
Repository: repo,
|
||||
FlockRepo: flockRepo,
|
||||
FlockRepo: flockRepo,
|
||||
KandangRepo: kandangRepo,
|
||||
}
|
||||
}
|
||||
@@ -127,19 +128,33 @@ func (s *projectflockService) CreateOne(c *fiber.Ctx, req *validation.Create) (*
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to start transaction")
|
||||
}
|
||||
|
||||
var nextPeriod int
|
||||
periodQuery := tx.Model(&entity.ProjectFlock{}).
|
||||
Where("flock_id = ?", req.FlockId).
|
||||
Clauses(clause.Locking{Strength: "UPDATE"})
|
||||
if err := periodQuery.Select("COALESCE(MAX(period), 0)").Scan(&nextPeriod).Error; err != nil {
|
||||
tx.Rollback()
|
||||
s.Log.Errorf("Failed to determine next period for flock %d: %+v", req.FlockId, err)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to determine next period")
|
||||
}
|
||||
nextPeriod++
|
||||
|
||||
projectRepo := s.Repository.WithTx(tx)
|
||||
createBody := &entity.ProjectFlock{
|
||||
FlockId: req.FlockId,
|
||||
FlockId: req.FlockId,
|
||||
AreaId: req.AreaId,
|
||||
ProductCategoryId: req.ProductCategoryId,
|
||||
FcrId: req.FcrId,
|
||||
LocationId: req.LocationId,
|
||||
Period: req.Period,
|
||||
Period: nextPeriod,
|
||||
CreatedBy: 1,
|
||||
}
|
||||
|
||||
if err := projectRepo.CreateOne(c.Context(), createBody, nil); err != nil {
|
||||
tx.Rollback()
|
||||
if errors.Is(err, gorm.ErrDuplicatedKey) {
|
||||
return nil, fiber.NewError(fiber.StatusConflict, "Project flock period already exists")
|
||||
}
|
||||
s.Log.Errorf("Failed to create projectflock: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
@@ -353,7 +368,7 @@ func (s projectflockService) GetFlockPeriodSummary(c *fiber.Ctx, flockID uint) (
|
||||
}
|
||||
|
||||
return &FlockPeriodSummary{
|
||||
Flock: *flock,
|
||||
Flock: *flock,
|
||||
NextPeriod: maxPeriod + 1,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
package validation
|
||||
|
||||
type Create struct {
|
||||
FlockId uint `json:"flock_id" validate:"required_strict,number,gt=0"`
|
||||
FlockId uint `json:"flock_id" validate:"required_strict,number,gt=0"`
|
||||
AreaId uint `json:"area_id" validate:"required_strict,number,gt=0"`
|
||||
ProductCategoryId uint `json:"product_category_id" validate:"required_strict,number,gt=0"`
|
||||
FcrId uint `json:"fcr_id" validate:"required_strict,number,gt=0"`
|
||||
LocationId uint `json:"location_id" validate:"required_strict,number,gt=0"`
|
||||
Period int `json:"period" validate:"required_strict,number,gt=0"`
|
||||
KandangIds []uint `json:"kandang_ids" validate:"required,min=1,dive,gt=0"`
|
||||
}
|
||||
|
||||
type Update struct {
|
||||
FlockId *uint `json:"flock_id,omitempty" validate:"omitempty,number,gt=0"`
|
||||
FlockId *uint `json:"flock_id,omitempty" validate:"omitempty,number,gt=0"`
|
||||
AreaId *uint `json:"area_id,omitempty" validate:"omitempty,number,gt=0"`
|
||||
ProductCategoryId *uint `json:"product_category_id,omitempty" validate:"omitempty,number,gt=0"`
|
||||
FcrId *uint `json:"fcr_id,omitempty" validate:"omitempty,number,gt=0"`
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/dto"
|
||||
service "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/services"
|
||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/validations"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/response"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type RecordingController struct {
|
||||
RecordingService service.RecordingService
|
||||
}
|
||||
|
||||
func NewRecordingController(recordingService service.RecordingService) *RecordingController {
|
||||
return &RecordingController{
|
||||
RecordingService: recordingService,
|
||||
}
|
||||
}
|
||||
|
||||
func (u *RecordingController) GetAll(c *fiber.Ctx) error {
|
||||
query := &validation.Query{
|
||||
Page: c.QueryInt("page", 1),
|
||||
Limit: c.QueryInt("limit", 10),
|
||||
Search: c.Query("search", ""),
|
||||
}
|
||||
|
||||
result, totalResults, err := u.RecordingService.GetAll(c, query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).
|
||||
JSON(response.SuccessWithPaginate[dto.RecordingListDTO]{
|
||||
Code: fiber.StatusOK,
|
||||
Status: "success",
|
||||
Message: "Get all recordings successfully",
|
||||
Meta: response.Meta{
|
||||
Page: query.Page,
|
||||
Limit: query.Limit,
|
||||
TotalPages: int64(math.Ceil(float64(totalResults) / float64(query.Limit))),
|
||||
TotalResults: totalResults,
|
||||
},
|
||||
Data: dto.ToRecordingListDTOs(result),
|
||||
})
|
||||
}
|
||||
|
||||
func (u *RecordingController) GetOne(c *fiber.Ctx) error {
|
||||
param := c.Params("id")
|
||||
|
||||
id, err := strconv.Atoi(param)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
|
||||
}
|
||||
|
||||
result, err := u.RecordingService.GetOne(c, uint(id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).
|
||||
JSON(response.Success{
|
||||
Code: fiber.StatusOK,
|
||||
Status: "success",
|
||||
Message: "Get recording successfully",
|
||||
Data: dto.ToRecordingListDTO(*result),
|
||||
})
|
||||
}
|
||||
|
||||
func (u *RecordingController) CreateOne(c *fiber.Ctx) error {
|
||||
req := new(validation.Create)
|
||||
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
|
||||
}
|
||||
|
||||
result, err := u.RecordingService.CreateOne(c, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusCreated).
|
||||
JSON(response.Success{
|
||||
Code: fiber.StatusCreated,
|
||||
Status: "success",
|
||||
Message: "Create recording successfully",
|
||||
Data: dto.ToRecordingListDTO(*result),
|
||||
})
|
||||
}
|
||||
|
||||
func (u *RecordingController) UpdateOne(c *fiber.Ctx) error {
|
||||
req := new(validation.Update)
|
||||
param := c.Params("id")
|
||||
|
||||
id, err := strconv.Atoi(param)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
|
||||
}
|
||||
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
|
||||
}
|
||||
|
||||
result, err := u.RecordingService.UpdateOne(c, req, uint(id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).
|
||||
JSON(response.Success{
|
||||
Code: fiber.StatusOK,
|
||||
Status: "success",
|
||||
Message: "Update recording successfully",
|
||||
Data: dto.ToRecordingListDTO(*result),
|
||||
})
|
||||
}
|
||||
|
||||
func (u *RecordingController) DeleteOne(c *fiber.Ctx) error {
|
||||
param := c.Params("id")
|
||||
|
||||
id, err := strconv.Atoi(param)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
|
||||
}
|
||||
|
||||
if err := u.RecordingService.DeleteOne(c, uint(id)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).
|
||||
JSON(response.Common{
|
||||
Code: fiber.StatusOK,
|
||||
Status: "success",
|
||||
Message: "Delete recording successfully",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto"
|
||||
)
|
||||
|
||||
// === DTO Structs ===
|
||||
|
||||
type RecordingBaseDTO struct {
|
||||
Id uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type RecordingListDTO struct {
|
||||
RecordingBaseDTO
|
||||
CreatedUser *userDTO.UserBaseDTO `json:"created_user"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
type RecordingDetailDTO struct {
|
||||
RecordingListDTO
|
||||
}
|
||||
|
||||
// === Mapper Functions ===
|
||||
|
||||
func ToRecordingBaseDTO(e entity.Recording) RecordingBaseDTO {
|
||||
return RecordingBaseDTO{
|
||||
Id: e.Id,
|
||||
Name: e.Name,
|
||||
}
|
||||
}
|
||||
|
||||
func ToRecordingListDTO(e entity.Recording) RecordingListDTO {
|
||||
var createdUser *userDTO.UserBaseDTO
|
||||
if e.CreatedUser.Id != 0 {
|
||||
mapped := userDTO.ToUserBaseDTO(e.CreatedUser)
|
||||
createdUser = &mapped
|
||||
}
|
||||
|
||||
return RecordingListDTO{
|
||||
RecordingBaseDTO: ToRecordingBaseDTO(e),
|
||||
CreatedAt: e.CreatedAt,
|
||||
UpdatedAt: e.UpdatedAt,
|
||||
CreatedUser: createdUser,
|
||||
}
|
||||
}
|
||||
|
||||
func ToRecordingListDTOs(e []entity.Recording) []RecordingListDTO {
|
||||
result := make([]RecordingListDTO, len(e))
|
||||
for i, r := range e {
|
||||
result[i] = ToRecordingListDTO(r)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func ToRecordingDetailDTO(e entity.Recording) RecordingDetailDTO {
|
||||
return RecordingDetailDTO{
|
||||
RecordingListDTO: ToRecordingListDTO(e),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package recordings
|
||||
|
||||
import (
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"gorm.io/gorm"
|
||||
|
||||
rRecording "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories"
|
||||
sRecording "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/services"
|
||||
|
||||
rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories"
|
||||
sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||
)
|
||||
|
||||
type RecordingModule struct{}
|
||||
|
||||
func (RecordingModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *validator.Validate) {
|
||||
recordingRepo := rRecording.NewRecordingRepository(db)
|
||||
userRepo := rUser.NewUserRepository(db)
|
||||
|
||||
recordingService := sRecording.NewRecordingService(recordingRepo, validate)
|
||||
userService := sUser.NewUserService(userRepo, validate)
|
||||
|
||||
RecordingRoutes(router, userService, recordingService)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type RecordingRepository interface {
|
||||
repository.BaseRepository[entity.Recording]
|
||||
}
|
||||
|
||||
type RecordingRepositoryImpl struct {
|
||||
*repository.BaseRepositoryImpl[entity.Recording]
|
||||
}
|
||||
|
||||
func NewRecordingRepository(db *gorm.DB) RecordingRepository {
|
||||
return &RecordingRepositoryImpl{
|
||||
BaseRepositoryImpl: repository.NewBaseRepository[entity.Recording](db),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package recordings
|
||||
|
||||
import (
|
||||
// m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/controllers"
|
||||
recording "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/services"
|
||||
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func RecordingRoutes(v1 fiber.Router, u user.UserService, s recording.RecordingService) {
|
||||
ctrl := controller.NewRecordingController(s)
|
||||
|
||||
route := v1.Group("/recordings")
|
||||
|
||||
// route.Get("/", m.Auth(u), ctrl.GetAll)
|
||||
// route.Post("/", m.Auth(u), ctrl.CreateOne)
|
||||
// route.Get("/:id", m.Auth(u), ctrl.GetOne)
|
||||
// route.Patch("/:id", m.Auth(u), ctrl.UpdateOne)
|
||||
// route.Delete("/:id", m.Auth(u), ctrl.DeleteOne)
|
||||
|
||||
route.Get("/", ctrl.GetAll)
|
||||
route.Post("/", ctrl.CreateOne)
|
||||
route.Get("/:id", ctrl.GetOne)
|
||||
route.Patch("/:id", ctrl.UpdateOne)
|
||||
route.Delete("/:id", ctrl.DeleteOne)
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
repository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories"
|
||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/validations"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type RecordingService interface {
|
||||
GetAll(ctx *fiber.Ctx, params *validation.Query) ([]entity.Recording, int64, error)
|
||||
GetOne(ctx *fiber.Ctx, id uint) (*entity.Recording, error)
|
||||
CreateOne(ctx *fiber.Ctx, req *validation.Create) (*entity.Recording, error)
|
||||
UpdateOne(ctx *fiber.Ctx, req *validation.Update, id uint) (*entity.Recording, error)
|
||||
DeleteOne(ctx *fiber.Ctx, id uint) error
|
||||
}
|
||||
|
||||
type recordingService struct {
|
||||
Log *logrus.Logger
|
||||
Validate *validator.Validate
|
||||
Repository repository.RecordingRepository
|
||||
}
|
||||
|
||||
func NewRecordingService(repo repository.RecordingRepository, validate *validator.Validate) RecordingService {
|
||||
return &recordingService{
|
||||
Log: utils.Log,
|
||||
Validate: validate,
|
||||
Repository: repo,
|
||||
}
|
||||
}
|
||||
|
||||
func (s recordingService) withRelations(db *gorm.DB) *gorm.DB {
|
||||
return db.Preload("CreatedUser")
|
||||
}
|
||||
|
||||
func (s recordingService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.Recording, int64, error) {
|
||||
if err := s.Validate.Struct(params); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
offset := (params.Page - 1) * params.Limit
|
||||
|
||||
recordings, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
|
||||
db = s.withRelations(db)
|
||||
if params.Search != "" {
|
||||
return db.Where("name LIKE ?", "%"+params.Search+"%")
|
||||
}
|
||||
return db.Order("created_at DESC").Order("updated_at DESC")
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to get recordings: %+v", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
return recordings, total, nil
|
||||
}
|
||||
|
||||
func (s recordingService) GetOne(c *fiber.Ctx, id uint) (*entity.Recording, error) {
|
||||
recording, err := s.Repository.GetByID(c.Context(), id, s.withRelations)
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Recording not found")
|
||||
}
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed get recording by id: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
return recording, nil
|
||||
}
|
||||
|
||||
func (s *recordingService) CreateOne(c *fiber.Ctx, req *validation.Create) (*entity.Recording, error) {
|
||||
if err := s.Validate.Struct(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
createBody := &entity.Recording{
|
||||
Name: req.Name,
|
||||
}
|
||||
|
||||
if err := s.Repository.CreateOne(c.Context(), createBody, nil); err != nil {
|
||||
s.Log.Errorf("Failed to create recording: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.GetOne(c, createBody.Id)
|
||||
}
|
||||
|
||||
func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint) (*entity.Recording, error) {
|
||||
if err := s.Validate.Struct(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updateBody := make(map[string]any)
|
||||
|
||||
if req.Name != nil {
|
||||
updateBody["name"] = *req.Name
|
||||
}
|
||||
|
||||
if len(updateBody) == 0 {
|
||||
return s.GetOne(c, id)
|
||||
}
|
||||
|
||||
if err := s.Repository.PatchOne(c.Context(), id, updateBody, nil); err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Recording not found")
|
||||
}
|
||||
s.Log.Errorf("Failed to update recording: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.GetOne(c, id)
|
||||
}
|
||||
|
||||
func (s recordingService) DeleteOne(c *fiber.Ctx, id uint) error {
|
||||
if err := s.Repository.DeleteOne(c.Context(), id); err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return fiber.NewError(fiber.StatusNotFound, "Recording not found")
|
||||
}
|
||||
s.Log.Errorf("Failed to delete recording: %+v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package validation
|
||||
|
||||
type Create struct {
|
||||
Name string `json:"name" validate:"required_strict,min=3"`
|
||||
}
|
||||
|
||||
type Update struct {
|
||||
Name *string `json:"name,omitempty" validate:"omitempty"`
|
||||
}
|
||||
|
||||
type Query struct {
|
||||
Page int `query:"page" validate:"omitempty,number,min=1"`
|
||||
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"`
|
||||
Search string `query:"search" validate:"omitempty,max=50"`
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"gorm.io/gorm"
|
||||
|
||||
projectflocks "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks"
|
||||
recordings "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings"
|
||||
// MODULE IMPORTS
|
||||
)
|
||||
|
||||
@@ -16,6 +17,7 @@ func RegisterRoutes(router fiber.Router, db *gorm.DB, validate *validator.Valida
|
||||
|
||||
allModules := []modules.Module{
|
||||
projectflocks.ProjectflockModule{},
|
||||
recordings.RecordingModule{},
|
||||
// MODULE REGISTRY
|
||||
}
|
||||
|
||||
|
||||
@@ -22,12 +22,11 @@ func TestProjectFlockSummary(t *testing.T) {
|
||||
kandangID := createKandang(t, app, "Kandang Summary", locationID, 1)
|
||||
|
||||
createPayload := map[string]any{
|
||||
"flock_id": flockID,
|
||||
"flock_id": flockID,
|
||||
"area_id": areaID,
|
||||
"product_category_id": categoryID,
|
||||
"fcr_id": fcrID,
|
||||
"location_id": locationID,
|
||||
"period": 1,
|
||||
"kandang_ids": []uint{kandangID},
|
||||
}
|
||||
resp, body := doJSONRequest(t, app, http.MethodPost, "/api/production/project_flocks", createPayload)
|
||||
@@ -37,14 +36,9 @@ func TestProjectFlockSummary(t *testing.T) {
|
||||
|
||||
var createResp struct {
|
||||
Data struct {
|
||||
Id uint `json:"id"`
|
||||
FlockId uint `json:"flock_id"`
|
||||
AreaId uint `json:"area_id"`
|
||||
ProductCategoryId uint `json:"product_category_id"`
|
||||
FcrId uint `json:"fcr_id"`
|
||||
LocationId uint `json:"location_id"`
|
||||
Period int `json:"period"`
|
||||
Flock struct {
|
||||
Id uint `json:"id"`
|
||||
Period int `json:"period"`
|
||||
Flock struct {
|
||||
Id uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
} `json:"flock"`
|
||||
@@ -82,18 +76,47 @@ func TestProjectFlockSummary(t *testing.T) {
|
||||
if err := json.Unmarshal(body, &createResp); err != nil {
|
||||
t.Fatalf("failed to parse create response: %v", err)
|
||||
}
|
||||
if createResp.Data.FlockId != flockID || createResp.Data.Flock.Name == "" {
|
||||
if createResp.Data.Flock.Id != flockID || createResp.Data.Flock.Name == "" {
|
||||
t.Fatalf("expected flock detail to be present, got %+v", createResp.Data.Flock)
|
||||
}
|
||||
if createResp.Data.AreaId != areaID || createResp.Data.Area.Name == "" {
|
||||
if createResp.Data.Area.Id != areaID || createResp.Data.Area.Name == "" {
|
||||
t.Fatalf("expected area detail to be present, got %+v", createResp.Data.Area)
|
||||
}
|
||||
if createResp.Data.LocationId != locationID || createResp.Data.Location.Name == "" {
|
||||
if createResp.Data.Location.Id != locationID || createResp.Data.Location.Name == "" {
|
||||
t.Fatalf("expected location detail to be present, got %+v", createResp.Data.Location)
|
||||
}
|
||||
if len(createResp.Data.Kandangs) != 1 || createResp.Data.Kandangs[0].Id != kandangID {
|
||||
t.Fatalf("expected kandang detail to be present, got %+v", createResp.Data.Kandangs)
|
||||
}
|
||||
if createResp.Data.Period != 1 {
|
||||
t.Fatalf("expected period 1 to be assigned automatically, got %d", createResp.Data.Period)
|
||||
}
|
||||
|
||||
secondKandangID := createKandang(t, app, "Kandang Summary 2", locationID, 1)
|
||||
secondPayload := map[string]any{
|
||||
"flock_id": flockID,
|
||||
"area_id": areaID,
|
||||
"product_category_id": categoryID,
|
||||
"fcr_id": fcrID,
|
||||
"location_id": locationID,
|
||||
"kandang_ids": []uint{secondKandangID},
|
||||
}
|
||||
resp, body = doJSONRequest(t, app, http.MethodPost, "/api/production/project_flocks", secondPayload)
|
||||
if resp.StatusCode != fiber.StatusCreated {
|
||||
t.Fatalf("expected 201 when creating second project flock, got %d: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
var createRespSecond struct {
|
||||
Data struct {
|
||||
Id uint `json:"id"`
|
||||
Period int `json:"period"`
|
||||
} `json:"data"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &createRespSecond); err != nil {
|
||||
t.Fatalf("failed to parse second create response: %v", err)
|
||||
}
|
||||
if createRespSecond.Data.Period != 2 {
|
||||
t.Fatalf("expected second period to be 2, got %d", createRespSecond.Data.Period)
|
||||
}
|
||||
|
||||
resp, body = doJSONRequest(t, app, http.MethodGet, "/api/production/project_flocks/flocks/"+uintToString(flockID)+"/periods", nil)
|
||||
if resp.StatusCode != fiber.StatusOK {
|
||||
@@ -109,8 +132,31 @@ func TestProjectFlockSummary(t *testing.T) {
|
||||
t.Fatalf("failed to parse summary response: %v", err)
|
||||
}
|
||||
|
||||
if summary.Data.NextPeriod != 2 {
|
||||
t.Fatalf("expected next_period 2, got %d", summary.Data.NextPeriod)
|
||||
if summary.Data.NextPeriod != 3 {
|
||||
t.Fatalf("expected next_period 3, got %d", summary.Data.NextPeriod)
|
||||
}
|
||||
|
||||
resp, body = doJSONRequest(t, app, http.MethodDelete, "/api/production/project_flocks/"+uintToString(createResp.Data.Id), nil)
|
||||
if resp.StatusCode != fiber.StatusOK {
|
||||
t.Fatalf("expected 200 when deleting first project flock, got %d: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
resp, body = doJSONRequest(t, app, http.MethodDelete, "/api/production/project_flocks/"+uintToString(createRespSecond.Data.Id), nil)
|
||||
if resp.StatusCode != fiber.StatusOK {
|
||||
t.Fatalf("expected 200 when deleting second project flock, got %d: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
resp, body = doJSONRequest(t, app, http.MethodGet, "/api/production/project_flocks/flocks/"+uintToString(flockID)+"/periods", nil)
|
||||
if resp.StatusCode != fiber.StatusOK {
|
||||
t.Fatalf("expected 200 when fetching summary after delete, got %d: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(body, &summary); err != nil {
|
||||
t.Fatalf("failed to parse summary response after delete: %v", err)
|
||||
}
|
||||
|
||||
if summary.Data.NextPeriod != 1 {
|
||||
t.Fatalf("expected next_period 1 after soft deletes, got %d", summary.Data.NextPeriod)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user