mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
[FIX/BE-US] fix recording stock and dashboard filtering
This commit is contained in:
@@ -5,6 +5,8 @@ import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"gorm.io/gorm"
|
||||
|
||||
commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||
commonService "gitlab.com/mbugroup/lti-api.git/internal/common/service"
|
||||
rDashboard "gitlab.com/mbugroup/lti-api.git/internal/modules/dashboards/repositories"
|
||||
sDashboard "gitlab.com/mbugroup/lti-api.git/internal/modules/dashboards/services"
|
||||
|
||||
@@ -16,11 +18,12 @@ type DashboardModule struct{}
|
||||
|
||||
func (DashboardModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *validator.Validate) {
|
||||
dashboardRepo := rDashboard.NewDashboardRepository(db)
|
||||
hppCostRepo := commonRepo.NewHppCostRepository(db)
|
||||
userRepo := rUser.NewUserRepository(db)
|
||||
|
||||
dashboardService := sDashboard.NewDashboardService(dashboardRepo, validate)
|
||||
hppSvc := commonService.NewHppService(hppCostRepo)
|
||||
dashboardService := sDashboard.NewDashboardService(dashboardRepo, validate, hppSvc)
|
||||
userService := sUser.NewUserService(userRepo, validate)
|
||||
|
||||
DashboardRoutes(router, userService, dashboardService)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ type DashboardRepository interface {
|
||||
SumSellingPrice(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) (SellingPriceAggregate, error)
|
||||
SumEggProductionWeightGrams(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) (float64, error)
|
||||
SumEggProductionWeightKg(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) (float64, error)
|
||||
ListProjectFlockKandangIDsByEggProduction(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) ([]uint, error)
|
||||
GetRecordingWeeklyMetrics(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) ([]RecordingWeeklyMetric, error)
|
||||
GetUniformityWeeklyMetrics(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) ([]UniformityWeeklyMetric, error)
|
||||
GetStandardWeeklyMetrics(ctx context.Context, weeks []int, filters *validation.DashboardFilter) ([]StandardWeeklyMetric, error)
|
||||
|
||||
@@ -309,6 +309,27 @@ func (r *DashboardRepositoryImpl) SumEggProductionWeightKg(ctx context.Context,
|
||||
return grams / 1000, nil
|
||||
}
|
||||
|
||||
func (r *DashboardRepositoryImpl) ListProjectFlockKandangIDsByEggProduction(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) ([]uint, error) {
|
||||
var ids []uint
|
||||
|
||||
db := r.DB().WithContext(ctx).
|
||||
Table("recording_eggs AS re").
|
||||
Select("DISTINCT r.project_flock_kandangs_id").
|
||||
Joins("JOIN recordings AS r ON r.id = re.recording_id").
|
||||
Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = r.project_flock_kandangs_id").
|
||||
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
||||
Where("r.record_datetime >= ? AND r.record_datetime < ?", start, end).
|
||||
Where("r.deleted_at IS NULL")
|
||||
|
||||
db = applyDashboardFilters(db, filters)
|
||||
|
||||
if err := db.Scan(&ids).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
func (r *DashboardRepositoryImpl) GetFeedUsageByUom(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter) ([]FeedUsageByUom, error) {
|
||||
var rows []FeedUsageByUom
|
||||
|
||||
@@ -553,7 +574,7 @@ func (r *DashboardRepositoryImpl) GetComparisonWeeklyMetrics(ctx context.Context
|
||||
var rows []ComparisonWeeklyMetric
|
||||
db := r.DB().WithContext(ctx).
|
||||
Table("recordings AS r").
|
||||
Select(fmt.Sprintf(`((r.day - 1) / 7 + 1) AS week,
|
||||
Select(fmt.Sprintf(`(CASE WHEN r.day IS NULL OR r.day <= 0 THEN 1 ELSE ((r.day - 1) / 7 + 1) END) AS week,
|
||||
%s AS series_id,
|
||||
COALESCE(AVG(%s), 0) AS value`, seriesExpr, metricExpr)).
|
||||
Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = r.project_flock_kandangs_id").
|
||||
@@ -561,8 +582,7 @@ func (r *DashboardRepositoryImpl) GetComparisonWeeklyMetrics(ctx context.Context
|
||||
Joins("JOIN project_flocks AS pf ON pf.id = pfk.project_flock_id").
|
||||
Joins("JOIN locations AS loc ON loc.id = k.location_id").
|
||||
Where("r.record_datetime >= ? AND r.record_datetime < ?", start, end).
|
||||
Where("r.deleted_at IS NULL").
|
||||
Where("r.day IS NOT NULL AND r.day > 0")
|
||||
Where("r.deleted_at IS NULL")
|
||||
|
||||
db = applyDashboardFilters(db, filters)
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
commonService "gitlab.com/mbugroup/lti-api.git/internal/common/service"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/modules/dashboards/dto"
|
||||
repository "gitlab.com/mbugroup/lti-api.git/internal/modules/dashboards/repositories"
|
||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/dashboards/validations"
|
||||
@@ -27,13 +28,15 @@ type dashboardService struct {
|
||||
Log *logrus.Logger
|
||||
Validate *validator.Validate
|
||||
Repository repository.DashboardRepository
|
||||
HppSvc commonService.HppService
|
||||
}
|
||||
|
||||
func NewDashboardService(repo repository.DashboardRepository, validate *validator.Validate) DashboardService {
|
||||
func NewDashboardService(repo repository.DashboardRepository, validate *validator.Validate, hppSvc commonService.HppService) DashboardService {
|
||||
return &dashboardService{
|
||||
Log: utils.Log,
|
||||
Validate: validate,
|
||||
Repository: repo,
|
||||
HppSvc: hppSvc,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -592,13 +595,13 @@ func buildAggregateComparisonPercent(weeks []int, seriesRows []repository.Compar
|
||||
count++
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if result[week] == nil {
|
||||
result[week] = map[uint]float64{}
|
||||
}
|
||||
if count == 0 {
|
||||
result[week][series.Id] = 0
|
||||
continue
|
||||
}
|
||||
result[week][series.Id] = sum / count
|
||||
}
|
||||
}
|
||||
@@ -846,6 +849,21 @@ func percentDelta(current, last float64) float64 {
|
||||
}
|
||||
|
||||
func (s dashboardService) calculateHppGlobal(ctx context.Context, startDate, endExclusive, endDate time.Time, location *time.Location) (float64, float64, error) {
|
||||
if s.HppSvc != nil {
|
||||
currentHpp, err := s.hppGlobalForPeriod(ctx, startDate, endExclusive)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
lastMonthStart, lastMonthEndExclusive := monthRange(endDate.AddDate(0, -1, 0), location)
|
||||
lastHpp, err := s.hppGlobalForPeriod(ctx, lastMonthStart, lastMonthEndExclusive)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
return currentHpp, lastHpp, nil
|
||||
}
|
||||
|
||||
totalEggKg, err := s.Repository.SumEggProductionWeightKg(ctx, startDate, endExclusive, nil)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
@@ -878,6 +896,37 @@ func (s dashboardService) calculateHppGlobal(ctx context.Context, startDate, end
|
||||
return hppCurrent, hppLast, nil
|
||||
}
|
||||
|
||||
func (s dashboardService) hppGlobalForPeriod(ctx context.Context, startDate, endExclusive time.Time) (float64, error) {
|
||||
kandangIDs, err := s.Repository.ListProjectFlockKandangIDsByEggProduction(ctx, startDate, endExclusive, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(kandangIDs) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
endOfPeriod := endExclusive.Add(-time.Nanosecond)
|
||||
totalCost := 0.0
|
||||
totalWeightKg := 0.0
|
||||
for _, kandangID := range kandangIDs {
|
||||
hppCost, err := s.HppSvc.CalculateHppCost(kandangID, &endOfPeriod)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if hppCost == nil {
|
||||
continue
|
||||
}
|
||||
totalCost += hppCost.Estimation.Total
|
||||
totalWeightKg += hppCost.Estimation.Kg
|
||||
}
|
||||
|
||||
if totalWeightKg <= 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return totalCost / totalWeightKg, nil
|
||||
}
|
||||
|
||||
func (s dashboardService) calculateSellingPrice(ctx context.Context, endDate time.Time, location *time.Location) (float64, float64, error) {
|
||||
startPrevMonth, endPrevMonthExclusive := monthRange(endDate.AddDate(0, -1, 0), location)
|
||||
currentEndExclusive := endDate.AddDate(0, 0, 1)
|
||||
|
||||
@@ -287,6 +287,7 @@ func (s *recordingService) CreateOne(c *fiber.Ctx, req *validation.Create) (*ent
|
||||
}
|
||||
|
||||
mappedDepletions := recordingutil.MapDepletions(createdRecording.Id, req.Depletions)
|
||||
depletionDesired := resetDepletionQuantitiesForFIFO(mappedDepletions, s.FifoSvc != nil)
|
||||
if s.FifoSvc != nil && len(mappedDepletions) > 0 {
|
||||
sourceWarehouseID, err := s.resolvePopulationWarehouseID(ctx, req.ProjectFlockKandangId)
|
||||
if err != nil {
|
||||
@@ -301,6 +302,7 @@ func (s *recordingService) CreateOne(c *fiber.Ctx, req *validation.Create) (*ent
|
||||
return err
|
||||
}
|
||||
if s.FifoSvc != nil {
|
||||
applyDepletionDesiredQuantities(mappedDepletions, depletionDesired, true)
|
||||
note := fmt.Sprintf("Recording-Create#%d", createdRecording.Id)
|
||||
if err := s.consumeRecordingDepletions(ctx, tx, mappedDepletions, note, actorID); err != nil {
|
||||
return err
|
||||
@@ -465,6 +467,7 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin
|
||||
}
|
||||
|
||||
mappedDepletions := recordingutil.MapDepletions(recordingEntity.Id, req.Depletions)
|
||||
depletionDesired := resetDepletionQuantitiesForFIFO(mappedDepletions, s.FifoSvc != nil)
|
||||
if s.FifoSvc != nil && len(mappedDepletions) > 0 {
|
||||
sourceWarehouseID, err := s.resolvePopulationWarehouseID(ctx, recordingEntity.ProjectFlockKandangId)
|
||||
if err != nil {
|
||||
@@ -480,6 +483,7 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin
|
||||
}
|
||||
|
||||
if s.FifoSvc != nil {
|
||||
applyDepletionDesiredQuantities(mappedDepletions, depletionDesired, true)
|
||||
note := fmt.Sprintf("Recording-Edit#%d", recordingEntity.Id)
|
||||
if err := s.consumeRecordingDepletions(ctx, tx, mappedDepletions, note, actorID); err != nil {
|
||||
return err
|
||||
@@ -929,6 +933,9 @@ func (s *recordingService) consumeRecordingDepletions(
|
||||
|
||||
destDelta := depletion.Qty + depletion.PendingQty
|
||||
if depletion.ProductWarehouseId != 0 && destDelta > 0 && strings.TrimSpace(note) != "" && actorID != 0 {
|
||||
if depletion.ProductWarehouseId == sourceWarehouseID {
|
||||
continue
|
||||
}
|
||||
log := &entity.StockLog{
|
||||
ProductWarehouseId: depletion.ProductWarehouseId,
|
||||
CreatedBy: actorID,
|
||||
@@ -1066,6 +1073,9 @@ func (s *recordingService) releaseRecordingDepletions(
|
||||
|
||||
destDelta := depletion.Qty + depletion.PendingQty
|
||||
if depletion.ProductWarehouseId != 0 && destDelta > 0 && strings.TrimSpace(note) != "" && actorID != 0 {
|
||||
if depletion.ProductWarehouseId == sourceWarehouseID {
|
||||
continue
|
||||
}
|
||||
log := &entity.StockLog{
|
||||
ProductWarehouseId: depletion.ProductWarehouseId,
|
||||
CreatedBy: actorID,
|
||||
@@ -1235,6 +1245,11 @@ type desiredStock struct {
|
||||
Pending float64
|
||||
}
|
||||
|
||||
type desiredDepletion struct {
|
||||
Qty float64
|
||||
Pending float64
|
||||
}
|
||||
|
||||
func resetStockQuantitiesForFIFO(stocks []entity.RecordingStock, enabled bool) []desiredStock {
|
||||
desired := make([]desiredStock, len(stocks))
|
||||
for i := range stocks {
|
||||
@@ -1269,6 +1284,33 @@ func applyStockDesiredQuantities(stocks []entity.RecordingStock, desired []desir
|
||||
}
|
||||
}
|
||||
|
||||
func resetDepletionQuantitiesForFIFO(depletions []entity.RecordingDepletion, enabled bool) []desiredDepletion {
|
||||
desired := make([]desiredDepletion, len(depletions))
|
||||
for i := range depletions {
|
||||
desired[i].Qty = depletions[i].Qty
|
||||
desired[i].Pending = depletions[i].PendingQty
|
||||
if !enabled {
|
||||
continue
|
||||
}
|
||||
depletions[i].Qty = 0
|
||||
depletions[i].PendingQty = 0
|
||||
}
|
||||
return desired
|
||||
}
|
||||
|
||||
func applyDepletionDesiredQuantities(depletions []entity.RecordingDepletion, desired []desiredDepletion, enabled bool) {
|
||||
if !enabled {
|
||||
return
|
||||
}
|
||||
for i := range depletions {
|
||||
if i >= len(desired) {
|
||||
break
|
||||
}
|
||||
depletions[i].Qty = desired[i].Qty
|
||||
depletions[i].PendingQty = desired[i].Pending
|
||||
}
|
||||
}
|
||||
|
||||
func (s *recordingService) syncRecordingStocks(
|
||||
ctx context.Context,
|
||||
tx *gorm.DB,
|
||||
|
||||
Reference in New Issue
Block a user