Feat[BE]: refactored Chickin createone and implement approvals and add more needed constant

This commit is contained in:
aguhh18
2025-10-31 15:33:31 +07:00
parent c91d84b652
commit 219a6a39ed
17 changed files with 485 additions and 415 deletions
+136 -136
View File
@@ -93,9 +93,9 @@ func Run(db *gorm.DB) error {
if err := seedTransferStock(tx, adminID); err != nil { if err := seedTransferStock(tx, adminID); err != nil {
return err return err
} }
if err := seedChickin(tx, adminID); err != nil { // if err := seedChickin(tx, adminID); err != nil {
return err // return err
} // }
fmt.Println("✅ Master data seeding completed") fmt.Println("✅ Master data seeding completed")
return nil return nil
@@ -1134,151 +1134,151 @@ func seedTransferStock(tx *gorm.DB, createdBy uint) error {
return nil return nil
} }
func seedChickin(tx *gorm.DB, createdBy uint) error { // func seedChickin(tx *gorm.DB, createdBy uint) error {
seeds := []struct { // seeds := []struct {
ProjectFlockKandangId uint // ProjectFlockKandangId uint
ChickInDate string // ChickInDate string
Quantity float64 // Quantity float64
Note string // Note string
}{ // }{
{ProjectFlockKandangId: 1, ChickInDate: "2025-10-20", Quantity: 100, Note: "Seeder chickin 1"}, // {ProjectFlockKandangId: 1, ChickInDate: "2025-10-20", Quantity: 100, Note: "Seeder chickin 1"},
{ProjectFlockKandangId: 2, ChickInDate: "2025-10-21", Quantity: 200, Note: "Seeder chickin 2"}, // {ProjectFlockKandangId: 2, ChickInDate: "2025-10-21", Quantity: 200, Note: "Seeder chickin 2"},
} // }
for _, seed := range seeds { // for _, seed := range seeds {
chickinDate, err := time.Parse("2006-01-02", seed.ChickInDate) // chickinDate, err := time.Parse("2006-01-02", seed.ChickInDate)
if err != nil { // if err != nil {
return err // return err
} // }
// Insert ProjectChickin jika belum ada // // Insert ProjectChickin jika belum ada
var chickin entity.ProjectChickin // var chickin entity.ProjectChickin
err = tx.Where("project_flock_kandang_id = ? AND chick_in_date = ?", seed.ProjectFlockKandangId, chickinDate). // err = tx.Where("project_flock_kandang_id = ? AND chick_in_date = ?", seed.ProjectFlockKandangId, chickinDate).
First(&chickin).Error // First(&chickin).Error
if errors.Is(err, gorm.ErrRecordNotFound) { // if errors.Is(err, gorm.ErrRecordNotFound) {
chickin = entity.ProjectChickin{ // chickin = entity.ProjectChickin{
ProjectFlockKandangId: seed.ProjectFlockKandangId, // ProjectFlockKandangId: seed.ProjectFlockKandangId,
ChickInDate: chickinDate, // ChickInDate: chickinDate,
Quantity: seed.Quantity, // Quantity: seed.Quantity,
Note: seed.Note, // Note: seed.Note,
CreatedBy: createdBy, // CreatedBy: createdBy,
} // }
if err := tx.Create(&chickin).Error; err != nil { // if err := tx.Create(&chickin).Error; err != nil {
return err // return err
} // }
} else if err != nil { // } else if err != nil {
return err // return err
} // }
var population entity.ProjectFlockPopulation // var population entity.ProjectFlockPopulation
err = tx.Where("project_flock_kandang_id = ?", seed.ProjectFlockKandangId).First(&population).Error // err = tx.Where("project_flock_kandang_id = ?", seed.ProjectFlockKandangId).First(&population).Error
if errors.Is(err, gorm.ErrRecordNotFound) { // if errors.Is(err, gorm.ErrRecordNotFound) {
population = entity.ProjectFlockPopulation{ // population = entity.ProjectFlockPopulation{
ProjectFlockKandangId: seed.ProjectFlockKandangId, // ProjectFlockKandangId: seed.ProjectFlockKandangId,
InitialQuantity: seed.Quantity, // InitialQuantity: seed.Quantity,
CurrentQuantity: seed.Quantity, // CurrentQuantity: seed.Quantity,
ReservedQuantity: 0, // ReservedQuantity: 0,
CreatedBy: createdBy, // CreatedBy: createdBy,
} // }
if err := tx.Create(&population).Error; err != nil { // if err := tx.Create(&population).Error; err != nil {
return err // return err
} // }
} else if err != nil { // } else if err != nil {
return err // return err
} else { // } else {
// Update population quantities // // Update population quantities
if err := tx.Model(&entity.ProjectFlockPopulation{}). // if err := tx.Model(&entity.ProjectFlockPopulation{}).
Where("id = ?", population.Id). // Where("id = ?", population.Id).
Updates(map[string]any{ // Updates(map[string]any{
"initial_quantity": population.InitialQuantity + seed.Quantity, // "initial_quantity": population.InitialQuantity + seed.Quantity,
"current_quantity": population.CurrentQuantity + seed.Quantity, // "current_quantity": population.CurrentQuantity + seed.Quantity,
"reserved_quantity": 0, // "reserved_quantity": 0,
}).Error; err != nil { // }).Error; err != nil {
return err // return err
} // }
} // }
var pfk entity.ProjectFlockKandang // var pfk entity.ProjectFlockKandang
if err := tx.Where("id = ?", seed.ProjectFlockKandangId).First(&pfk).Error; err != nil { // if err := tx.Where("id = ?", seed.ProjectFlockKandangId).First(&pfk).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { // if errors.Is(err, gorm.ErrRecordNotFound) {
// no pivot found; skip creating details // // no pivot found; skip creating details
continue // continue
} // }
return err // return err
} // }
var warehouse entity.Warehouse // var warehouse entity.Warehouse
if err := tx.Where("kandang_id = ?", pfk.KandangId).First(&warehouse).Error; err != nil { // if err := tx.Where("kandang_id = ?", pfk.KandangId).First(&warehouse).Error; err != nil {
// if warehouse not found, cannot create details // // if warehouse not found, cannot create details
if errors.Is(err, gorm.ErrRecordNotFound) { // if errors.Is(err, gorm.ErrRecordNotFound) {
continue // continue
} // }
return err // return err
} // }
var productWarehouses []entity.ProductWarehouse // var productWarehouses []entity.ProductWarehouse
err = tx.Table("product_warehouses"). // err = tx.Table("product_warehouses").
Select("product_warehouses.*"). // Select("product_warehouses.*").
Joins("JOIN products ON products.id = product_warehouses.product_id"). // Joins("JOIN products ON products.id = product_warehouses.product_id").
Joins("JOIN product_categories ON product_categories.id = products.product_category_id"). // Joins("JOIN product_categories ON product_categories.id = products.product_category_id").
Where("product_categories.code = ? AND product_warehouses.warehouse_id = ?", "DOC", warehouse.Id). // Where("product_categories.code = ? AND product_warehouses.warehouse_id = ?", "DOC", warehouse.Id).
Order("product_warehouses.created_at DESC"). // Order("product_warehouses.created_at DESC").
Find(&productWarehouses).Error // Find(&productWarehouses).Error
if err != nil { // if err != nil {
return err // return err
} // }
// If no product warehouses found, keep existing chickin.Quantity and skip details // // If no product warehouses found, keep existing chickin.Quantity and skip details
if len(productWarehouses) == 0 { // if len(productWarehouses) == 0 {
continue // continue
} // }
// sum all pw quantities and set chickin.Quantity to that total (mimic CreateOne) // // sum all pw quantities and set chickin.Quantity to that total (mimic CreateOne)
totalQty := 0.0 // totalQty := 0.0
for _, pw := range productWarehouses { // for _, pw := range productWarehouses {
totalQty += pw.Quantity // totalQty += pw.Quantity
} // }
if chickin.Quantity != totalQty { // if chickin.Quantity != totalQty {
if err := tx.Model(&entity.ProjectChickin{}).Where("id = ?", chickin.Id).Update("quantity", totalQty).Error; err != nil { // if err := tx.Model(&entity.ProjectChickin{}).Where("id = ?", chickin.Id).Update("quantity", totalQty).Error; err != nil {
return err // return err
} // }
chickin.Quantity = totalQty // chickin.Quantity = totalQty
} // }
for _, pw := range productWarehouses { // for _, pw := range productWarehouses {
// ensure detail exists or create it with full pw.Quantity // // ensure detail exists or create it with full pw.Quantity
var detail entity.ProjectChickinDetail // var detail entity.ProjectChickinDetail
err = tx.Where("project_chickin_id = ? AND product_warehouse_id = ?", chickin.Id, pw.Id).First(&detail).Error // err = tx.Where("project_chickin_id = ? AND product_warehouse_id = ?", chickin.Id, pw.Id).First(&detail).Error
if errors.Is(err, gorm.ErrRecordNotFound) { // if errors.Is(err, gorm.ErrRecordNotFound) {
detail = entity.ProjectChickinDetail{ // detail = entity.ProjectChickinDetail{
ProjectChickinId: chickin.Id, // ProjectChickinId: chickin.Id,
ProductWarehouseId: pw.Id, // ProductWarehouseId: pw.Id,
Quantity: pw.Quantity, // Quantity: pw.Quantity,
CreatedBy: createdBy, // CreatedBy: createdBy,
} // }
if err := tx.Create(&detail).Error; err != nil { // if err := tx.Create(&detail).Error; err != nil {
return err // return err
} // }
} else if err != nil { // } else if err != nil {
return err // return err
} else { // } else {
if detail.Quantity != pw.Quantity { // if detail.Quantity != pw.Quantity {
if err := tx.Model(&entity.ProjectChickinDetail{}).Where("id = ?", detail.Id).Update("quantity", pw.Quantity).Error; err != nil { // if err := tx.Model(&entity.ProjectChickinDetail{}).Where("id = ?", detail.Id).Update("quantity", pw.Quantity).Error; err != nil {
return err // return err
} // }
} // }
} // }
// zero out pw quantity // // zero out pw quantity
if err := tx.Model(&entity.ProductWarehouse{}).Where("id = ?", pw.Id).Update("quantity", 0).Error; err != nil { // if err := tx.Model(&entity.ProductWarehouse{}).Where("id = ?", pw.Id).Update("quantity", 0).Error; err != nil {
return err // return err
} // }
} // }
} // }
return nil // return nil
} // }
func ptr[T any](v T) *T { func ptr[T any](v T) *T {
return &v return &v
+7 -4
View File
@@ -12,13 +12,16 @@ type ProjectChickin struct {
Id uint `gorm:"primaryKey"` Id uint `gorm:"primaryKey"`
ProjectFlockKandangId uint `gorm:"not null"` ProjectFlockKandangId uint `gorm:"not null"`
ChickInDate time.Time `gorm:"not null"` ChickInDate time.Time `gorm:"not null"`
Quantity float64 `gorm:"not null"` ProductWarehouseId uint `gorm:"not null"`
Note string `gorm:"type:text"` UsageQty float64 `gorm:"type:numeric(15,3);not null"`
PendingUsageQty float64 `gorm:"type:numeric(15,3);default:0"`
Notes string `gorm:"type:text"`
CreatedBy uint `gorm:"not null"` CreatedBy uint `gorm:"not null"`
CreatedAt time.Time `gorm:"autoCreateTime"` CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"` UpdatedAt time.Time `gorm:"autoUpdateTime"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
ProjectFlockKandang ProjectFlockKandang `gorm:"foreignKey:ProjectFlockKandangId;references:Id"` ProjectFlockKandang *ProjectFlockKandang `gorm:"foreignKey:ProjectFlockKandangId;references:Id"`
CreatedUser User `gorm:"foreignKey:CreatedBy;references:Id"` ProductWarehouse *ProductWarehouse `gorm:"foreignKey:ProductWarehouseId;references:Id"`
CreatedUser *User `gorm:"foreignKey:CreatedBy;references:Id"`
} }
@@ -8,15 +8,17 @@ import (
type ProjectFlockPopulation struct { type ProjectFlockPopulation struct {
Id uint `gorm:"primaryKey"` Id uint `gorm:"primaryKey"`
ProjectFlockKandangId uint `gorm:"not null"` ProjectChickinId uint `gorm:"not null"`
InitialQuantity float64 `gorm:"type:numeric(15,3);not null"` ProductWarehouseId uint `gorm:"not null"`
CurrentQuantity float64 `gorm:"type:numeric(15,3);not null"` TotalQty float64 `gorm:"type:numeric(15,3);not null"`
ReservedQuantity float64 `gorm:"type:numeric(15,3)"` TotalUsedQty float64 `gorm:"type:numeric(15,3);not null"`
Notes string `gorm:"type:text"`
CreatedBy uint `gorm:"not null"` CreatedBy uint `gorm:"not null"`
CreatedAt time.Time `gorm:"autoCreateTime"` CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"` UpdatedAt time.Time `gorm:"autoUpdateTime"`
DeletedAt gorm.DeletedAt `gorm:"index"` DeletedAt gorm.DeletedAt `gorm:"index"`
ProjectFlockKandang *ProjectFlockKandang `gorm:"foreignKey:ProjectFlockKandangId;references:Id"` ProjectChickin *ProjectChickin `gorm:"foreignKey:ProjectChickinId;references:Id"`
ProductWarehouse *ProductWarehouse `gorm:"foreignKey:ProductWarehouseId;references:Id"`
CreatedUser *User `gorm:"foreignKey:CreatedBy;references:Id"` CreatedUser *User `gorm:"foreignKey:CreatedBy;references:Id"`
} }
+1
View File
@@ -28,3 +28,4 @@ type ProjectFlock struct {
KandangHistory []ProjectFlockKandang `gorm:"foreignKey:ProjectFlockId;references:Id"` KandangHistory []ProjectFlockKandang `gorm:"foreignKey:ProjectFlockId;references:Id"`
LatestApproval *Approval `gorm:"-" json:"-"` LatestApproval *Approval `gorm:"-" json:"-"`
} }
@@ -7,6 +7,8 @@ type ProjectFlockKandang struct {
ProjectFlockId uint `gorm:"not null;index:idx_project_flock_kandangs_project;uniqueIndex:idx_project_flock_kandangs_unique"` ProjectFlockId uint `gorm:"not null;index:idx_project_flock_kandangs_project;uniqueIndex:idx_project_flock_kandangs_unique"`
KandangId uint `gorm:"not null;index:idx_project_flock_kandangs_kandang;uniqueIndex:idx_project_flock_kandangs_unique"` KandangId uint `gorm:"not null;index:idx_project_flock_kandangs_kandang;uniqueIndex:idx_project_flock_kandangs_unique"`
CreatedAt time.Time `gorm:"autoCreateTime"` CreatedAt time.Time `gorm:"autoCreateTime"`
ProjectFlock ProjectFlock `gorm:"foreignKey:ProjectFlockId;references:Id"` ProjectFlock ProjectFlock `gorm:"foreignKey:ProjectFlockId;references:Id"`
Kandang Kandang `gorm:"foreignKey:KandangId;references:Id"` Kandang Kandang `gorm:"foreignKey:KandangId;references:Id"`
Chickins []ProjectChickin `gorm:"foreignKey:ProjectFlockKandangId;references:Id"`
} }
@@ -82,6 +82,10 @@ func (r *ConstantRepositoryImpl) GetConstants() map[string]interface{} {
"LOKASI", "LOKASI",
"KANDANG", "KANDANG",
}, },
"stock_log": map[string][]string{
"log_types": []string{"TRANSFER", "ADJUSTMENT"},
"transaction_types": []string{"INCREASE", "DECREASE"},
},
"supplier_categories": []string{ "supplier_categories": []string{
"BOP", "BOP",
"SAPRONAK", "SAPRONAK",
@@ -139,23 +139,33 @@ func (u *ChickinController) DeleteOne(c *fiber.Ctx) error {
}) })
} }
func (u *ChickinController) Approve(c *fiber.Ctx) error { func (u *ChickinController) Approval(c *fiber.Ctx) error {
param := c.Params("id") req := new(validation.Approve)
if err := c.BodyParser(req); err != nil {
id, err := strconv.Atoi(param) return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
} }
if err := u.ChickinService.Approve(c, uint(id)); err != nil { results, err := u.ChickinService.Approval(c, req)
if err != nil {
return err return err
} }
var (
data interface{}
message = "Submit chickin approval successfully"
)
if len(results) == 1 {
data = dto.ToChickinListDTO(results[0])
} else {
message = "Submit chickin approvals successfully"
data = dto.ToChickinListDTOs(results)
}
return c.Status(fiber.StatusOK). return c.Status(fiber.StatusOK).
JSON(response.Success{ JSON(response.Success{
Code: fiber.StatusOK, Code: fiber.StatusOK,
Status: "success", Status: "success",
Message: "Approve chickin successfully", Message: message,
Data: nil, Data: data,
}) })
} }
@@ -18,8 +18,10 @@ type ChickinBaseDTO struct {
Id uint `json:"id"` Id uint `json:"id"`
ProjectFlockKandang *ProjectFlockKandangDTO `json:"project_flock_kandang"` ProjectFlockKandang *ProjectFlockKandangDTO `json:"project_flock_kandang"`
ChickInDate time.Time `json:"chick_in_date"` ChickInDate time.Time `json:"chick_in_date"`
Quantity float64 `json:"quantity"` ProductWarehouseId uint `json:"product_warehouse_id"`
Note string `json:"note"` UsageQty float64 `json:"usage_qty"`
PendingUsageQty float64 `json:"pending_usage_qty"`
Notes string `json:"notes"`
} }
type ProjectFlockDTO struct { type ProjectFlockDTO struct {
@@ -44,8 +46,10 @@ type ChickinSimpleDTO struct {
Id uint `json:"id"` Id uint `json:"id"`
ProjectFlockKandangId uint `json:"project_flock_kandang_id"` ProjectFlockKandangId uint `json:"project_flock_kandang_id"`
ChickInDate time.Time `json:"chick_in_date"` ChickInDate time.Time `json:"chick_in_date"`
Quantity float64 `json:"quantity"` ProductWarehouseId uint `json:"product_warehouse_id"`
Note string `json:"note"` UsageQty float64 `json:"usage_qty"`
PendingUsageQty float64 `json:"pending_usage_qty"`
Notes string `json:"notes"`
CreatedBy uint `json:"created_by"` CreatedBy uint `json:"created_by"`
} }
@@ -138,16 +142,18 @@ func ToProjectFlockKandangDTO(e entity.ProjectFlockKandang) ProjectFlockKandangD
func ToChickinBaseDTO(e entity.ProjectChickin) ChickinBaseDTO { func ToChickinBaseDTO(e entity.ProjectChickin) ChickinBaseDTO {
var pfk *ProjectFlockKandangDTO var pfk *ProjectFlockKandangDTO
if e.ProjectFlockKandang.Id != 0 { if e.ProjectFlockKandang != nil && e.ProjectFlockKandang.Id != 0 {
mapped := ToProjectFlockKandangDTO(e.ProjectFlockKandang) mapped := ToProjectFlockKandangDTO(*e.ProjectFlockKandang)
pfk = &mapped pfk = &mapped
} }
return ChickinBaseDTO{ return ChickinBaseDTO{
Id: e.Id, Id: e.Id,
ProjectFlockKandang: pfk, ProjectFlockKandang: pfk,
ChickInDate: e.ChickInDate, ChickInDate: e.ChickInDate,
Quantity: e.Quantity, ProductWarehouseId: e.ProductWarehouseId,
Note: e.Note, UsageQty: e.UsageQty,
PendingUsageQty: e.PendingUsageQty,
Notes: e.Notes,
} }
} }
@@ -156,21 +162,23 @@ func ToChickinSimpleDTO(e entity.ProjectChickin) ChickinSimpleDTO {
Id: e.Id, Id: e.Id,
ProjectFlockKandangId: e.ProjectFlockKandangId, ProjectFlockKandangId: e.ProjectFlockKandangId,
ChickInDate: e.ChickInDate, ChickInDate: e.ChickInDate,
Quantity: e.Quantity, ProductWarehouseId: e.ProductWarehouseId,
Note: e.Note, UsageQty: e.UsageQty,
PendingUsageQty: e.PendingUsageQty,
Notes: e.Notes,
CreatedBy: e.CreatedBy, CreatedBy: e.CreatedBy,
} }
} }
func ToChickinListDTO(e entity.ProjectChickin) ChickinListDTO { func ToChickinListDTO(e entity.ProjectChickin) ChickinListDTO {
var createdUser *userBaseDTO.UserBaseDTO var createdUser *userBaseDTO.UserBaseDTO
if e.CreatedUser.Id != 0 { if e.CreatedUser != nil && e.CreatedUser.Id != 0 {
mapped := userBaseDTO.ToUserBaseDTO(e.CreatedUser) mapped := userBaseDTO.ToUserBaseDTO(*e.CreatedUser)
createdUser = &mapped createdUser = &mapped
} }
var pfk *ProjectFlockKandangDTO var pfk *ProjectFlockKandangDTO
if e.ProjectFlockKandang.Id != 0 { if e.ProjectFlockKandang != nil && e.ProjectFlockKandang.Id != 0 {
mapped := ToProjectFlockKandangDTO(e.ProjectFlockKandang) mapped := ToProjectFlockKandangDTO(*e.ProjectFlockKandang)
pfk = &mapped pfk = &mapped
} }
return ChickinListDTO{ return ChickinListDTO{
@@ -1,10 +1,15 @@
package chickins package chickins
import ( import (
"fmt"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"gorm.io/gorm" "gorm.io/gorm"
commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories" rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
rKandang "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/repositories" rKandang "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/repositories"
rWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories" rWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories"
@@ -15,6 +20,8 @@ import (
rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories" rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories"
sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services" sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
utils "gitlab.com/mbugroup/lti-api.git/internal/utils"
) )
type ChickinModule struct{} type ChickinModule struct{}
@@ -32,6 +39,12 @@ func (ChickinModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *
userRepo := rUser.NewUserRepository(db) userRepo := rUser.NewUserRepository(db)
approvalRepo := commonRepo.NewApprovalRepository(db)
approvalService := commonSvc.NewApprovalService(approvalRepo)
if err := approvalService.RegisterWorkflowSteps(utils.ApprovalWorkflowProjectFlockKandang, utils.ProjectFlockKandangApprovalSteps); err != nil {
panic(fmt.Sprintf("failed to register project flock kandang approval workflow: %v", err))
}
chickinService := sChickin.NewChickinService(chickinRepo, kandangRepo, warehouseRepo, productWarehouseRepo, projectFlockRepo, projectflockkandangrepo, projectflockpopulationrepo, chickinDetailRepo, validate) chickinService := sChickin.NewChickinService(chickinRepo, kandangRepo, warehouseRepo, productWarehouseRepo, projectFlockRepo, projectflockkandangrepo, projectflockpopulationrepo, chickinDetailRepo, validate)
userService := sUser.NewUserService(userRepo, validate) userService := sUser.NewUserService(userRepo, validate)
@@ -25,5 +25,5 @@ func ChickinRoutes(v1 fiber.Router, u user.UserService, s chickin.ChickinService
route.Get("/:id", ctrl.GetOne) route.Get("/:id", ctrl.GetOne)
route.Patch("/:id", ctrl.UpdateOne) route.Patch("/:id", ctrl.UpdateOne)
route.Delete("/:id", ctrl.DeleteOne) route.Delete("/:id", ctrl.DeleteOne)
route.Post("/:id/approve", ctrl.Approve) route.Post("/approvals", ctrl.Approval)
} }
@@ -2,7 +2,11 @@ package service
import ( import (
"errors" "errors"
"fmt"
"strings"
commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities" entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories" rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
KandangRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/repositories" KandangRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/repositories"
@@ -24,7 +28,7 @@ type ChickinService interface {
CreateOne(ctx *fiber.Ctx, req *validation.Create) (*entity.ProjectChickin, error) CreateOne(ctx *fiber.Ctx, req *validation.Create) (*entity.ProjectChickin, error)
UpdateOne(ctx *fiber.Ctx, req *validation.Update, id uint) (*entity.ProjectChickin, error) UpdateOne(ctx *fiber.Ctx, req *validation.Update, id uint) (*entity.ProjectChickin, error)
DeleteOne(ctx *fiber.Ctx, id uint) error DeleteOne(ctx *fiber.Ctx, id uint) error
Approve(ctx *fiber.Ctx, id uint) error Approval(ctx *fiber.Ctx, req *validation.Approve) ([]entity.ProjectChickin, error)
} }
type chickinService struct { type chickinService struct {
@@ -110,107 +114,98 @@ func (s *chickinService) CreateOne(c *fiber.Ctx, req *validation.Create) (*entit
return nil, err return nil, err
} }
projectflockkandang, err := s.ProjectflockKandangRepo.GetByID(c.Context(), req.ProjectFlockKandangId) projectFlockKandang, err := s.ProjectflockKandangRepo.GetByID(c.Context(), req.ProjectFlockKandangId)
if err != nil { if err != nil {
s.Log.Errorf("Failed to get projectflock kandang: %+v", err) return nil, fiber.NewError(fiber.StatusNotFound, "Project Flock Kandang not found")
return nil, err
} }
warehouse, err := s.WarehouseRepo.GetByKandangID(c.Context(), projectflockkandang.KandangId) warehouse, err := s.WarehouseRepo.GetByKandangID(c.Context(), projectFlockKandang.KandangId)
if err != nil { if err != nil {
s.Log.Errorf("Failed to get warehouse: %+v", err) return nil, fiber.NewError(fiber.StatusNotFound, "Warehouse for Kandang not found")
return nil, err
} }
// move complex DB query into repository for cleaner service var productWarehouses []entity.ProductWarehouse
productWarehouses, err := s.ProductWarehouseRepo.GetByCategoryCodeAndWarehouseID(c.Context(), "DOC", warehouse.Id)
if err != nil {
s.Log.Errorf("Failed to get product warehouses: %+v", err)
return nil, err
}
if len(productWarehouses) == 0 {
return nil, fiber.NewError(fiber.StatusNotFound, "Product Warehouse not found for the given Project Flock and Warehouse")
}
totalQuantity := 0.0
for _, pw := range productWarehouses {
totalQuantity += pw.Quantity
}
if totalQuantity < 1 { if strings.ToUpper(strings.TrimSpace(projectFlockKandang.ProjectFlock.Category)) == string(utils.ProjectFlockCategoryGrowing) {
return nil, fiber.NewError(fiber.StatusBadRequest, "Insufficient quantity in Product Warehouses")
productWarehouses, err = s.ProductWarehouseRepo.GetByCategoryCodeAndWarehouseID(c.Context(), "DOC", warehouse.Id)
if err != nil || len(productWarehouses) == 0 {
return nil, fiber.NewError(fiber.StatusNotFound, "Product for growing category in the Kandang's warehouse not found")
}
} }
chickinDate, err := utils.ParseDateString(req.ChickInDate) chickinDate, err := utils.ParseDateString(req.ChickInDate)
if err != nil { if err != nil {
s.Log.Errorf("Failed to parse chickin date: %+v", err)
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid ChickInDate format") return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid ChickInDate format")
} }
actorID := uint(1) // todo nanti ambil dari auth context
newChikins := make([]*entity.ProjectChickin, 0)
for _, productWarehouse := range productWarehouses {
if productWarehouse.Quantity > 0 {
newChickin := &entity.ProjectChickin{ newChickin := &entity.ProjectChickin{
ProjectFlockKandangId: projectflockkandang.Id,
ChickInDate: chickinDate,
Quantity: totalQuantity,
Note: req.Note,
CreatedBy: 1, //todo: ganti dengan user login
}
err = s.Repository.CreateOne(c.Context(), newChickin, nil)
if err != nil {
s.Log.Errorf("Failed to create chickin: %+v", err)
return nil, err
}
// Update semua product warehouse: set quantity jadi 0
for _, pw := range productWarehouses {
err = s.ProductWarehouseRepo.PatchOne(c.Context(), pw.Id, map[string]any{
"quantity": 0,
}, nil)
if err != nil {
s.Log.Errorf("Failed to update product warehouse quantity: %+v", err)
return nil, err
}
newChickinDetail := &entity.ProjectChickinDetail{
ProjectChickinId: newChickin.Id,
ProductWarehouseId: pw.Id,
Quantity: pw.Quantity,
CreatedBy: 1, // todo: ganti dengan user login
}
err = s.ProjectChickinDetailRepo.CreateOne(c.Context(), newChickinDetail, nil)
if err != nil {
s.Log.Errorf("Failed to create chickin detail: %+v", err)
return nil, err
}
}
existingPopulation, err := s.ProjectflockPopulationRepo.GetByProjectFlockKandangID(c.Context(), req.ProjectFlockKandangId)
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
s.Log.Errorf("Failed to get project flock population: %+v", err)
return nil, err
}
if existingPopulation != nil {
err = s.ProjectflockPopulationRepo.PatchOne(c.Context(), existingPopulation.Id, map[string]any{
"reserved_quantity": newChickin.Quantity + existingPopulation.ReservedQuantity,
}, nil)
if err != nil {
s.Log.Errorf("Failed to update project flock population: %+v", err)
return nil, err
}
} else {
newPopulation := &entity.ProjectFlockPopulation{
ProjectFlockKandangId: req.ProjectFlockKandangId, ProjectFlockKandangId: req.ProjectFlockKandangId,
InitialQuantity: 0, ChickInDate: chickinDate,
CurrentQuantity: 0, UsageQty: 0,
ReservedQuantity: newChickin.Quantity, PendingUsageQty: productWarehouse.Quantity,
CreatedBy: 1, // todo: ganti dengan user login ProductWarehouseId: productWarehouse.Id,
Notes: req.Note,
CreatedBy: actorID,
} }
err = s.ProjectflockPopulationRepo.CreateOne(c.Context(), newPopulation, nil)
if err != nil { newChikins = append(newChikins, newChickin)
s.Log.Errorf("Failed to create project flock population: %+v", err)
return nil, err
} }
} }
return s.GetOne(c, newChickin.Id) if len(newChikins) == 0 {
return nil, fiber.NewError(fiber.StatusBadRequest, "No chickins to create")
}
err = s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
approvalSvcTx := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(dbTransaction))
productWarehouseTx := s.ProductWarehouseRepo.WithTx(dbTransaction)
if err := s.Repository.WithTx(dbTransaction).CreateMany(c.Context(), newChikins, nil); err != nil {
return err
}
latest, err := approvalSvcTx.LatestByTarget(c.Context(), utils.ApprovalWorkflowProjectFlockKandang, projectFlockKandang.Id, nil)
if err != nil {
return err
}
for _, chickin := range newChikins {
updates := map[string]any{"quantity": 0}
if err := productWarehouseTx.PatchOne(c.Context(), chickin.ProductWarehouseId, updates, nil); err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fmt.Errorf("failed to update product warehouse quantity for id %d", chickin.ProductWarehouseId)
}
return err
}
}
if latest == nil {
action := entity.ApprovalActionCreated
if _, err := approvalSvcTx.CreateApproval(c.Context(), utils.ApprovalWorkflowProjectFlockKandang, projectFlockKandang.Id, utils.ProjectFlockKandangStepPengajuan, &action, actorID, nil); err != nil {
lower := strings.ToLower(err.Error())
if !(strings.Contains(lower, "duplicate") || strings.Contains(lower, "unique constraint") || strings.Contains(lower, "23505")) {
return err
}
}
}
return nil
})
if err != nil {
return nil, fiber.NewError(fiber.StatusBadRequest, err.Error())
}
return newChikins[0], nil
} }
func (s chickinService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint) (*entity.ProjectChickin, error) { func (s chickinService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint) (*entity.ProjectChickin, error) {
@@ -224,7 +219,8 @@ func (s chickinService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint)
updateBody["chick_in_date"] = req.ChickInDate updateBody["chick_in_date"] = req.ChickInDate
} }
if req.Note != "" { if req.Note != "" {
updateBody["note"] = req.Note // entity uses `Notes` => column `notes`
updateBody["notes"] = req.Note
} }
if len(updateBody) == 0 { if len(updateBody) == 0 {
return s.GetOne(c, id) return s.GetOne(c, id)
@@ -243,160 +239,140 @@ func (s chickinService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint)
func (s chickinService) DeleteOne(c *fiber.Ctx, id uint) error { func (s chickinService) DeleteOne(c *fiber.Ctx, id uint) error {
// Simplified delete: directly call repository delete. Complex restore logic removed for now.
if err := s.Repository.DeleteOne(c.Context(), id); err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fiber.NewError(fiber.StatusNotFound, "Chickin not found")
}
return err
}
return nil
}
func (s chickinService) Approval(c *fiber.Ctx, req *validation.Approve) ([]entity.ProjectChickin, error) {
if err := s.Validate.Struct(req); err != nil {
return nil, err
}
actorID := uint(1) // todo nanti ambil dari auth context
var action entity.ApprovalAction
switch strings.ToUpper(strings.TrimSpace(req.Action)) {
case string(entity.ApprovalActionRejected):
action = entity.ApprovalActionRejected
case string(entity.ApprovalActionApproved):
action = entity.ApprovalActionApproved
default:
return nil, fiber.NewError(fiber.StatusBadRequest, "action must be APPROVED or REJECTED")
}
approvableIDs := uniqueUintSlice(req.ApprovableIds)
if len(approvableIDs) == 0 {
return nil, fiber.NewError(fiber.StatusBadRequest, "approvable_ids must contain at least one id")
}
step := utils.ProjectFlockKandangStepPengajuan
if action == entity.ApprovalActionApproved {
step = utils.ProjectFlockKandangStepDisetujui
}
err := s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error { err := s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
approvalSvc := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(dbTransaction))
ProjectFlockPopulationRepotx := s.ProjectflockPopulationRepo.WithTx(dbTransaction)
chickinRepoTx := s.Repository.WithTx(dbTransaction)
chickinRepo := repository.NewChickinRepository(dbTransaction) for _, approvableID := range approvableIDs {
projectFlockKandangRepo := s.ProjectflockKandangRepo.WithTx(dbTransaction)
productWarehouseRepo := s.ProductWarehouseRepo.WithTxRepo(dbTransaction)
projectFlockPopulationRepo := s.ProjectflockPopulationRepo.WithTx(dbTransaction)
projectChickinDetailRepo := s.ProjectChickinDetailRepo.WithTxRepo(dbTransaction)
warehouseRepoTx := rWarehouse.NewWarehouseRepository(dbTransaction)
chickin, err := chickinRepo.GetByID(c.Context(), id, nil) exists, err := s.ProjectflockKandangRepo.WithTx(dbTransaction).IdExists(c.Context(), approvableID)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fiber.NewError(fiber.StatusNotFound, "Chickin not found")
}
return err
}
population, err := projectFlockPopulationRepo.GetByProjectFlockKandangID(c.Context(), chickin.ProjectFlockKandangId)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fiber.NewError(fiber.StatusNotFound, "Project flock population not found")
}
return err
}
newReserved := population.ReservedQuantity - chickin.Quantity
if newReserved < 0 {
newReserved = 0
}
err = projectFlockPopulationRepo.PatchOne(c.Context(), population.Id, map[string]any{
"reserved_quantity": newReserved,
}, nil)
if err != nil { if err != nil {
return err return err
} }
if !exists {
restoreFromDetails := func() (bool, error) { return fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("ProjectFlockKandang %d not found", approvableID))
details, err := projectChickinDetailRepo.GetByProjectChickinID(c.Context(), chickin.Id)
if err != nil {
return false, err
} }
for _, d := range details { if _, err := approvalSvc.CreateApproval(
productWarehouse, err := productWarehouseRepo.GetByID(c.Context(), d.ProductWarehouseId, nil) c.Context(),
if err != nil { utils.ApprovalWorkflowProjectFlockKandang,
if errors.Is(err, gorm.ErrRecordNotFound) { approvableID,
continue step,
&action,
actorID,
req.Notes,
); err != nil {
lower := strings.ToLower(err.Error())
if !(strings.Contains(lower, "duplicate") || strings.Contains(lower, "unique constraint") || strings.Contains(lower, "23505")) {
return err
} }
return false, err s.Log.Infof("ignored duplicate approval for kandang %d: %v", approvableID, err)
} }
updatedQuantity := productWarehouse.Quantity + d.Quantity if action == entity.ApprovalActionApproved {
if err := productWarehouseRepo.PatchOne(c.Context(), productWarehouse.Id, map[string]any{"quantity": updatedQuantity}, nil); err != nil {
return false, err
}
}
if err := projectChickinDetailRepo.DeleteMany(c.Context(), func(db *gorm.DB) *gorm.DB { var chickins []entity.ProjectChickin
return db.Where("project_chickin_id = ?", chickin.Id) if err := chickinRepoTx.DB().WithContext(c.Context()).Where("project_flock_kandang_id = ?", approvableID).Find(&chickins).Error; err != nil {
}); err != nil {
return false, err
}
return true, nil
}
restored, err := restoreFromDetails()
if err != nil {
return err return err
} }
if !restored { for _, chickin := range chickins {
projectflockkandang, err := projectFlockKandangRepo.GetByID(c.Context(), population.ProjectFlockKandangId) population := &entity.ProjectFlockPopulation{
if err != nil { ProjectChickinId: chickin.Id,
if errors.Is(err, gorm.ErrRecordNotFound) { ProductWarehouseId: chickin.ProductWarehouseId,
return fiber.NewError(fiber.StatusNotFound, "Project flock kandang not found") TotalQty: chickin.PendingUsageQty,
TotalUsedQty: 0,
Notes: chickin.Notes,
CreatedBy: actorID,
} }
if err := ProjectFlockPopulationRepotx.CreateOne(c.Context(), population, nil); err != nil {
lower := strings.ToLower(err.Error())
if !(strings.Contains(lower, "duplicate") || strings.Contains(lower, "unique constraint") || strings.Contains(lower, "23505")) {
return err return err
} }
s.Log.Infof("ignored duplicate population for chickin %d: %v", chickin.Id, err)
warehouse, err := warehouseRepoTx.GetByKandangID(c.Context(), projectflockkandang.KandangId)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fiber.NewError(fiber.StatusNotFound, "Warehouse not found for kandang")
}
return err
}
productWarehouse, err := productWarehouseRepo.GetLatestByCategoryCodeAndWarehouseID(c.Context(), "DOC", warehouse.Id)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fiber.NewError(fiber.StatusNotFound, "Product Warehouse not found for the given Project Flock and Warehouse")
}
return err
}
updatedQuantity := productWarehouse.Quantity + chickin.Quantity
if err := productWarehouseRepo.PatchOne(c.Context(), productWarehouse.Id, map[string]any{"quantity": updatedQuantity}, nil); err != nil {
return err
} }
} }
if err := chickinRepo.DeleteOne(c.Context(), id); err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fiber.NewError(fiber.StatusNotFound, "Chickin not found")
} }
return err
} }
return nil return nil
}) })
if err != nil { if err != nil {
if ferr, ok := err.(*fiber.Error); ok { if fiberErr, ok := err.(*fiber.Error); ok {
return ferr return nil, fiberErr
} }
return err
}
return nil
}
func (s *chickinService) Approve(c *fiber.Ctx, id uint) error {
// todo: ini contoh akhir jika sudah approved
chickin, err := s.Repository.GetByID(c.Context(), id, nil)
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return fiber.NewError(fiber.StatusNotFound, "Chickin not found") return nil, fiber.NewError(fiber.StatusNotFound, "Chickin not found")
} }
if err != nil { s.Log.Errorf("Failed to record approval for chickins %+v: %+v", approvableIDs, err)
s.Log.Errorf("Failed get chickin by id: %+v", err) return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to record approval")
return err
} }
population, err := s.ProjectflockPopulationRepo.GetByProjectFlockKandangID(c.Context(), chickin.ProjectFlockKandangId) updated := make([]entity.ProjectChickin, 0)
if err != nil { for _, kandangID := range approvableIDs {
s.Log.Errorf("Failed to get project flock population: %+v", err) var chickins []entity.ProjectChickin
return err if err := s.Repository.DB().WithContext(c.Context()).Where("project_flock_kandang_id = ?", kandangID).Find(&chickins).Error; err != nil {
return nil, err
}
updated = append(updated, chickins...)
} }
err = s.ProjectflockPopulationRepo.PatchOne(c.Context(), population.Id, map[string]any{ return updated, nil
"reserved_quantity": population.ReservedQuantity - chickin.Quantity,
"initial_quantity": population.InitialQuantity + chickin.Quantity,
"current_quantity": population.CurrentQuantity + chickin.Quantity,
}, nil)
if err != nil {
s.Log.Errorf("Failed to update project flock population: %+v", err)
return err
} }
return nil func uniqueUintSlice(values []uint) []uint {
seen := make(map[uint]struct{}, len(values))
result := make([]uint, 0, len(values))
for _, v := range values {
if _, ok := seen[v]; ok {
continue
}
seen[v] = struct{}{}
result = append(result, v)
}
return result
} }
@@ -3,7 +3,7 @@ package validation
type Create struct { type Create struct {
ProjectFlockKandangId uint `json:"project_flock_kandang_id" validate:"required,number,min=1"` ProjectFlockKandangId uint `json:"project_flock_kandang_id" validate:"required,number,min=1"`
ChickInDate string `json:"chick_in_date" validate:"required,datetime=2006-01-02"` ChickInDate string `json:"chick_in_date" validate:"required,datetime=2006-01-02"`
Note string `json:"note" validate:"omitempty` Note string `json:"note" validate:"omitempty"`
} }
type Update struct { type Update struct {
@@ -16,3 +16,9 @@ type Query struct {
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"` Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"`
ProjectFlockKandangId uint `query:"project_flock_kandang_id" validate:"omitempty,number,min=1"` ProjectFlockKandangId uint `query:"project_flock_kandang_id" validate:"omitempty,number,min=1"`
} }
type Approve struct {
Action string `json:"action" validate:"required_strict"`
ApprovableIds []uint `json:"approvable_ids" validate:"required_strict,min=1,dive,gt=0"`
Notes *string `json:"notes,omitempty" validate:"omitempty,max=500"`
}
@@ -16,6 +16,7 @@ type ProjectflockRepository interface {
GetActiveByFlock(ctx context.Context, flockID uint) (*entity.ProjectFlock, error) GetActiveByFlock(ctx context.Context, flockID uint) (*entity.ProjectFlock, error)
GetMaxPeriodByFlock(ctx context.Context, flockID uint) (int, error) GetMaxPeriodByFlock(ctx context.Context, flockID uint) (int, error)
GetNextPeriodForFlock(ctx context.Context, flockID uint) (int, error) GetNextPeriodForFlock(ctx context.Context, flockID uint) (int, error)
IdExists(ctx context.Context, id uint) (bool, error)
} }
type ProjectflockRepositoryImpl struct { type ProjectflockRepositoryImpl struct {
@@ -28,6 +29,10 @@ func NewProjectflockRepository(db *gorm.DB) ProjectflockRepository {
} }
} }
func (r *ProjectflockRepositoryImpl) IdExists(ctx context.Context, id uint) (bool, error) {
return repository.Exists[entity.ProjectFlock](ctx, r.DB(), id)
}
func (r *ProjectflockRepositoryImpl) GetAllByFlock(ctx context.Context, flockID uint) ([]entity.ProjectFlock, error) { func (r *ProjectflockRepositoryImpl) GetAllByFlock(ctx context.Context, flockID uint) ([]entity.ProjectFlock, error) {
var records []entity.ProjectFlock var records []entity.ProjectFlock
if err := r.DB().WithContext(ctx). if err := r.DB().WithContext(ctx).
@@ -3,6 +3,7 @@ package repository
import ( import (
"context" "context"
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities" entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
"gorm.io/gorm" "gorm.io/gorm"
) )
@@ -14,6 +15,7 @@ type ProjectFlockKandangRepository interface {
DeleteMany(ctx context.Context, projectFlockID uint, kandangIDs []uint) error DeleteMany(ctx context.Context, projectFlockID uint, kandangIDs []uint) error
GetAll(ctx context.Context) ([]entity.ProjectFlockKandang, error) GetAll(ctx context.Context) ([]entity.ProjectFlockKandang, error)
WithTx(tx *gorm.DB) ProjectFlockKandangRepository WithTx(tx *gorm.DB) ProjectFlockKandangRepository
IdExists(ctx context.Context, id uint) (bool, error)
DB() *gorm.DB DB() *gorm.DB
} }
@@ -67,6 +69,9 @@ func (r *projectFlockKandangRepositoryImpl) WithTx(tx *gorm.DB) ProjectFlockKand
func (r *projectFlockKandangRepositoryImpl) DB() *gorm.DB { func (r *projectFlockKandangRepositoryImpl) DB() *gorm.DB {
return r.db return r.db
} }
func (r *projectFlockKandangRepositoryImpl) IdExists(ctx context.Context, id uint) (bool, error) {
return repository.Exists[entity.ProjectFlockKandang](ctx, r.db, id)
}
func (r *projectFlockKandangRepositoryImpl) GetByID(ctx context.Context, id uint) (*entity.ProjectFlockKandang, error) { func (r *projectFlockKandangRepositoryImpl) GetByID(ctx context.Context, id uint) (*entity.ProjectFlockKandang, error) {
record := new(entity.ProjectFlockKandang) record := new(entity.ProjectFlockKandang)
@@ -670,18 +670,19 @@ func (s *recordingService) getPreviousRecording(tx *gorm.DB, projectFlockKandang
} }
func (s *recordingService) getTotalChick(tx *gorm.DB, projectFlockKandangId uint) (int64, error) { func (s *recordingService) getTotalChick(tx *gorm.DB, projectFlockKandangId uint) (int64, error) {
var population entity.ProjectFlockPopulation // var population entity.ProjectFlockPopulation
err := tx. // err := tx.
Where("project_flock_kandang_id = ?", projectFlockKandangId). // Where("project_flock_kandang_id = ?", projectFlockKandangId).
Order("created_at DESC"). // Order("created_at DESC").
First(&population).Error // First(&population).Error
if errors.Is(err, gorm.ErrRecordNotFound) { // if errors.Is(err, gorm.ErrRecordNotFound) {
return 0, nil // return 0, nil
} // }
if err != nil { // if err != nil {
return 0, err // return 0, err
} // }
return int64(math.Round(population.InitialQuantity)), nil //todo : nanti ganti lagi mas saya hardcode dulu
return int64(math.Round(1000)), nil
} }
func (s *recordingService) getAverageBodyWeight(tx *gorm.DB, recordingID uint) (float64, error) { func (s *recordingService) getAverageBodyWeight(tx *gorm.DB, recordingID uint) (float64, error) {
@@ -5,6 +5,7 @@ import (
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"gorm.io/gorm" "gorm.io/gorm"
rProjectFlock "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
rTransferLaying "gitlab.com/mbugroup/lti-api.git/internal/modules/production/transfer_layings/repositories" 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" sTransferLaying "gitlab.com/mbugroup/lti-api.git/internal/modules/production/transfer_layings/services"
@@ -17,10 +18,11 @@ type TransferLayingModule struct{}
func (TransferLayingModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *validator.Validate) { func (TransferLayingModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *validator.Validate) {
transferLayingRepo := rTransferLaying.NewTransferLayingRepository(db) transferLayingRepo := rTransferLaying.NewTransferLayingRepository(db)
userRepo := rUser.NewUserRepository(db) userRepo := rUser.NewUserRepository(db)
projectFlockRepo := rProjectFlock.NewProjectflockRepository(db)
projectFlockKandangRepo := rProjectFlock.NewProjectFlockKandangRepository(db)
transferLayingService := sTransferLaying.NewTransferLayingService(transferLayingRepo, validate) transferLayingService := sTransferLaying.NewTransferLayingService(transferLayingRepo, projectFlockRepo, projectFlockKandangRepo, validate)
userService := sUser.NewUserService(userRepo, validate) userService := sUser.NewUserService(userRepo, validate)
TransferLayingRoutes(router, userService, transferLayingService) TransferLayingRoutes(router, userService, transferLayingService)
} }
+32
View File
@@ -79,6 +79,24 @@ const (
WarehouseTypeKandang WarehouseType = "KANDANG" WarehouseTypeKandang WarehouseType = "KANDANG"
) )
// -------------------------------------------------------------------
// Stock log
// -------------------------------------------------------------------
type StockLogTransactionType string
const (
StockLogTransactionTypeIncrease StockLogTransactionType = "INCREASE"
StockLogTransactionTypeDecrease StockLogTransactionType = "DECREASE"
)
type StockLogType string
const (
StockLogTypeAdjustment StockLogType = "ADJUSTMENT"
StockLogTypeTransfer StockLogType = "TRANSFER"
)
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// WarehouseType // WarehouseType
// ------------------------------------------------------------------- // -------------------------------------------------------------------
@@ -140,6 +158,20 @@ var ProjectFlockApprovalSteps = map[approvalutils.ApprovalStep]string{
ProjectFlockStepAktif: "Aktif", ProjectFlockStepAktif: "Aktif",
} }
// -------------------------------------------------------------------
// Project Flock Kandang Approval
// -------------------------------------------------------------------
const (
ApprovalWorkflowProjectFlockKandang approvalutils.ApprovalWorkflowKey = approvalutils.ApprovalWorkflowKey("PROJECT_FLOCK_KANDANGS")
ProjectFlockKandangStepPengajuan approvalutils.ApprovalStep = 1
ProjectFlockKandangStepDisetujui approvalutils.ApprovalStep = 2
)
var ProjectFlockKandangApprovalSteps = map[approvalutils.ApprovalStep]string{
ProjectFlockKandangStepPengajuan: "Pengajuan",
ProjectFlockKandangStepDisetujui: "Disetujui",
}
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Validators // Validators
// ------------------------------------------------------------------- // -------------------------------------------------------------------