mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-24 07:15:43 +00:00
Merge branch 'feat/BE/Sprint-6' of https://gitlab.com/mbugroup/lti-api into feat/BE/US-279/closing-produksi
This commit is contained in:
@@ -302,3 +302,29 @@ func (u *ProjectflockController) LookupProjectFlockKandang(c *fiber.Ctx) error {
|
||||
Message: "Get projectflock kandang successfully",
|
||||
Data: dtoResult})
|
||||
}
|
||||
|
||||
func (u *ProjectflockController) Resubmit(c *fiber.Ctx) error {
|
||||
param := c.Params("id")
|
||||
req := new(validation.Resubmit)
|
||||
|
||||
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.ProjectflockService.Resubmit(c, req, uint(id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).
|
||||
JSON(response.Success{
|
||||
Code: fiber.StatusOK,
|
||||
Status: "success",
|
||||
Message: "Resubmit projectflock successfully",
|
||||
Data: dto.ToProjectFlockListDTO(*result),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
fcrDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/fcrs/dto"
|
||||
kandangDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/dto"
|
||||
locationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/dto"
|
||||
nonstockDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/nonstocks/dto"
|
||||
|
||||
// pfutils "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/utils"
|
||||
userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto"
|
||||
@@ -24,15 +25,16 @@ type ProjectFlockRelationDTO struct {
|
||||
|
||||
type ProjectFlockListDTO struct {
|
||||
ProjectFlockRelationDTO
|
||||
Area *areaDTO.AreaRelationDTO `json:"area,omitempty"`
|
||||
Category string `json:"category"`
|
||||
Fcr *fcrDTO.FcrRelationDTO `json:"fcr,omitempty"`
|
||||
Location *locationDTO.LocationRelationDTO `json:"location,omitempty"`
|
||||
Kandangs []KandangWithProjectFlockIdDTO `json:"kandangs,omitempty"`
|
||||
CreatedUser *userDTO.UserRelationDTO `json:"created_user"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
Approval approvalDTO.ApprovalRelationDTO `json:"approval"`
|
||||
Area *areaDTO.AreaRelationDTO `json:"area,omitempty"`
|
||||
Category string `json:"category"`
|
||||
Fcr *fcrDTO.FcrRelationDTO `json:"fcr,omitempty"`
|
||||
Location *locationDTO.LocationRelationDTO `json:"location,omitempty"`
|
||||
Kandangs []KandangWithProjectFlockIdDTO `json:"kandangs,omitempty"`
|
||||
ProjectBudgets []ProjectBudgetDTO `json:"project_budgets,omitempty"`
|
||||
CreatedUser *userDTO.UserRelationDTO `json:"created_user"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
Approval approvalDTO.ApprovalRelationDTO `json:"approval"`
|
||||
}
|
||||
|
||||
type KandangWithProjectFlockIdDTO struct {
|
||||
@@ -51,6 +53,13 @@ type KandangPeriodSummaryDTO struct {
|
||||
Period int `json:"period"`
|
||||
}
|
||||
|
||||
type ProjectBudgetDTO struct {
|
||||
Id uint `json:"id"`
|
||||
Qty float64 `json:"qty"`
|
||||
Price float64 `json:"price"`
|
||||
Nonstock *nonstockDTO.NonstockRelationDTO `json:"nonstock,omitempty"`
|
||||
}
|
||||
|
||||
func ToProjectFlockListDTOWithPeriod(e entity.ProjectFlock, period int) ProjectFlockListDTO {
|
||||
var createdUser *userDTO.UserRelationDTO
|
||||
if e.CreatedUser.Id != 0 {
|
||||
@@ -110,6 +119,7 @@ func ToProjectFlockListDTOWithPeriod(e entity.ProjectFlock, period int) ProjectF
|
||||
ProjectFlockRelationDTO: createProjectFlockRelationDTO(e, period),
|
||||
Area: areaSummary,
|
||||
Kandangs: kandangSummaries,
|
||||
ProjectBudgets: ToProjectBudgetDTOs(e.Budgets),
|
||||
Category: e.Category,
|
||||
Fcr: fcrSummary,
|
||||
Location: locationSummary,
|
||||
@@ -184,3 +194,26 @@ func createProjectFlockRelationDTO(e entity.ProjectFlock, period int) ProjectFlo
|
||||
FlockName: e.FlockName,
|
||||
}
|
||||
}
|
||||
|
||||
func ToProjectBudgetDTO(e entity.ProjectBudget) ProjectBudgetDTO {
|
||||
var nonstockRef *nonstockDTO.NonstockRelationDTO
|
||||
if e.Nonstock != nil && e.Nonstock.Id != 0 {
|
||||
mapped := nonstockDTO.ToNonstockRelationDTO(*e.Nonstock)
|
||||
nonstockRef = &mapped
|
||||
}
|
||||
|
||||
return ProjectBudgetDTO{
|
||||
Id: e.Id,
|
||||
Qty: e.Qty,
|
||||
Price: e.Price,
|
||||
Nonstock: nonstockRef,
|
||||
}
|
||||
}
|
||||
|
||||
func ToProjectBudgetDTOs(e []entity.ProjectBudget) []ProjectBudgetDTO {
|
||||
result := make([]ProjectBudgetDTO, len(e))
|
||||
for i, r := range e {
|
||||
result[i] = ToProjectBudgetDTO(r)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -12,7 +12,9 @@ import (
|
||||
rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
|
||||
rFlock "gitlab.com/mbugroup/lti-api.git/internal/modules/master/flocks/repositories"
|
||||
rKandang "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/repositories"
|
||||
rNonstock "gitlab.com/mbugroup/lti-api.git/internal/modules/master/nonstocks/repositories"
|
||||
rWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories"
|
||||
rProjectBudget "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
||||
rProjectflock "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
||||
|
||||
sProjectflock "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/services"
|
||||
@@ -27,10 +29,12 @@ type ProjectflockModule struct{}
|
||||
func (ProjectflockModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *validator.Validate) {
|
||||
flockRepo := rFlock.NewFlockRepository(db)
|
||||
kandangRepo := rKandang.NewKandangRepository(db)
|
||||
nonstockRepo := rNonstock.NewNonstockRepository(db)
|
||||
projectflockRepo := rProjectflock.NewProjectflockRepository(db)
|
||||
projectflockKandangRepo := rProjectflock.NewProjectFlockKandangRepository(db)
|
||||
warehouseRepo := rWarehouse.NewWarehouseRepository(db)
|
||||
productWarehouseRepo := rProductWarehouse.NewProductWarehouseRepository(db)
|
||||
projectBudgetRepo := rProjectBudget.NewProjectBudgetRepository(db)
|
||||
userRepo := rUser.NewUserRepository(db)
|
||||
|
||||
approvalRepo := commonRepo.NewApprovalRepository(db)
|
||||
@@ -39,7 +43,7 @@ func (ProjectflockModule) RegisterRoutes(router fiber.Router, db *gorm.DB, valid
|
||||
panic(fmt.Sprintf("failed to register project flock approval workflow: %v", err))
|
||||
}
|
||||
|
||||
projectflockService := sProjectflock.NewProjectflockService(projectflockRepo, flockRepo, kandangRepo, projectflockKandangRepo, warehouseRepo, productWarehouseRepo, approvalService, validate)
|
||||
projectflockService := sProjectflock.NewProjectflockService(projectflockRepo, flockRepo, kandangRepo, projectflockKandangRepo, warehouseRepo, productWarehouseRepo, projectBudgetRepo, nonstockRepo, approvalService, validate)
|
||||
userService := sUser.NewUserService(userRepo, validate)
|
||||
|
||||
ProjectflockRoutes(router, userService, projectflockService)
|
||||
|
||||
@@ -54,7 +54,10 @@ func (r *ProjectflockRepositoryImpl) WithDefaultRelations() func(*gorm.DB) *gorm
|
||||
Preload("Location").
|
||||
Preload("Kandangs").
|
||||
Preload("KandangHistory").
|
||||
Preload("KandangHistory.Kandang")
|
||||
Preload("KandangHistory.Kandang").
|
||||
Preload("Budgets").
|
||||
Preload("Budgets.Nonstock").
|
||||
Preload("Budgets.Nonstock.Uom")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,5 +22,6 @@ func ProjectflockRoutes(v1 fiber.Router, u user.UserService, s projectflock.Proj
|
||||
route.Get("/kandangs/lookup", ctrl.LookupProjectFlockKandang)
|
||||
route.Post("/approvals", ctrl.Approval)
|
||||
route.Get("/locations/:location_id/periods", ctrl.GetPeriodSummary)
|
||||
route.Put("/:id/resubmit", ctrl.Resubmit)
|
||||
|
||||
}
|
||||
|
||||
@@ -15,7 +15,9 @@ import (
|
||||
flockDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/flocks/dto"
|
||||
flockRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/master/flocks/repositories"
|
||||
kandangRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/repositories"
|
||||
nonstockRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/master/nonstocks/repositories"
|
||||
warehouseRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories"
|
||||
projectBudgetRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
||||
repository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
||||
pfutils "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/utils"
|
||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/validations"
|
||||
@@ -38,6 +40,7 @@ type ProjectflockService interface {
|
||||
GetPeriodSummary(ctx *fiber.Ctx, locationID uint) ([]KandangPeriodSummary, error)
|
||||
GetProjectPeriods(ctx *fiber.Ctx, projectIDs []uint) (map[uint]int, error)
|
||||
Approval(ctx *fiber.Ctx, req *validation.Approve) ([]entity.ProjectFlock, error)
|
||||
Resubmit(ctx *fiber.Ctx, req *validation.Resubmit, id uint) (*entity.ProjectFlock, error)
|
||||
}
|
||||
|
||||
type projectflockService struct {
|
||||
@@ -46,8 +49,10 @@ type projectflockService struct {
|
||||
Repository repository.ProjectflockRepository
|
||||
FlockRepo flockRepository.FlockRepository
|
||||
KandangRepo kandangRepository.KandangRepository
|
||||
NonstockRepo nonstockRepository.NonstockRepository
|
||||
WarehouseRepo warehouseRepository.WarehouseRepository
|
||||
ProductWarehouseRepo productWarehouseRepository.ProductWarehouseRepository
|
||||
ProjectBudgetRepo projectBudgetRepository.ProjectBudgetRepository
|
||||
PivotRepo repository.ProjectFlockKandangRepository
|
||||
ApprovalSvc commonSvc.ApprovalService
|
||||
approvalWorkflow approvalutils.ApprovalWorkflowKey
|
||||
@@ -66,8 +71,11 @@ func NewProjectflockService(
|
||||
pivotRepo repository.ProjectFlockKandangRepository,
|
||||
warehouseRepo warehouseRepository.WarehouseRepository,
|
||||
productWarehouseRepo productWarehouseRepository.ProductWarehouseRepository,
|
||||
projectBudgetRepo projectBudgetRepository.ProjectBudgetRepository,
|
||||
nonstockRepo nonstockRepository.NonstockRepository,
|
||||
approvalSvc commonSvc.ApprovalService,
|
||||
validate *validator.Validate,
|
||||
|
||||
) ProjectflockService {
|
||||
return &projectflockService{
|
||||
Log: utils.Log,
|
||||
@@ -75,6 +83,7 @@ func NewProjectflockService(
|
||||
Repository: repo,
|
||||
FlockRepo: flockRepo,
|
||||
KandangRepo: kandangRepo,
|
||||
NonstockRepo: nonstockRepo,
|
||||
WarehouseRepo: warehouseRepo,
|
||||
ProductWarehouseRepo: productWarehouseRepo,
|
||||
PivotRepo: pivotRepo,
|
||||
@@ -298,7 +307,6 @@ func (s *projectflockService) CreateOne(c *fiber.Ctx, req *validation.Create) (*
|
||||
err = s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
|
||||
projectRepo := repository.NewProjectflockRepository(dbTransaction)
|
||||
|
||||
// Generate unique flock name (sequential per base name, starting from 1)
|
||||
generatedName, _, err := s.generateSequentialFlockName(c.Context(), projectRepo, canonicalBase, 1, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -309,7 +317,6 @@ func (s *projectflockService) CreateOne(c *fiber.Ctx, req *validation.Create) (*
|
||||
return err
|
||||
}
|
||||
|
||||
// Compute period per kandang so every kandang maintains its own cycle history.
|
||||
periods, err := projectRepo.GetNextPeriodsForKandangs(c.Context(), kandangIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -318,6 +325,10 @@ func (s *projectflockService) CreateOne(c *fiber.Ctx, req *validation.Create) (*
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.UpsertProjectBudget(c.Context(), dbTransaction, createBody.Id, req.ProjectBudgets); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
action := entity.ApprovalActionCreated
|
||||
approvalSvcTx := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(dbTransaction))
|
||||
_, err = approvalSvcTx.CreateApproval(
|
||||
@@ -842,3 +853,139 @@ func (s projectflockService) kandangRepoWithTx(tx *gorm.DB) kandangRepository.Ka
|
||||
}
|
||||
return kandangRepository.NewKandangRepository(s.Repository.DB())
|
||||
}
|
||||
|
||||
func (s projectflockService) Resubmit(c *fiber.Ctx, req *validation.Resubmit, id uint) (*entity.ProjectFlock, error) {
|
||||
if err := s.Validate.Struct(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
actorID, err := m.ActorIDFromContext(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
existing, err := s.Repository.GetByID(c.Context(), id, s.Repository.WithDefaultRelations())
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Project flock tidak ditemukan")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Gagal mengambil data project flock")
|
||||
}
|
||||
|
||||
kandangIDs := uniqueUintSlice(req.KandangIds)
|
||||
kandangs, err := s.KandangRepo.GetByIDs(c.Context(), kandangIDs, nil)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Beberapa kandang tidak ditemukan")
|
||||
}
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Gagal mengambil data kandang")
|
||||
}
|
||||
if len(kandangs) != len(kandangIDs) {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Beberapa kandang tidak ditemukan")
|
||||
}
|
||||
|
||||
for _, pb := range req.ProjectBudgets {
|
||||
if err := commonSvc.EnsureRelations(c.Context(),
|
||||
commonSvc.RelationCheck{Name: "Nonstock", ID: &pb.NonstockId, Exists: s.NonstockRepo.IdExists},
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
err = s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
|
||||
|
||||
approvalSvc := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(dbTransaction))
|
||||
|
||||
var period int = 1
|
||||
if len(existing.KandangHistory) > 0 {
|
||||
period = existing.KandangHistory[0].Period
|
||||
}
|
||||
|
||||
periods := make(map[uint]int, len(kandangIDs))
|
||||
for _, kandangID := range kandangIDs {
|
||||
periods[kandangID] = period
|
||||
}
|
||||
|
||||
if err := s.attachKandangs(c.Context(), dbTransaction, existing.Id, kandangIDs, periods); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.UpsertProjectBudget(c.Context(), dbTransaction, existing.Id, req.ProjectBudgets); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
action := entity.ApprovalActionUpdated
|
||||
_, err = approvalSvc.CreateApproval(
|
||||
c.Context(),
|
||||
utils.ApprovalWorkflowProjectFlock,
|
||||
existing.Id,
|
||||
utils.ProjectFlockStepPengajuan,
|
||||
&action,
|
||||
actorID,
|
||||
nil,
|
||||
)
|
||||
return err
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if fiberErr, ok := err.(*fiber.Error); ok {
|
||||
return nil, fiberErr
|
||||
}
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Project flock tidak ditemukan")
|
||||
}
|
||||
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Gagal mengajukan ulang project flock")
|
||||
}
|
||||
|
||||
return s.getOneEntityOnly(c, id)
|
||||
}
|
||||
|
||||
func (s projectflockService) UpsertProjectBudget(ctx context.Context, dbTransaction *gorm.DB, projectFlockID uint, budgets []validation.ProjectBudget) error {
|
||||
|
||||
if len(budgets) == 0 {
|
||||
return nil
|
||||
}
|
||||
budgetRepo := projectBudgetRepository.NewProjectBudgetRepository(dbTransaction)
|
||||
|
||||
nonstockMap := make(map[uint]bool)
|
||||
relationChecks := make([]commonSvc.RelationCheck, 0, len(budgets))
|
||||
for _, b := range budgets {
|
||||
if nonstockMap[b.NonstockId] {
|
||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Duplicate nonstock_id: %d", b.NonstockId))
|
||||
}
|
||||
nonstockMap[b.NonstockId] = true
|
||||
nonstockID := b.NonstockId
|
||||
relationChecks = append(relationChecks, commonSvc.RelationCheck{
|
||||
Name: "Nonstock",
|
||||
ID: &nonstockID,
|
||||
Exists: s.NonstockRepo.IdExists,
|
||||
})
|
||||
}
|
||||
|
||||
if err := commonSvc.EnsureRelations(ctx, relationChecks...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := budgetRepo.DeleteMany(ctx, func(q *gorm.DB) *gorm.DB {
|
||||
return q.Where("project_flock_id = ?", projectFlockID)
|
||||
}); err != nil && err != gorm.ErrRecordNotFound {
|
||||
return err
|
||||
}
|
||||
|
||||
records := make([]*entity.ProjectBudget, 0, len(budgets))
|
||||
for _, b := range budgets {
|
||||
records = append(records, &entity.ProjectBudget{
|
||||
ProjectFlockId: projectFlockID,
|
||||
NonstockId: b.NonstockId,
|
||||
Price: b.Price,
|
||||
Qty: b.Qty,
|
||||
})
|
||||
}
|
||||
|
||||
if err := budgetRepo.CreateMany(ctx, records, nil); err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to save project budgets")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package validation
|
||||
|
||||
type Create struct {
|
||||
FlockName string `json:"flock_name" validate:"required_strict"`
|
||||
AreaId uint `json:"area_id" validate:"required_strict,number,gt=0"`
|
||||
Category string `json:"category" validate:"required_strict"`
|
||||
FcrId uint `json:"fcr_id" validate:"required_strict,number,gt=0"`
|
||||
LocationId uint `json:"location_id" validate:"required_strict,number,gt=0"`
|
||||
KandangIds []uint `json:"kandang_ids" validate:"required,min=1,dive,gt=0"`
|
||||
FlockName string `json:"flock_name" validate:"required_strict"`
|
||||
AreaId uint `json:"area_id" validate:"required_strict,number,gt=0"`
|
||||
Category string `json:"category" validate:"required_strict"`
|
||||
FcrId uint `json:"fcr_id" validate:"required_strict,number,gt=0"`
|
||||
LocationId uint `json:"location_id" validate:"required_strict,number,gt=0"`
|
||||
KandangIds []uint `json:"kandang_ids" validate:"required,min=1,dive,gt=0"`
|
||||
ProjectBudgets []ProjectBudget `json:"project_budgets" validate:"required,min=1,dive"`
|
||||
}
|
||||
|
||||
type Query struct {
|
||||
@@ -27,3 +28,14 @@ type Approve struct {
|
||||
ApprovableIds []uint `json:"approvable_ids" validate:"required_strict,min=1,dive,gt=0"`
|
||||
Notes *string `json:"notes,omitempty" validate:"omitempty,max=500"`
|
||||
}
|
||||
|
||||
type ProjectBudget struct {
|
||||
NonstockId uint `json:"nonstock_id" validate:"required_strict,number,gt=0"`
|
||||
Price float64 `json:"price" validate:"required_strict,number,gt=0"`
|
||||
Qty float64 `json:"qty" validate:"required_strict,number,gt=0"`
|
||||
}
|
||||
|
||||
type Resubmit struct {
|
||||
KandangIds []uint `json:"kandang_ids" validate:"required_strict,min=1,dive,gt=0"`
|
||||
ProjectBudgets []ProjectBudget `json:"project_budgets" validate:"required_strict,min=1,dive"`
|
||||
}
|
||||
|
||||
@@ -146,27 +146,6 @@ func (u *RecordingController) UpdateOne(c *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (u *RecordingController) SubmitGrading(c *fiber.Ctx) error {
|
||||
req := new(validation.SubmitGrading)
|
||||
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
|
||||
}
|
||||
|
||||
result, err := u.RecordingService.SubmitGrading(c, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).
|
||||
JSON(response.Success{
|
||||
Code: fiber.StatusOK,
|
||||
Status: "success",
|
||||
Message: "Submit grading eggs successfully",
|
||||
Data: dto.ToRecordingDetailDTO(*result),
|
||||
})
|
||||
}
|
||||
|
||||
func (u *RecordingController) Approve(c *fiber.Ctx) error {
|
||||
req := new(validation.Approve)
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -16,22 +15,19 @@ import (
|
||||
// === DTO Structs ===
|
||||
|
||||
type RecordingRelationDTO struct {
|
||||
Id uint `json:"id"`
|
||||
ProjectFlockKandangId uint `json:"project_flock_kandang_id"`
|
||||
RecordDatetime time.Time `json:"record_datetime"`
|
||||
Day int `json:"day"`
|
||||
ProjectFlockCategory string `json:"project_flock_category"`
|
||||
TotalDepletionQty float64 `json:"total_depletion_qty"`
|
||||
CumDepletionRate float64 `json:"cum_depletion_rate"`
|
||||
DailyGain float64 `json:"daily_gain"`
|
||||
AvgDailyGain float64 `json:"avg_daily_gain"`
|
||||
CumIntake int `json:"cum_intake"`
|
||||
FcrValue float64 `json:"fcr_value"`
|
||||
TotalChickQty float64 `json:"total_chick_qty"`
|
||||
Approval approvalDTO.ApprovalRelationDTO `json:"approval"`
|
||||
EggGradingStatus *string `json:"egg_grading_status"`
|
||||
EggGradingPendingQty *int `json:"egg_grading_pending_qty"`
|
||||
EggGradingCompletedQty *int `json:"egg_grading_completed_qty"`
|
||||
Id uint `json:"id"`
|
||||
ProjectFlockKandangId uint `json:"project_flock_kandang_id"`
|
||||
RecordDatetime time.Time `json:"record_datetime"`
|
||||
Day int `json:"day"`
|
||||
ProjectFlockCategory string `json:"project_flock_category"`
|
||||
TotalDepletionQty float64 `json:"total_depletion_qty"`
|
||||
CumDepletionRate float64 `json:"cum_depletion_rate"`
|
||||
DailyGain float64 `json:"daily_gain"`
|
||||
AvgDailyGain float64 `json:"avg_daily_gain"`
|
||||
CumIntake int `json:"cum_intake"`
|
||||
FcrValue float64 `json:"fcr_value"`
|
||||
TotalChickQty float64 `json:"total_chick_qty"`
|
||||
Approval approvalDTO.ApprovalRelationDTO `json:"approval"`
|
||||
}
|
||||
|
||||
type RecordingListDTO struct {
|
||||
@@ -72,8 +68,8 @@ type RecordingEggDTO struct {
|
||||
Id uint `json:"id"`
|
||||
ProductWarehouseId uint `json:"product_warehouse_id"`
|
||||
Qty int `json:"qty"`
|
||||
Weight *float64 `json:"weight,omitempty"`
|
||||
ProductWarehouse productWarehouseDTO.ProductWarehouseDTO `json:"product_warehouse"`
|
||||
Gradings []RecordingEggGradingDTO `json:"gradings,omitempty"`
|
||||
}
|
||||
|
||||
type RecordingProductWarehouseDTO struct {
|
||||
@@ -84,11 +80,6 @@ type RecordingProductWarehouseDTO struct {
|
||||
WarehouseName string `json:"warehouse_name"`
|
||||
}
|
||||
|
||||
type RecordingEggGradingDTO struct {
|
||||
Grade string `json:"grade,omitempty"`
|
||||
Qty float64 `json:"qty"`
|
||||
}
|
||||
|
||||
// === Mapper Functions ===
|
||||
|
||||
func ToRecordingRelationDTO(e entity.Recording) RecordingRelationDTO {
|
||||
@@ -140,25 +131,20 @@ func ToRecordingRelationDTO(e entity.Recording) RecordingRelationDTO {
|
||||
latestApproval = snapshot
|
||||
}
|
||||
|
||||
gradingStatus, gradingPending, gradingCompleted := computeEggGradingStatus(e)
|
||||
|
||||
return RecordingRelationDTO{
|
||||
Id: e.Id,
|
||||
ProjectFlockKandangId: e.ProjectFlockKandangId,
|
||||
RecordDatetime: e.RecordDatetime,
|
||||
Day: day,
|
||||
ProjectFlockCategory: projectFlockCategory,
|
||||
TotalDepletionQty: totalDepletionQty,
|
||||
CumDepletionRate: cumDepletionRate,
|
||||
DailyGain: dailyGain,
|
||||
AvgDailyGain: avgDailyGain,
|
||||
CumIntake: cumIntake,
|
||||
FcrValue: fcrValue,
|
||||
TotalChickQty: totalChickQty,
|
||||
Approval: latestApproval,
|
||||
EggGradingStatus: gradingStatus,
|
||||
EggGradingPendingQty: gradingPending,
|
||||
EggGradingCompletedQty: gradingCompleted,
|
||||
Id: e.Id,
|
||||
ProjectFlockKandangId: e.ProjectFlockKandangId,
|
||||
RecordDatetime: e.RecordDatetime,
|
||||
Day: day,
|
||||
ProjectFlockCategory: projectFlockCategory,
|
||||
TotalDepletionQty: totalDepletionQty,
|
||||
CumDepletionRate: cumDepletionRate,
|
||||
DailyGain: dailyGain,
|
||||
AvgDailyGain: avgDailyGain,
|
||||
CumIntake: cumIntake,
|
||||
FcrValue: fcrValue,
|
||||
TotalChickQty: totalChickQty,
|
||||
Approval: latestApproval,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,29 +239,13 @@ func ToRecordingEggDTOs(eggs []entity.RecordingEgg) []RecordingEggDTO {
|
||||
Id: egg.Id,
|
||||
ProductWarehouseId: egg.ProductWarehouseId,
|
||||
Qty: egg.Qty,
|
||||
Weight: egg.Weight,
|
||||
ProductWarehouse: mapProductWarehouseDTO(&egg.ProductWarehouse),
|
||||
Gradings: ToRecordingEggGradingDTOs(egg.GradingEggs),
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func ToRecordingEggGradingDTOs(gradings []entity.GradingEgg) []RecordingEggGradingDTO {
|
||||
if len(gradings) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
result := make([]RecordingEggGradingDTO, len(gradings))
|
||||
for i, grading := range gradings {
|
||||
result[i] = RecordingEggGradingDTO{
|
||||
Grade: grading.Grade,
|
||||
Qty: grading.Qty,
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func mapProductWarehouseDTO(pw *entity.ProductWarehouse) productWarehouseDTO.ProductWarehouseDTO {
|
||||
if pw == nil {
|
||||
return productWarehouseDTO.ProductWarehouseDTO{}
|
||||
@@ -289,61 +259,6 @@ func mapProductWarehouseDTO(pw *entity.ProductWarehouse) productWarehouseDTO.Pro
|
||||
return *mapped
|
||||
}
|
||||
|
||||
const goodEggProductWarehouseID uint = 5
|
||||
|
||||
func computeEggGradingStatus(e entity.Recording) (*string, *int, *int) {
|
||||
goodEggs := filterGoodEggs(e.Eggs)
|
||||
if len(goodEggs) == 0 {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
totalEggs := 0
|
||||
totalGraded := 0.0
|
||||
for _, egg := range goodEggs {
|
||||
totalEggs += egg.Qty
|
||||
for _, grading := range egg.GradingEggs {
|
||||
totalGraded += grading.Qty
|
||||
}
|
||||
}
|
||||
|
||||
if totalEggs == 0 {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
pendingFloat := float64(totalEggs) - totalGraded
|
||||
if pendingFloat < 0 {
|
||||
pendingFloat = 0
|
||||
}
|
||||
pendingInt := int(math.Round(pendingFloat))
|
||||
completedInt := int(math.Round(totalGraded))
|
||||
if completedInt < 0 {
|
||||
completedInt = 0
|
||||
}
|
||||
|
||||
if pendingInt > 0 {
|
||||
status := "GRADING_TELUR"
|
||||
return &status, &pendingInt, &completedInt
|
||||
}
|
||||
|
||||
status := "GRADING_SELESAI"
|
||||
zero := 0
|
||||
return &status, &zero, &completedInt
|
||||
}
|
||||
|
||||
func filterGoodEggs(eggs []entity.RecordingEgg) []entity.RecordingEgg {
|
||||
if len(eggs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
result := make([]entity.RecordingEgg, 0, len(eggs))
|
||||
for _, egg := range eggs {
|
||||
if egg.ProductWarehouseId == goodEggProductWarehouseID {
|
||||
result = append(result, egg)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func defaultRecordingLatestApproval(e entity.Recording) approvalDTO.ApprovalRelationDTO {
|
||||
result := approvalDTO.ApprovalRelationDTO{}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ func (RecordingModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate
|
||||
ProductWarehouseID: "product_warehouse_id",
|
||||
UsageQuantity: "usage_qty",
|
||||
PendingQuantity: "pending_qty",
|
||||
CreatedAt: "created_at",
|
||||
CreatedAt: "id",
|
||||
},
|
||||
}); err != nil {
|
||||
if !strings.Contains(strings.ToLower(err.Error()), "already registered") {
|
||||
|
||||
@@ -35,8 +35,6 @@ type RecordingRepository interface {
|
||||
DeleteEggs(tx *gorm.DB, recordingID uint) error
|
||||
ListEggs(tx *gorm.DB, recordingID uint) ([]entity.RecordingEgg, error)
|
||||
GetRecordingEggByID(ctx context.Context, id uint, modifier func(*gorm.DB) *gorm.DB) (*entity.RecordingEgg, error)
|
||||
CreateGradingEggs(tx *gorm.DB, gradings []entity.GradingEgg) error
|
||||
DeleteGradingEggs(tx *gorm.DB, recordingEggID uint) error
|
||||
|
||||
ExistsOnDate(ctx context.Context, projectFlockKandangId uint, recordTime time.Time) (bool, error)
|
||||
|
||||
@@ -76,8 +74,7 @@ func (r *RecordingRepositoryImpl) WithRelations(db *gorm.DB) *gorm.DB {
|
||||
Preload("Eggs").
|
||||
Preload("Eggs.ProductWarehouse").
|
||||
Preload("Eggs.ProductWarehouse.Product").
|
||||
Preload("Eggs.ProductWarehouse.Warehouse").
|
||||
Preload("Eggs.GradingEggs")
|
||||
Preload("Eggs.ProductWarehouse.Warehouse")
|
||||
}
|
||||
|
||||
func (r *RecordingRepositoryImpl) GenerateNextDay(tx *gorm.DB, projectFlockKandangId uint) (int, error) {
|
||||
@@ -188,7 +185,6 @@ func (r *RecordingRepositoryImpl) GetRecordingEggByID(
|
||||
Preload("Recording.ProjectFlockKandang").
|
||||
Preload("Recording.ProjectFlockKandang.ProjectFlock").
|
||||
Preload("ProductWarehouse").
|
||||
Preload("GradingEggs").
|
||||
Where("id = ?", id)
|
||||
|
||||
if err := query.First(&egg).Error; err != nil {
|
||||
@@ -197,17 +193,6 @@ func (r *RecordingRepositoryImpl) GetRecordingEggByID(
|
||||
return &egg, nil
|
||||
}
|
||||
|
||||
func (r *RecordingRepositoryImpl) CreateGradingEggs(tx *gorm.DB, gradings []entity.GradingEgg) error {
|
||||
if len(gradings) == 0 {
|
||||
return nil
|
||||
}
|
||||
return tx.Create(&gradings).Error
|
||||
}
|
||||
|
||||
func (r *RecordingRepositoryImpl) DeleteGradingEggs(tx *gorm.DB, recordingEggID uint) error {
|
||||
return tx.Where("recording_egg_id = ?", recordingEggID).Delete(&entity.GradingEgg{}).Error
|
||||
}
|
||||
|
||||
func (r *RecordingRepositoryImpl) ExistsOnDate(ctx context.Context, projectFlockKandangId uint, recordTime time.Time) (bool, error) {
|
||||
if projectFlockKandangId == 0 {
|
||||
return false, nil
|
||||
|
||||
@@ -18,7 +18,6 @@ func RecordingRoutes(v1 fiber.Router, u user.UserService, s recording.RecordingS
|
||||
route.Get("/", ctrl.GetAll)
|
||||
route.Get("/next-day", ctrl.GetNextDay)
|
||||
route.Post("/", ctrl.CreateOne)
|
||||
route.Post("/gradings", ctrl.SubmitGrading)
|
||||
route.Get("/:id", ctrl.GetOne)
|
||||
route.Patch("/:id", ctrl.UpdateOne)
|
||||
route.Post("/approvals", ctrl.Approve)
|
||||
|
||||
@@ -33,7 +33,6 @@ type RecordingService interface {
|
||||
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
|
||||
SubmitGrading(ctx *fiber.Ctx, req *validation.SubmitGrading) (*entity.Recording, error)
|
||||
Approval(ctx *fiber.Ctx, req *validation.Approve) ([]entity.Recording, error)
|
||||
}
|
||||
|
||||
@@ -273,7 +272,7 @@ func (s *recordingService) CreateOne(c *fiber.Ctx, req *validation.Create) (*ent
|
||||
}
|
||||
|
||||
action := entity.ApprovalActionCreated
|
||||
if err := s.createRecordingApproval(ctx, tx, createdRecording.Id, utils.RecordingStepGradingTelur, action, createdRecording.CreatedBy, nil); err != nil {
|
||||
if err := s.createRecordingApproval(ctx, tx, createdRecording.Id, utils.RecordingStepPengajuan, action, createdRecording.CreatedBy, nil); err != nil {
|
||||
s.Log.Errorf("Failed to create recording approval for %d: %+v", createdRecording.Id, err)
|
||||
return err
|
||||
}
|
||||
@@ -347,16 +346,6 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin
|
||||
}
|
||||
}
|
||||
|
||||
hasExistingGradings := false
|
||||
for _, egg := range recordingEntity.Eggs {
|
||||
if len(egg.GradingEggs) > 0 {
|
||||
hasExistingGradings = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
hasEggsAfterUpdate := len(recordingEntity.Eggs) > 0
|
||||
|
||||
if hasBodyChanges {
|
||||
if err := s.Repository.DeleteBodyWeights(tx, recordingEntity.Id); err != nil {
|
||||
s.Log.Errorf("Failed to clear body weights: %+v", err)
|
||||
@@ -441,9 +430,6 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin
|
||||
s.Log.Errorf("Failed to adjust product warehouses for eggs: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
hasExistingGradings = false
|
||||
hasEggsAfterUpdate = len(req.Eggs) > 0
|
||||
}
|
||||
|
||||
if hasBodyChanges || hasStockChanges || hasDepletionChanges {
|
||||
@@ -459,20 +445,7 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Actor Id tidak valid untuk approval")
|
||||
}
|
||||
|
||||
var step approvalutils.ApprovalStep
|
||||
if isLaying {
|
||||
if !hasEggsAfterUpdate {
|
||||
step = utils.RecordingStepGradingTelur
|
||||
} else if hasEggChanges {
|
||||
step = utils.RecordingStepGradingTelur
|
||||
} else if hasExistingGradings {
|
||||
step = utils.RecordingStepPengajuan
|
||||
} else {
|
||||
step = utils.RecordingStepGradingTelur
|
||||
}
|
||||
} else {
|
||||
step = utils.RecordingStepPengajuan
|
||||
}
|
||||
step := utils.RecordingStepPengajuan
|
||||
|
||||
latestApproval := recordingEntity.LatestApproval
|
||||
if latestApproval == nil {
|
||||
@@ -517,109 +490,6 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin
|
||||
return s.GetOne(c, id)
|
||||
}
|
||||
|
||||
func (s *recordingService) SubmitGrading(c *fiber.Ctx, req *validation.SubmitGrading) (*entity.Recording, error) {
|
||||
if err := s.Validate.Struct(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(req.EggsGrading) == 0 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "eggs_grading must contain at least one item")
|
||||
}
|
||||
|
||||
recordingEggID := req.EggsGrading[0].RecordingEggId
|
||||
for _, grading := range req.EggsGrading[1:] {
|
||||
if grading.RecordingEggId != recordingEggID {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "semua grading harus untuk recording egg yang sama")
|
||||
}
|
||||
}
|
||||
|
||||
ctx := c.Context()
|
||||
var recordingID uint
|
||||
transactionErr := s.Repository.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
recordingEgg, err := s.Repository.GetRecordingEggByID(ctx, recordingEggID, func(db *gorm.DB) *gorm.DB {
|
||||
return tx
|
||||
})
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return fiber.NewError(fiber.StatusNotFound, "Recording egg not found")
|
||||
}
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to get recording egg %d: %+v", recordingEggID, err)
|
||||
return err
|
||||
}
|
||||
|
||||
var category string
|
||||
if recordingEgg.Recording.ProjectFlockKandang != nil && recordingEgg.Recording.ProjectFlockKandang.ProjectFlock.Id != 0 {
|
||||
category = strings.ToUpper(recordingEgg.Recording.ProjectFlockKandang.ProjectFlock.Category)
|
||||
}
|
||||
if category != strings.ToUpper(string(utils.ProjectFlockCategoryLaying)) {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Grading eggs hanya diperbolehkan pada project flock dengan kategori laying")
|
||||
}
|
||||
|
||||
totalGradingQty := 0.0
|
||||
for _, grading := range req.EggsGrading {
|
||||
totalGradingQty += grading.Qty
|
||||
}
|
||||
|
||||
availableRecorded := float64(recordingEgg.Qty)
|
||||
if totalGradingQty > availableRecorded {
|
||||
return fiber.NewError(
|
||||
fiber.StatusBadRequest,
|
||||
fmt.Sprintf("Total grading (%.2f) melebihi jumlah telur tercatat (%.2f)", totalGradingQty, availableRecorded),
|
||||
)
|
||||
}
|
||||
|
||||
if recordingEgg.ProductWarehouse.Id != 0 {
|
||||
availableWarehouse := recordingEgg.ProductWarehouse.Quantity
|
||||
if totalGradingQty > availableWarehouse {
|
||||
return fiber.NewError(
|
||||
fiber.StatusBadRequest,
|
||||
fmt.Sprintf("Total grading (%.2f) melebihi stok telur baik (%.2f)", totalGradingQty, availableWarehouse),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.Repository.DeleteGradingEggs(tx, recordingEgg.Id); err != nil {
|
||||
s.Log.Errorf("Failed to clear grading eggs for recording egg %d: %+v", recordingEgg.Id, err)
|
||||
return err
|
||||
}
|
||||
|
||||
gradings := make([]entity.GradingEgg, 0, len(req.EggsGrading))
|
||||
createdBy := recordingEgg.CreatedBy
|
||||
if createdBy == 0 {
|
||||
createdBy = recordingEgg.Recording.CreatedBy
|
||||
}
|
||||
for _, item := range req.EggsGrading {
|
||||
gradings = append(gradings, entity.GradingEgg{
|
||||
RecordingEggId: recordingEgg.Id,
|
||||
Grade: strings.TrimSpace(item.Grade),
|
||||
Qty: item.Qty,
|
||||
CreatedBy: createdBy,
|
||||
})
|
||||
}
|
||||
|
||||
if len(gradings) > 0 {
|
||||
if err := s.Repository.CreateGradingEggs(tx, gradings); err != nil {
|
||||
s.Log.Errorf("Failed to persist grading eggs for recording egg %d: %+v", recordingEgg.Id, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
action := entity.ApprovalActionUpdated
|
||||
if err := s.createRecordingApproval(ctx, tx, recordingEgg.RecordingId, utils.RecordingStepPengajuan, action, createdBy, nil); err != nil {
|
||||
s.Log.Errorf("Failed to create approval after grading for recording %d: %+v", recordingEgg.RecordingId, err)
|
||||
return err
|
||||
}
|
||||
|
||||
recordingID = recordingEgg.RecordingId
|
||||
return nil
|
||||
})
|
||||
if transactionErr != nil {
|
||||
return nil, transactionErr
|
||||
}
|
||||
|
||||
return s.GetOne(c, recordingID)
|
||||
}
|
||||
|
||||
func (s recordingService) Approval(c *fiber.Ctx, req *validation.Approve) ([]entity.Recording, error) {
|
||||
if err := s.Validate.Struct(req); err != nil {
|
||||
return nil, err
|
||||
@@ -934,14 +804,10 @@ func (s *recordingService) computeAndUpdateMetrics(ctx context.Context, tx *gorm
|
||||
return fmt.Errorf("getFeedUsageInGrams: %w", err)
|
||||
}
|
||||
|
||||
fcrId, err := s.Repository.GetFcrID(tx, recording.ProjectFlockKandangId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getFcrID: %w", err)
|
||||
}
|
||||
|
||||
currentAvgGrams := recordingutil.ToGrams(currentAvgWeight)
|
||||
currentAvgKg := recordingutil.GramsToKg(currentAvgGrams)
|
||||
prevAvgGrams := recordingutil.ToGrams(prevAvgWeight)
|
||||
prevAvgKg := recordingutil.GramsToKg(prevAvgGrams)
|
||||
|
||||
currentDepletion := float64(totalDepletionQty)
|
||||
cumDepletionQty := prevCumDepletionQty + currentDepletion
|
||||
@@ -951,9 +817,10 @@ func (s *recordingService) computeAndUpdateMetrics(ctx context.Context, tx *gorm
|
||||
}
|
||||
recording.TotalDepletionQty = &cumDepletionQty
|
||||
|
||||
var remainingChick float64
|
||||
if totalChick > 0 {
|
||||
totalChickFloat := float64(totalChick)
|
||||
remainingChick := totalChickFloat - cumDepletionQty
|
||||
remainingChick = totalChickFloat - cumDepletionQty
|
||||
if remainingChick < 0 {
|
||||
remainingChick = 0
|
||||
}
|
||||
@@ -978,24 +845,19 @@ func (s *recordingService) computeAndUpdateMetrics(ctx context.Context, tx *gorm
|
||||
updates["daily_gain"] = dailyGainKg
|
||||
recording.DailyGain = &dailyGainKg
|
||||
} else {
|
||||
updates["daily_gain"] = gorm.Expr("NULL")
|
||||
recording.DailyGain = nil
|
||||
dailyGainKg := 0.0
|
||||
updates["daily_gain"] = dailyGainKg
|
||||
recording.DailyGain = &dailyGainKg
|
||||
}
|
||||
|
||||
if fcrId != 0 && currentAvgKg > 0 && day > 0 {
|
||||
if fcrWeightKg, ok, err := s.Repository.GetFcrStandardWeightKg(tx, fcrId, currentAvgKg); err != nil {
|
||||
return fmt.Errorf("getFcrStandardWeightKg: %w", err)
|
||||
} else if ok {
|
||||
avgDailyGain := (currentAvgKg - fcrWeightKg) / float64(day)
|
||||
updates["avg_daily_gain"] = avgDailyGain
|
||||
recording.AvgDailyGain = &avgDailyGain
|
||||
} else {
|
||||
updates["avg_daily_gain"] = gorm.Expr("NULL")
|
||||
recording.AvgDailyGain = nil
|
||||
}
|
||||
if currentAvgKg > 0 && remainingChick > 0 {
|
||||
avgDailyGain := (currentAvgKg - prevAvgKg) / remainingChick
|
||||
updates["avg_daily_gain"] = avgDailyGain
|
||||
recording.AvgDailyGain = &avgDailyGain
|
||||
} else {
|
||||
updates["avg_daily_gain"] = gorm.Expr("NULL")
|
||||
recording.AvgDailyGain = nil
|
||||
avgDailyGain := 0.0
|
||||
updates["avg_daily_gain"] = avgDailyGain
|
||||
recording.AvgDailyGain = &avgDailyGain
|
||||
}
|
||||
|
||||
if usageInGrams > 0 && totalChick > 0 {
|
||||
|
||||
@@ -19,8 +19,9 @@ type (
|
||||
}
|
||||
|
||||
Egg struct {
|
||||
ProductWarehouseId uint `json:"product_warehouse_id" validate:"required,number,min=1"`
|
||||
Qty int `json:"qty" validate:"required,number,min=0"`
|
||||
ProductWarehouseId uint `json:"product_warehouse_id" validate:"required,number,min=1"`
|
||||
Qty int `json:"qty" validate:"required,number,min=0"`
|
||||
Weight *float64 `json:"weight,omitempty" validate:"omitempty,gte=0"`
|
||||
}
|
||||
)
|
||||
|
||||
@@ -45,16 +46,6 @@ type Query struct {
|
||||
ProjectFlockKandangId uint `query:"project_flock_kandang_id" validate:"omitempty,number,min=1"`
|
||||
}
|
||||
|
||||
type EggGrading struct {
|
||||
RecordingEggId uint `json:"recording_egg_id" validate:"required,number,min=1"`
|
||||
Grade string `json:"grade" validate:"required"`
|
||||
Qty float64 `json:"qty" validate:"required,gte=0"`
|
||||
}
|
||||
|
||||
type SubmitGrading struct {
|
||||
EggsGrading []EggGrading `json:"eggs_grading" validate:"required,dive"`
|
||||
}
|
||||
|
||||
type Approve struct {
|
||||
Action string `json:"action" validate:"required_strict"`
|
||||
ApprovableIds []uint `json:"approvable_ids" validate:"required_strict,min=1,dive,gt=0"`
|
||||
|
||||
Reference in New Issue
Block a user