mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-24 23:35:43 +00:00
Merge branch 'development-before-sso' of https://gitlab.com/mbugroup/lti-api into refactor-to-serve/with-middleware
This commit is contained in:
@@ -146,6 +146,60 @@ 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)
|
||||
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
|
||||
}
|
||||
|
||||
results, err := u.RecordingService.Approval(c, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
data interface{}
|
||||
message = "Submit recording approvals successfully"
|
||||
)
|
||||
|
||||
if len(results) == 1 {
|
||||
message = "Submit recording approval successfully"
|
||||
data = dto.ToRecordingDetailDTO(results[0])
|
||||
} else {
|
||||
data = dto.ToRecordingListDTOs(results)
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).
|
||||
JSON(response.Success{
|
||||
Code: fiber.StatusOK,
|
||||
Status: "success",
|
||||
Message: message,
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
|
||||
func (u *RecordingController) DeleteOne(c *fiber.Ctx) error {
|
||||
param := c.Params("id")
|
||||
|
||||
|
||||
@@ -1,30 +1,35 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
approvalDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/approvals/dto"
|
||||
userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||
approvalutils "gitlab.com/mbugroup/lti-api.git/internal/utils/approvals"
|
||||
)
|
||||
|
||||
// === DTO Structs ===
|
||||
|
||||
type RecordingBaseDTO struct {
|
||||
Id uint `json:"id"`
|
||||
ProjectFlockKandangId uint `json:"project_flock_kandang_id"`
|
||||
RecordDatetime time.Time `json:"record_datetime"`
|
||||
RecordDate *time.Time `json:"record_date,omitempty"`
|
||||
Ontime bool `json:"ontime"`
|
||||
Day *int `json:"day,omitempty"`
|
||||
TotalDepletion *int `json:"total_depletion,omitempty"`
|
||||
CumDepletionRate *float64 `json:"cum_depletion_rate,omitempty"`
|
||||
DailyGain *float64 `json:"daily_gain,omitempty"`
|
||||
AvgDailyGain *float64 `json:"avg_daily_gain,omitempty"`
|
||||
CumIntake *int64 `json:"cum_intake,omitempty"`
|
||||
FcrValue *float64 `json:"fcr_value,omitempty"`
|
||||
TotalChick *int64 `json:"total_chick,omitempty"`
|
||||
DailyDepletionRate *float64 `json:"daily_depletion_rate,omitempty"`
|
||||
CumDepletion *int `json:"cum_depletion,omitempty"`
|
||||
Id uint `json:"id"`
|
||||
ProjectFlockKandangId uint `json:"project_flock_kandang_id"`
|
||||
RecordDatetime time.Time `json:"record_datetime"`
|
||||
Day *int `json:"day,omitempty"`
|
||||
ProjectFlockCategory *string `json:"project_flock_category,omitempty"`
|
||||
TotalDepletionQty *float64 `json:"total_depletion_qty,omitempty"`
|
||||
CumDepletionRate *float64 `json:"cum_depletion_rate,omitempty"`
|
||||
DailyGain *float64 `json:"daily_gain,omitempty"`
|
||||
AvgDailyGain *float64 `json:"avg_daily_gain,omitempty"`
|
||||
CumIntake *int `json:"cum_intake,omitempty"`
|
||||
FcrValue *float64 `json:"fcr_value,omitempty"`
|
||||
TotalChickQty *float64 `json:"total_chick_qty,omitempty"`
|
||||
Approval approvalDTO.ApprovalBaseDTO `json:"approval"`
|
||||
EggGradingStatus *string `json:"egg_grading_status,omitempty"`
|
||||
EggGradingPendingQty *int `json:"egg_grading_pending_qty,omitempty"`
|
||||
EggGradingCompletedQty *int `json:"egg_grading_completed_qty,omitempty"`
|
||||
}
|
||||
|
||||
type RecordingListDTO struct {
|
||||
@@ -39,30 +44,35 @@ type RecordingDetailDTO struct {
|
||||
BodyWeights []RecordingBodyWeightDTO `json:"body_weights"`
|
||||
Depletions []RecordingDepletionDTO `json:"depletions"`
|
||||
Stocks []RecordingStockDTO `json:"stocks"`
|
||||
Eggs []RecordingEggDTO `json:"eggs"`
|
||||
}
|
||||
|
||||
type RecordingBodyWeightDTO struct {
|
||||
Weight float64 `json:"weight"`
|
||||
Qty int `json:"qty"`
|
||||
Notes *string `json:"notes,omitempty"`
|
||||
AvgWeight float64 `json:"avg_weight"`
|
||||
Qty float64 `json:"qty"`
|
||||
TotalWeight float64 `json:"total_weight"`
|
||||
}
|
||||
|
||||
type RecordingDepletionDTO struct {
|
||||
ProductWarehouseId uint `json:"product_warehouse_id"`
|
||||
Total int64 `json:"total"`
|
||||
Notes *string `json:"notes,omitempty"`
|
||||
Qty float64 `json:"qty"`
|
||||
ProductWarehouse *RecordingProductWarehouseDTO `json:"product_warehouse,omitempty"`
|
||||
}
|
||||
|
||||
type RecordingStockDTO struct {
|
||||
ProductWarehouseId uint `json:"product_warehouse_id"`
|
||||
Increase *float64 `json:"increase,omitempty"`
|
||||
Decrease *float64 `json:"decrease,omitempty"`
|
||||
UsageAmount *int64 `json:"usage_amount,omitempty"`
|
||||
Notes *string `json:"notes,omitempty"`
|
||||
UsageAmount *float64 `json:"usage_amount,omitempty"`
|
||||
PendingQty *float64 `json:"pending_qty,omitempty"`
|
||||
ProductWarehouse *RecordingProductWarehouseDTO `json:"product_warehouse,omitempty"`
|
||||
}
|
||||
|
||||
type RecordingEggDTO struct {
|
||||
ProductWarehouseId uint `json:"product_warehouse_id"`
|
||||
Qty int `json:"qty"`
|
||||
ProductWarehouse *RecordingProductWarehouseDTO `json:"product_warehouse,omitempty"`
|
||||
Gradings []RecordingEggGradingDTO `json:"gradings,omitempty"`
|
||||
}
|
||||
|
||||
type RecordingProductWarehouseDTO struct {
|
||||
Id uint `json:"id"`
|
||||
ProductId uint `json:"product_id"`
|
||||
@@ -71,36 +81,47 @@ type RecordingProductWarehouseDTO struct {
|
||||
WarehouseName string `json:"warehouse_name"`
|
||||
}
|
||||
|
||||
type RecordingEggGradingDTO struct {
|
||||
Grade string `json:"grade,omitempty"`
|
||||
Qty float64 `json:"qty"`
|
||||
}
|
||||
|
||||
// === Mapper Functions ===
|
||||
|
||||
func ToRecordingBaseDTO(e entity.Recording) RecordingBaseDTO {
|
||||
recordDate := e.RecordDate
|
||||
if recordDate == nil {
|
||||
rd := time.Date(
|
||||
e.RecordDatetime.Year(),
|
||||
e.RecordDatetime.Month(),
|
||||
e.RecordDatetime.Day(),
|
||||
0, 0, 0, 0,
|
||||
e.RecordDatetime.Location(),
|
||||
)
|
||||
recordDate = &rd
|
||||
var projectFlockCategory *string
|
||||
if e.ProjectFlockKandang != nil && e.ProjectFlockKandang.ProjectFlock.Id != 0 {
|
||||
category := e.ProjectFlockKandang.ProjectFlock.Category
|
||||
if category != "" {
|
||||
projectFlockCategory = &category
|
||||
}
|
||||
}
|
||||
|
||||
latestApproval := defaultRecordingLatestApproval(e)
|
||||
if e.LatestApproval != nil {
|
||||
snapshot := approvalDTO.ToApprovalDTO(*e.LatestApproval)
|
||||
latestApproval = snapshot
|
||||
}
|
||||
|
||||
gradingStatus, gradingPending, gradingCompleted := computeEggGradingStatus(e)
|
||||
|
||||
return RecordingBaseDTO{
|
||||
Id: e.Id,
|
||||
ProjectFlockKandangId: e.ProjectFlockKandangId,
|
||||
RecordDatetime: e.RecordDatetime,
|
||||
RecordDate: recordDate,
|
||||
Ontime: e.Ontime == 1,
|
||||
Day: e.Day,
|
||||
TotalDepletion: e.TotalDepletion,
|
||||
CumDepletionRate: e.CumDepletionRate,
|
||||
DailyGain: e.DailyGain,
|
||||
AvgDailyGain: e.AvgDailyGain,
|
||||
CumIntake: e.CumIntake,
|
||||
FcrValue: e.FcrValue,
|
||||
TotalChick: e.TotalChick,
|
||||
DailyDepletionRate: e.DailyDepletionRate,
|
||||
CumDepletion: e.CumDepletion,
|
||||
Id: e.Id,
|
||||
ProjectFlockKandangId: e.ProjectFlockKandangId,
|
||||
RecordDatetime: e.RecordDatetime,
|
||||
Day: e.Day,
|
||||
ProjectFlockCategory: projectFlockCategory,
|
||||
TotalDepletionQty: e.TotalDepletionQty,
|
||||
CumDepletionRate: e.CumDepletionRate,
|
||||
DailyGain: e.DailyGain,
|
||||
AvgDailyGain: e.AvgDailyGain,
|
||||
CumIntake: e.CumIntake,
|
||||
FcrValue: e.FcrValue,
|
||||
TotalChickQty: e.TotalChickQty,
|
||||
Approval: latestApproval,
|
||||
EggGradingStatus: gradingStatus,
|
||||
EggGradingPendingQty: gradingPending,
|
||||
EggGradingCompletedQty: gradingCompleted,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,6 +154,7 @@ func ToRecordingDetailDTO(e entity.Recording) RecordingDetailDTO {
|
||||
BodyWeights: ToRecordingBodyWeightDTOs(e.BodyWeights),
|
||||
Depletions: ToRecordingDepletionDTOs(e.Depletions),
|
||||
Stocks: ToRecordingStockDTOs(e.Stocks),
|
||||
Eggs: ToRecordingEggDTOs(e.Eggs),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,9 +162,9 @@ func ToRecordingBodyWeightDTOs(bodyWeights []entity.RecordingBW) []RecordingBody
|
||||
result := make([]RecordingBodyWeightDTO, len(bodyWeights))
|
||||
for i, bw := range bodyWeights {
|
||||
result[i] = RecordingBodyWeightDTO{
|
||||
Weight: bw.Weight,
|
||||
Qty: bw.Qty,
|
||||
Notes: bw.Notes,
|
||||
AvgWeight: bw.AvgWeight,
|
||||
Qty: bw.Qty,
|
||||
TotalWeight: bw.TotalWeight,
|
||||
}
|
||||
}
|
||||
return result
|
||||
@@ -153,8 +175,7 @@ func ToRecordingDepletionDTOs(depletions []entity.RecordingDepletion) []Recordin
|
||||
for i, d := range depletions {
|
||||
result[i] = RecordingDepletionDTO{
|
||||
ProductWarehouseId: d.ProductWarehouseId,
|
||||
Total: d.Total,
|
||||
Notes: d.Notes,
|
||||
Qty: d.Qty,
|
||||
ProductWarehouse: toRecordingProductWarehouseDTO(&d.ProductWarehouse),
|
||||
}
|
||||
}
|
||||
@@ -166,16 +187,43 @@ func ToRecordingStockDTOs(stocks []entity.RecordingStock) []RecordingStockDTO {
|
||||
for i, s := range stocks {
|
||||
result[i] = RecordingStockDTO{
|
||||
ProductWarehouseId: s.ProductWarehouseId,
|
||||
Increase: s.Increase,
|
||||
Decrease: s.Decrease,
|
||||
UsageAmount: s.UsageAmount,
|
||||
Notes: s.Notes,
|
||||
UsageAmount: s.UsageQty,
|
||||
PendingQty: s.PendingQty,
|
||||
ProductWarehouse: toRecordingProductWarehouseDTO(&s.ProductWarehouse),
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func ToRecordingEggDTOs(eggs []entity.RecordingEgg) []RecordingEggDTO {
|
||||
result := make([]RecordingEggDTO, len(eggs))
|
||||
for i, egg := range eggs {
|
||||
result[i] = RecordingEggDTO{
|
||||
ProductWarehouseId: egg.ProductWarehouseId,
|
||||
Qty: egg.Qty,
|
||||
ProductWarehouse: toRecordingProductWarehouseDTO(&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 toRecordingProductWarehouseDTO(pw *entity.ProductWarehouse) *RecordingProductWarehouseDTO {
|
||||
if pw == nil || pw.Id == 0 {
|
||||
return nil
|
||||
@@ -196,3 +244,81 @@ func toRecordingProductWarehouseDTO(pw *entity.ProductWarehouse) *RecordingProdu
|
||||
|
||||
return &dto
|
||||
}
|
||||
|
||||
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.ApprovalBaseDTO {
|
||||
result := approvalDTO.ApprovalBaseDTO{}
|
||||
|
||||
step := utils.RecordingStepPengajuan
|
||||
result.StepNumber = uint16(step)
|
||||
if label, ok := approvalutils.ApprovalStepName(utils.ApprovalWorkflowRecording, step); ok {
|
||||
result.StepName = label
|
||||
} else if label, ok := utils.RecordingApprovalSteps[step]; ok {
|
||||
result.StepName = label
|
||||
}
|
||||
|
||||
if e.CreatedUser != nil && e.CreatedUser.Id != 0 {
|
||||
result.ActionBy = userDTO.ToUserBaseDTO(*e.CreatedUser)
|
||||
} else if e.CreatedBy != 0 {
|
||||
result.ActionBy = userDTO.UserBaseDTO{
|
||||
Id: e.CreatedBy,
|
||||
IdUser: int64(e.CreatedBy),
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
package recordings
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"gorm.io/gorm"
|
||||
|
||||
commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
|
||||
rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
|
||||
rProjectFlockKandang "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"
|
||||
rRecording "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories"
|
||||
sRecording "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/services"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||
|
||||
rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories"
|
||||
sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||
@@ -18,11 +23,26 @@ type RecordingModule struct{}
|
||||
|
||||
func (RecordingModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *validator.Validate) {
|
||||
recordingRepo := rRecording.NewRecordingRepository(db)
|
||||
projectFlockKandangRepo := rProjectFlockKandang.NewProjectFlockKandangRepository(db)
|
||||
projectFlockKandangRepo := rProjectFlock.NewProjectFlockKandangRepository(db)
|
||||
projectFlockPopulationRepo := rProjectFlock.NewProjectFlockPopulationRepository(db)
|
||||
productWarehouseRepo := rProductWarehouse.NewProductWarehouseRepository(db)
|
||||
approvalRepo := commonRepo.NewApprovalRepository(db)
|
||||
approvalService := commonSvc.NewApprovalService(approvalRepo)
|
||||
if err := approvalService.RegisterWorkflowSteps(utils.ApprovalWorkflowRecording, utils.RecordingApprovalSteps); err != nil {
|
||||
panic(fmt.Sprintf("failed to register recording approval workflow: %v", err))
|
||||
}
|
||||
|
||||
userRepo := rUser.NewUserRepository(db)
|
||||
|
||||
recordingService := sRecording.NewRecordingService(recordingRepo, projectFlockKandangRepo, productWarehouseRepo, validate)
|
||||
recordingService := sRecording.NewRecordingService(
|
||||
recordingRepo,
|
||||
projectFlockKandangRepo,
|
||||
productWarehouseRepo,
|
||||
projectFlockPopulationRepo,
|
||||
approvalRepo,
|
||||
approvalService,
|
||||
validate,
|
||||
)
|
||||
userService := sUser.NewUserService(userRepo, validate)
|
||||
|
||||
RecordingRoutes(router, userService, recordingService)
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package recordings
|
||||
|
||||
const (
|
||||
PermissionRecordingRead = "recording.read"
|
||||
PermissionRecordingCreate = "recording.write"
|
||||
PermissionRecordingUpdate = "recording.update"
|
||||
PermissionRecordingDelete = "recording.delete"
|
||||
)
|
||||
@@ -1,10 +1,12 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
@@ -22,11 +24,22 @@ type RecordingRepository interface {
|
||||
|
||||
CreateStocks(tx *gorm.DB, stocks []entity.RecordingStock) error
|
||||
DeleteStocks(tx *gorm.DB, recordingID uint) error
|
||||
ListStocks(tx *gorm.DB, recordingID uint) ([]entity.RecordingStock, error)
|
||||
|
||||
CreateDepletions(tx *gorm.DB, depletions []entity.RecordingDepletion) error
|
||||
DeleteDepletions(tx *gorm.DB, recordingID uint) error
|
||||
ListDepletions(tx *gorm.DB, recordingID uint) ([]entity.RecordingDepletion, error)
|
||||
|
||||
SumRecordingDepletions(tx *gorm.DB, recordingID uint) (int64, error)
|
||||
CreateEggs(tx *gorm.DB, eggs []entity.RecordingEgg) error
|
||||
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)
|
||||
|
||||
SumRecordingDepletions(tx *gorm.DB, recordingID uint) (float64, error)
|
||||
FindPreviousRecording(tx *gorm.DB, projectFlockKandangId uint, currentDay int) (*entity.Recording, error)
|
||||
GetTotalChick(tx *gorm.DB, projectFlockKandangId uint) (int64, error)
|
||||
GetAverageBodyWeight(tx *gorm.DB, recordingID uint) (float64, error)
|
||||
@@ -58,13 +71,18 @@ func (r *RecordingRepositoryImpl) WithRelations(db *gorm.DB) *gorm.DB {
|
||||
Preload("Stocks").
|
||||
Preload("Stocks.ProductWarehouse").
|
||||
Preload("Stocks.ProductWarehouse.Product").
|
||||
Preload("Stocks.ProductWarehouse.Warehouse")
|
||||
Preload("Stocks.ProductWarehouse.Warehouse").
|
||||
Preload("Eggs").
|
||||
Preload("Eggs.ProductWarehouse").
|
||||
Preload("Eggs.ProductWarehouse.Product").
|
||||
Preload("Eggs.ProductWarehouse.Warehouse").
|
||||
Preload("Eggs.GradingEggs")
|
||||
}
|
||||
|
||||
func (r *RecordingRepositoryImpl) GenerateNextDay(tx *gorm.DB, projectFlockKandangId uint) (int, error) {
|
||||
var days []int
|
||||
if err := tx.Model(&entity.Recording{}).
|
||||
Where("project_flock_id = ?", projectFlockKandangId).
|
||||
Where("project_flock_kandangs_id = ?", projectFlockKandangId).
|
||||
Where("day IS NOT NULL").
|
||||
Pluck("day", &days).Error; err != nil {
|
||||
return 0, err
|
||||
@@ -94,6 +112,14 @@ func (r *RecordingRepositoryImpl) DeleteStocks(tx *gorm.DB, recordingID uint) er
|
||||
return tx.Where("recording_id = ?", recordingID).Delete(&entity.RecordingStock{}).Error
|
||||
}
|
||||
|
||||
func (r *RecordingRepositoryImpl) ListStocks(tx *gorm.DB, recordingID uint) ([]entity.RecordingStock, error) {
|
||||
var items []entity.RecordingStock
|
||||
if err := tx.Where("recording_id = ?", recordingID).Find(&items).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (r *RecordingRepositoryImpl) CreateDepletions(tx *gorm.DB, depletions []entity.RecordingDepletion) error {
|
||||
if len(depletions) == 0 {
|
||||
return nil
|
||||
@@ -105,11 +131,100 @@ func (r *RecordingRepositoryImpl) DeleteDepletions(tx *gorm.DB, recordingID uint
|
||||
return tx.Where("recording_id = ?", recordingID).Delete(&entity.RecordingDepletion{}).Error
|
||||
}
|
||||
|
||||
func (r *RecordingRepositoryImpl) SumRecordingDepletions(tx *gorm.DB, recordingID uint) (int64, error) {
|
||||
var result int64
|
||||
func (r *RecordingRepositoryImpl) ListDepletions(tx *gorm.DB, recordingID uint) ([]entity.RecordingDepletion, error) {
|
||||
var items []entity.RecordingDepletion
|
||||
if err := tx.Where("recording_id = ?", recordingID).Find(&items).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (r *RecordingRepositoryImpl) CreateEggs(tx *gorm.DB, eggs []entity.RecordingEgg) error {
|
||||
if len(eggs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return tx.Create(&eggs).Error
|
||||
}
|
||||
|
||||
func (r *RecordingRepositoryImpl) DeleteEggs(tx *gorm.DB, recordingID uint) error {
|
||||
return tx.Where("recording_id = ?", recordingID).Delete(&entity.RecordingEgg{}).Error
|
||||
}
|
||||
|
||||
func (r *RecordingRepositoryImpl) ListEggs(tx *gorm.DB, recordingID uint) ([]entity.RecordingEgg, error) {
|
||||
var items []entity.RecordingEgg
|
||||
if err := tx.Where("recording_id = ?", recordingID).Find(&items).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (r *RecordingRepositoryImpl) GetRecordingEggByID(
|
||||
ctx context.Context,
|
||||
id uint,
|
||||
modifier func(*gorm.DB) *gorm.DB,
|
||||
) (*entity.RecordingEgg, error) {
|
||||
if id == 0 {
|
||||
return nil, gorm.ErrRecordNotFound
|
||||
}
|
||||
|
||||
db := r.DB()
|
||||
if modifier != nil {
|
||||
db = modifier(db)
|
||||
}
|
||||
|
||||
var egg entity.RecordingEgg
|
||||
query := db.WithContext(ctx).
|
||||
Preload("Recording").
|
||||
Preload("Recording.ProjectFlockKandang").
|
||||
Preload("Recording.ProjectFlockKandang.ProjectFlock").
|
||||
Preload("ProductWarehouse").
|
||||
Preload("GradingEggs").
|
||||
Where("id = ?", id)
|
||||
|
||||
if err := query.First(&egg).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
ref := recordTime.In(time.UTC)
|
||||
startOfDay := time.Date(ref.Year(), ref.Month(), ref.Day(), 0, 0, 0, 0, time.UTC)
|
||||
endOfDay := startOfDay.Add(24 * time.Hour)
|
||||
|
||||
var count int64
|
||||
err := r.DB().
|
||||
WithContext(ctx).
|
||||
Model(&entity.Recording{}).
|
||||
Where("project_flock_kandangs_id = ?", projectFlockKandangId).
|
||||
Where("record_datetime >= ? AND record_datetime < ?", startOfDay, endOfDay).
|
||||
Count(&count).Error
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
func (r *RecordingRepositoryImpl) SumRecordingDepletions(tx *gorm.DB, recordingID uint) (float64, error) {
|
||||
var result float64
|
||||
if err := tx.Model(&entity.RecordingDepletion{}).
|
||||
Where("recording_id = ?", recordingID).
|
||||
Select("COALESCE(SUM(total), 0)").
|
||||
Select("COALESCE(SUM(qty), 0)").
|
||||
Scan(&result).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -123,7 +238,7 @@ func (r *RecordingRepositoryImpl) FindPreviousRecording(tx *gorm.DB, projectFloc
|
||||
|
||||
var prev entity.Recording
|
||||
err := tx.
|
||||
Where("project_flock_id = ? AND day < ?", projectFlockKandangId, currentDay).
|
||||
Where("project_flock_kandangs_id = ? AND day < ?", projectFlockKandangId, currentDay).
|
||||
Where("day IS NOT NULL").
|
||||
Order("day DESC").
|
||||
Limit(1).
|
||||
@@ -159,7 +274,7 @@ func (r *RecordingRepositoryImpl) GetAverageBodyWeight(tx *gorm.DB, recordingID
|
||||
TotalQty float64
|
||||
}
|
||||
if err := tx.Model(&entity.RecordingBW{}).
|
||||
Select("COALESCE(SUM(weight * qty), 0) AS total_weight, COALESCE(SUM(qty), 0) AS total_qty").
|
||||
Select("COALESCE(SUM(total_weight), 0) AS total_weight, COALESCE(SUM(qty), 0) AS total_qty").
|
||||
Where("recording_id = ?", recordingID).
|
||||
Scan(&result).Error; err != nil {
|
||||
return 0, err
|
||||
@@ -172,13 +287,13 @@ func (r *RecordingRepositoryImpl) GetAverageBodyWeight(tx *gorm.DB, recordingID
|
||||
|
||||
func (r *RecordingRepositoryImpl) GetFeedUsageInGrams(tx *gorm.DB, recordingID uint) (float64, error) {
|
||||
var rows []struct {
|
||||
UsageAmount float64
|
||||
UomName string
|
||||
UsageQty float64
|
||||
UomName string
|
||||
}
|
||||
|
||||
if err := tx.
|
||||
Table("recording_stocks").
|
||||
Select("COALESCE(recording_stocks.usage_amount, 0) AS usage_amount, LOWER(uoms.name) AS uom_name").
|
||||
Select("COALESCE(recording_stocks.usage_qty, 0) AS usage_qty, LOWER(uoms.name) AS uom_name").
|
||||
Joins("JOIN product_warehouses ON product_warehouses.id = recording_stocks.product_warehouse_id").
|
||||
Joins("JOIN products ON products.id = product_warehouses.product_id").
|
||||
Joins("JOIN uoms ON uoms.id = products.uom_id").
|
||||
@@ -189,16 +304,16 @@ func (r *RecordingRepositoryImpl) GetFeedUsageInGrams(tx *gorm.DB, recordingID u
|
||||
|
||||
var total float64
|
||||
for _, row := range rows {
|
||||
if row.UsageAmount <= 0 {
|
||||
if row.UsageQty <= 0 {
|
||||
continue
|
||||
}
|
||||
switch strings.TrimSpace(row.UomName) {
|
||||
case "kilogram", "kg", "kilograms", "kilo":
|
||||
total += row.UsageAmount * 1000
|
||||
total += row.UsageQty * 1000
|
||||
case "gram", "g", "grams":
|
||||
total += row.UsageAmount
|
||||
total += row.UsageQty
|
||||
default:
|
||||
total += row.UsageAmount
|
||||
total += row.UsageQty
|
||||
}
|
||||
}
|
||||
return total, nil
|
||||
|
||||
@@ -18,7 +18,9 @@ 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)
|
||||
route.Delete("/:id", ctrl.DeleteOne)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,23 +2,25 @@ package validation
|
||||
|
||||
type (
|
||||
BodyWeight struct {
|
||||
Weight float64 `json:"weight" validate:"required"`
|
||||
Qty int `json:"qty" validate:"required,number,min=1"`
|
||||
Notes *string `json:"notes,omitempty" validate:"omitempty"`
|
||||
AvgWeight float64 `json:"avg_weight" validate:"required"`
|
||||
Qty float64 `json:"qty" validate:"required,gt=0"`
|
||||
TotalWeight *float64 `json:"total_weight,omitempty" validate:"omitempty,gt=0"`
|
||||
}
|
||||
|
||||
Stock struct {
|
||||
ProductWarehouseId uint `json:"product_warehouse_id" validate:"required,number,min=1"`
|
||||
Increase *float64 `json:"increase,omitempty" validate:"omitempty"`
|
||||
Decrease *float64 `json:"decrease,omitempty" validate:"omitempty"`
|
||||
UsageAmount *int64 `json:"usage_amount,omitempty" validate:"omitempty,min=0"`
|
||||
Notes *string `json:"notes,omitempty" validate:"omitempty"`
|
||||
Qty *float64 `json:"qty,omitempty" validate:"required_without=UsageAmount,gte=0"`
|
||||
PendingQty *float64 `json:"pending_qty,omitempty" validate:"omitempty,gte=0"`
|
||||
}
|
||||
|
||||
Depletion struct {
|
||||
ProductWarehouseId uint `json:"product_warehouse_id" validate:"required,number,min=1"`
|
||||
Total int64 `json:"total" validate:"required,number,min=0"`
|
||||
Notes *string `json:"notes,omitempty" validate:"omitempty"`
|
||||
Qty float64 `json:"qty" validate:"required,gte=0"`
|
||||
}
|
||||
|
||||
Egg struct {
|
||||
ProductWarehouseId uint `json:"product_warehouse_id" validate:"required,number,min=1"`
|
||||
Qty int `json:"qty" validate:"required,number,min=0"`
|
||||
}
|
||||
)
|
||||
|
||||
@@ -27,12 +29,14 @@ type Create struct {
|
||||
BodyWeights []BodyWeight `json:"body_weights,omitempty" validate:"omitempty,dive"`
|
||||
Stocks []Stock `json:"stocks,omitempty" validate:"omitempty,dive"`
|
||||
Depletions []Depletion `json:"depletions,omitempty" validate:"omitempty,dive"`
|
||||
Eggs []Egg `json:"eggs,omitempty" validate:"omitempty,dive"`
|
||||
}
|
||||
|
||||
type Update struct {
|
||||
BodyWeights []BodyWeight `json:"body_weights,omitempty" validate:"omitempty,dive"`
|
||||
Stocks []Stock `json:"stocks,omitempty" validate:"omitempty,dive"`
|
||||
Depletions []Depletion `json:"depletions,omitempty" validate:"omitempty,dive"`
|
||||
Eggs []Egg `json:"eggs,omitempty" validate:"omitempty,dive"`
|
||||
}
|
||||
|
||||
type Query struct {
|
||||
@@ -40,3 +44,19 @@ type Query struct {
|
||||
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"`
|
||||
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"`
|
||||
Notes *string `json:"notes,omitempty" validate:"omitempty,max=500"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user