Feat(BE-127): create migration for transfer to laying and inisiate module

This commit is contained in:
aguhh18
2025-10-30 09:06:21 +07:00
parent a390d1d23a
commit 31bb28f7da
13 changed files with 532 additions and 1 deletions
@@ -0,0 +1 @@
DROP TABLE IF EXISTS laying_transfers;
@@ -0,0 +1,42 @@
CREATE TABLE IF NOT EXISTS laying_transfers (
id BIGSERIAL PRIMARY KEY,
from_project_flock_id BIGINT NOT NULL,
to_project_flock_id BIGINT NOT NULL,
total_quantity INTEGER,
notes TEXT,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now(),
deleted_at TIMESTAMPTZ,
created_by BIGINT
);
-- FOREIGN KEYS (dijalankan setelah semua tabel parent ada)
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'project_flocks') THEN
ALTER TABLE laying_transfers
ADD CONSTRAINT fk_laying_from_project_flock
FOREIGN KEY (from_project_flock_id)
REFERENCES project_flocks(id)
ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE laying_transfers
ADD CONSTRAINT fk_laying_to_project_flock
FOREIGN KEY (to_project_flock_id)
REFERENCES project_flocks(id)
ON DELETE RESTRICT ON UPDATE CASCADE;
END IF;
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'users') THEN
ALTER TABLE laying_transfers
ADD CONSTRAINT fk_laying_created_by
FOREIGN KEY (created_by)
REFERENCES users(id)
ON DELETE RESTRICT ON UPDATE CASCADE;
END IF;
END $$;
-- INDEXES
CREATE INDEX IF NOT EXISTS idx_laying_transfers_from_project_flock_id ON laying_transfers(from_project_flock_id);
CREATE INDEX IF NOT EXISTS idx_laying_transfers_to_project_flock_id ON laying_transfers(to_project_flock_id);
CREATE INDEX IF NOT EXISTS idx_laying_transfers_created_by ON laying_transfers(created_by);
CREATE INDEX IF NOT EXISTS idx_laying_transfers_deleted_at ON laying_transfers(deleted_at);
@@ -0,0 +1 @@
DROP TABLE IF EXISTS laying_kandang_transfers;
@@ -0,0 +1,40 @@
CREATE TABLE IF NOT EXISTS laying_kandang_transfers (
id BIGSERIAL PRIMARY KEY,
kandang_id BIGINT,
product_warehouse_id BIGINT,
qty NUMERIC(15,3),
laying_transfer_id BIGINT NOT NULL
);
-- FOREIGN KEYS (dijalankan setelah semua tabel parent ada)
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'kandangs') THEN
ALTER TABLE laying_kandang_transfers
ADD CONSTRAINT fk_laying_kandang_transfers_kandang
FOREIGN KEY (kandang_id)
REFERENCES kandangs(id)
ON DELETE RESTRICT ON UPDATE CASCADE;
END IF;
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'product_warehouses') THEN
ALTER TABLE laying_kandang_transfers
ADD CONSTRAINT fk_laying_kandang_transfers_product_warehouse
FOREIGN KEY (product_warehouse_id)
REFERENCES product_warehouses(id)
ON DELETE RESTRICT ON UPDATE CASCADE;
END IF;
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'laying_transfers') THEN
ALTER TABLE laying_kandang_transfers
ADD CONSTRAINT fk_laying_kandang_transfers_laying_transfer
FOREIGN KEY (laying_transfer_id)
REFERENCES laying_transfers(id)
ON DELETE RESTRICT ON UPDATE CASCADE;
END IF;
END $$;
-- INDEXES
CREATE INDEX IF NOT EXISTS idx_laying_kandang_transfers_kandang_id ON laying_kandang_transfers(kandang_id);
CREATE INDEX IF NOT EXISTS idx_laying_kandang_transfers_product_warehouse_id ON laying_kandang_transfers(product_warehouse_id);
CREATE INDEX IF NOT EXISTS idx_laying_kandang_transfers_laying_transfer_id ON laying_kandang_transfers(laying_transfer_id);
+18
View File
@@ -0,0 +1,18 @@
package entities
import (
"time"
"gorm.io/gorm"
)
type TransferLaying 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"`
}
+3 -1
View File
@@ -10,6 +10,7 @@ import (
chickins "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins"
projectflocks "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks"
recordings "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings"
transferLayings "gitlab.com/mbugroup/lti-api.git/internal/modules/production/transfer_layings"
// MODULE IMPORTS
)
@@ -20,8 +21,9 @@ func RegisterRoutes(router fiber.Router, db *gorm.DB, validate *validator.Valida
projectflocks.ProjectflockModule{},
recordings.RecordingModule{},
chickins.ChickinModule{},
transferLayings.TransferLayingModule{},
// MODULE REGISTRY
}
}
for _, m := range allModules {
m.RegisterRoutes(group, db, validate)
@@ -0,0 +1,144 @@
package controller
import (
"math"
"strconv"
"gitlab.com/mbugroup/lti-api.git/internal/modules/production/transfer_layings/dto"
service "gitlab.com/mbugroup/lti-api.git/internal/modules/production/transfer_layings/services"
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/transfer_layings/validations"
"gitlab.com/mbugroup/lti-api.git/internal/response"
"github.com/gofiber/fiber/v2"
)
type TransferLayingController struct {
TransferLayingService service.TransferLayingService
}
func NewTransferLayingController(transferLayingService service.TransferLayingService) *TransferLayingController {
return &TransferLayingController{
TransferLayingService: transferLayingService,
}
}
func (u *TransferLayingController) GetAll(c *fiber.Ctx) error {
query := &validation.Query{
Page: c.QueryInt("page", 1),
Limit: c.QueryInt("limit", 10),
Search: c.Query("search", ""),
}
if query.Page < 1 || query.Limit < 1 {
return fiber.NewError(fiber.StatusBadRequest, "page and limit must be greater than 0")
}
result, totalResults, err := u.TransferLayingService.GetAll(c, query)
if err != nil {
return err
}
return c.Status(fiber.StatusOK).
JSON(response.SuccessWithPaginate[dto.TransferLayingListDTO]{
Code: fiber.StatusOK,
Status: "success",
Message: "Get all transferLayings successfully",
Meta: response.Meta{
Page: query.Page,
Limit: query.Limit,
TotalPages: int64(math.Ceil(float64(totalResults) / float64(query.Limit))),
TotalResults: totalResults,
},
Data: dto.ToTransferLayingListDTOs(result),
})
}
func (u *TransferLayingController) 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.TransferLayingService.GetOne(c, uint(id))
if err != nil {
return err
}
return c.Status(fiber.StatusOK).
JSON(response.Success{
Code: fiber.StatusOK,
Status: "success",
Message: "Get transferLaying successfully",
Data: dto.ToTransferLayingListDTO(*result),
})
}
func (u *TransferLayingController) 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.TransferLayingService.CreateOne(c, req)
if err != nil {
return err
}
return c.Status(fiber.StatusCreated).
JSON(response.Success{
Code: fiber.StatusCreated,
Status: "success",
Message: "Create transferLaying successfully",
Data: dto.ToTransferLayingListDTO(*result),
})
}
func (u *TransferLayingController) 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.TransferLayingService.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 transferLaying successfully",
Data: dto.ToTransferLayingListDTO(*result),
})
}
func (u *TransferLayingController) 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.TransferLayingService.DeleteOne(c, uint(id)); err != nil {
return err
}
return c.Status(fiber.StatusOK).
JSON(response.Common{
Code: fiber.StatusOK,
Status: "success",
Message: "Delete transferLaying 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 TransferLayingBaseDTO struct {
Id uint `json:"id"`
Name string `json:"name"`
}
type TransferLayingListDTO struct {
TransferLayingBaseDTO
CreatedUser *userDTO.UserBaseDTO `json:"created_user"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type TransferLayingDetailDTO struct {
TransferLayingListDTO
}
// === Mapper Functions ===
func ToTransferLayingBaseDTO(e entity.TransferLaying) TransferLayingBaseDTO {
return TransferLayingBaseDTO{
Id: e.Id,
Name: e.Name,
}
}
func ToTransferLayingListDTO(e entity.TransferLaying) TransferLayingListDTO {
var createdUser *userDTO.UserBaseDTO
if e.CreatedUser.Id != 0 {
mapped := userDTO.ToUserBaseDTO(e.CreatedUser)
createdUser = &mapped
}
return TransferLayingListDTO{
TransferLayingBaseDTO: ToTransferLayingBaseDTO(e),
CreatedAt: e.CreatedAt,
UpdatedAt: e.UpdatedAt,
CreatedUser: createdUser,
}
}
func ToTransferLayingListDTOs(e []entity.TransferLaying) []TransferLayingListDTO {
result := make([]TransferLayingListDTO, len(e))
for i, r := range e {
result[i] = ToTransferLayingListDTO(r)
}
return result
}
func ToTransferLayingDetailDTO(e entity.TransferLaying) TransferLayingDetailDTO {
return TransferLayingDetailDTO{
TransferLayingListDTO: ToTransferLayingListDTO(e),
}
}
@@ -0,0 +1,26 @@
package transfer_layings
import (
"github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2"
"gorm.io/gorm"
rTransferLaying "gitlab.com/mbugroup/lti-api.git/internal/modules/production/transfer_layings/repositories"
sTransferLaying "gitlab.com/mbugroup/lti-api.git/internal/modules/production/transfer_layings/services"
rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories"
sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
)
type TransferLayingModule struct{}
func (TransferLayingModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *validator.Validate) {
transferLayingRepo := rTransferLaying.NewTransferLayingRepository(db)
userRepo := rUser.NewUserRepository(db)
transferLayingService := sTransferLaying.NewTransferLayingService(transferLayingRepo, validate)
userService := sUser.NewUserService(userRepo, validate)
TransferLayingRoutes(router, userService, transferLayingService)
}
@@ -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 TransferLayingRepository interface {
repository.BaseRepository[entity.TransferLaying]
}
type TransferLayingRepositoryImpl struct {
*repository.BaseRepositoryImpl[entity.TransferLaying]
}
func NewTransferLayingRepository(db *gorm.DB) TransferLayingRepository {
return &TransferLayingRepositoryImpl{
BaseRepositoryImpl: repository.NewBaseRepository[entity.TransferLaying](db),
}
}
@@ -0,0 +1,28 @@
package transfer_layings
import (
// m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/production/transfer_layings/controllers"
transferLaying "gitlab.com/mbugroup/lti-api.git/internal/modules/production/transfer_layings/services"
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
"github.com/gofiber/fiber/v2"
)
func TransferLayingRoutes(v1 fiber.Router, u user.UserService, s transferLaying.TransferLayingService) {
ctrl := controller.NewTransferLayingController(s)
route := v1.Group("/transfer_layings")
// 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/transfer_layings/repositories"
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/transfer_layings/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 TransferLayingService interface {
GetAll(ctx *fiber.Ctx, params *validation.Query) ([]entity.TransferLaying, int64, error)
GetOne(ctx *fiber.Ctx, id uint) (*entity.TransferLaying, error)
CreateOne(ctx *fiber.Ctx, req *validation.Create) (*entity.TransferLaying, error)
UpdateOne(ctx *fiber.Ctx, req *validation.Update, id uint) (*entity.TransferLaying, error)
DeleteOne(ctx *fiber.Ctx, id uint) error
}
type transferLayingService struct {
Log *logrus.Logger
Validate *validator.Validate
Repository repository.TransferLayingRepository
}
func NewTransferLayingService(repo repository.TransferLayingRepository, validate *validator.Validate) TransferLayingService {
return &transferLayingService{
Log: utils.Log,
Validate: validate,
Repository: repo,
}
}
func (s transferLayingService) withRelations(db *gorm.DB) *gorm.DB {
return db.Preload("CreatedUser")
}
func (s transferLayingService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.TransferLaying, int64, error) {
if err := s.Validate.Struct(params); err != nil {
return nil, 0, err
}
offset := (params.Page - 1) * params.Limit
transferLayings, 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 transferLayings: %+v", err)
return nil, 0, err
}
return transferLayings, total, nil
}
func (s transferLayingService) GetOne(c *fiber.Ctx, id uint) (*entity.TransferLaying, error) {
transferLaying, err := s.Repository.GetByID(c.Context(), id, s.withRelations)
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fiber.NewError(fiber.StatusNotFound, "TransferLaying not found")
}
if err != nil {
s.Log.Errorf("Failed get transferLaying by id: %+v", err)
return nil, err
}
return transferLaying, nil
}
func (s *transferLayingService) CreateOne(c *fiber.Ctx, req *validation.Create) (*entity.TransferLaying, error) {
if err := s.Validate.Struct(req); err != nil {
return nil, err
}
createBody := &entity.TransferLaying{
Name: req.Name,
}
if err := s.Repository.CreateOne(c.Context(), createBody, nil); err != nil {
s.Log.Errorf("Failed to create transferLaying: %+v", err)
return nil, err
}
return s.GetOne(c, createBody.Id)
}
func (s transferLayingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint) (*entity.TransferLaying, 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, "TransferLaying not found")
}
s.Log.Errorf("Failed to update transferLaying: %+v", err)
return nil, err
}
return s.GetOne(c, id)
}
func (s transferLayingService) 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, "TransferLaying not found")
}
s.Log.Errorf("Failed to delete transferLaying: %+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,gt=0"`
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100,gt=0"`
Search string `query:"search" validate:"omitempty,max=50"`
}