From e0043544208300b401b5044916cc4fb6cd6e6f1a Mon Sep 17 00:00:00 2001 From: giovanni Date: Wed, 14 Jan 2026 16:20:59 +0700 Subject: [PATCH 1/5] adjust api production-result --- internal/modules/repports/module.go | 19 ++- .../production_result.repository.go | 23 ++++ .../repports/services/repport.service.go | 115 +++++++++++++++--- 3 files changed, 141 insertions(+), 16 deletions(-) diff --git a/internal/modules/repports/module.go b/internal/modules/repports/module.go index 61f37d4d..c6495a14 100644 --- a/internal/modules/repports/module.go +++ b/internal/modules/repports/module.go @@ -12,6 +12,7 @@ import ( expenseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/repositories" marketingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/repositories" + productionStandardRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/repositories" chickinRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/repositories" recordingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories" purchaseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/purchases/repositories" @@ -34,10 +35,26 @@ func (RepportModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate * debtSupplierRepository := repportRepo.NewDebtSupplierRepository(db) hppPerKandangRepository := repportRepo.NewHppPerKandangRepository(db) productionResultRepository := repportRepo.NewProductionResultRepository(db) + standardGrowthDetailRepository := productionStandardRepo.NewStandardGrowthDetailRepository(db) + productionStandardDetailRepository := productionStandardRepo.NewProductionStandardDetailRepository(db) userRepository := rUser.NewUserRepository(db) approvalSvc := approvalService.NewApprovalService(approvalRepository) - repportService := sRepport.NewRepportService(validate, expenseRealizationRepository, marketingDeliveryProductRepository, purchaseRepository, chickinRepository, recordingRepository, approvalSvc, purchaseSupplierRepository, debtSupplierRepository, hppPerKandangRepository, productionResultRepository) + repportService := sRepport.NewRepportService( + validate, + expenseRealizationRepository, + marketingDeliveryProductRepository, + purchaseRepository, + chickinRepository, + recordingRepository, + approvalSvc, + purchaseSupplierRepository, + debtSupplierRepository, + hppPerKandangRepository, + productionResultRepository, + standardGrowthDetailRepository, + productionStandardDetailRepository, + ) userService := sUser.NewUserService(userRepository, validate) RepportRoutes(router, userService, repportService) diff --git a/internal/modules/repports/repositories/production_result.repository.go b/internal/modules/repports/repositories/production_result.repository.go index 19007d0f..a8eccb91 100644 --- a/internal/modules/repports/repositories/production_result.repository.go +++ b/internal/modules/repports/repositories/production_result.repository.go @@ -11,6 +11,7 @@ import ( type ProductionResultRepository interface { GetRecordingsByProjectFlockKandang(ctx context.Context, projectFlockKandangID uint, offset, limit int) ([]entity.Recording, int64, error) + GetProductionStandardIDByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (uint, error) } type productionResultRepositoryImpl struct { @@ -76,3 +77,25 @@ func (r *productionResultRepositoryImpl) GetRecordingsByProjectFlockKandang( return recordings, total, nil } + +func (r *productionResultRepositoryImpl) GetProductionStandardIDByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (uint, error) { + if projectFlockKandangID == 0 { + return 0, nil + } + + var row struct { + ProductionStandardID uint `gorm:"column:production_standard_id"` + } + + err := r.db.WithContext(ctx). + Table("project_flock_kandangs pfk"). + Select("pf.production_standard_id"). + Joins("JOIN project_flocks pf ON pf.id = pfk.project_flock_id"). + Where("pfk.id = ?", projectFlockKandangID). + Take(&row).Error + if err != nil { + return 0, err + } + + return row.ProductionStandardID, nil +} diff --git a/internal/modules/repports/services/repport.service.go b/internal/modules/repports/services/repport.service.go index c4883b72..2577787a 100644 --- a/internal/modules/repports/services/repport.service.go +++ b/internal/modules/repports/services/repport.service.go @@ -2,6 +2,7 @@ package service import ( "context" + "errors" "fmt" "math" "sort" @@ -21,6 +22,7 @@ import ( areaDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto" supplierDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/suppliers/dto" warehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/dto" + productionStandardRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/repositories" chickinRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/repositories" recordingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories" purchaseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/purchases/repositories" @@ -55,6 +57,8 @@ type repportService struct { DebtSupplierRepo repportRepo.DebtSupplierRepository HppPerKandangRepo repportRepo.HppPerKandangRepository ProductionResultRepo repportRepo.ProductionResultRepository + StandardGrowthDetailRepo productionStandardRepository.StandardGrowthDetailRepository + ProductionStandardDetailRepo productionStandardRepository.ProductionStandardDetailRepository } type HppCostAggregate struct { @@ -78,6 +82,8 @@ func NewRepportService( debtSupplierRepo repportRepo.DebtSupplierRepository, hppPerKandangRepo repportRepo.HppPerKandangRepository, productionResultRepo repportRepo.ProductionResultRepository, + standardGrowthDetailRepo productionStandardRepository.StandardGrowthDetailRepository, + productionStandardDetailRepo productionStandardRepository.ProductionStandardDetailRepository, ) RepportService { return &repportService{ Log: utils.Log, @@ -92,6 +98,8 @@ func NewRepportService( DebtSupplierRepo: debtSupplierRepo, HppPerKandangRepo: hppPerKandangRepo, ProductionResultRepo: productionResultRepo, + StandardGrowthDetailRepo: standardGrowthDetailRepo, + ProductionStandardDetailRepo: productionStandardDetailRepo, } } @@ -285,6 +293,21 @@ func (s *repportService) GetProductionResult(ctx *fiber.Ctx, params *validation. weeklyResults := summarizeProductionResults(dailyResults, recordsPerWeek) + var productionStandardID uint + if s.ProductionResultRepo != nil { + standardID, err := s.ProductionResultRepo.GetProductionStandardIDByProjectFlockKandangID(ctx.Context(), params.ProjectFlockKandangID) + if err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, 0, err + } + } else { + productionStandardID = standardID + } + } + + standardDetailCache := make(map[int]*entity.ProductionStandardDetail) + growthDetailCache := make(map[int]*entity.StandardGrowthDetail) + var cumulativeButir int64 var cumulativeKg float64 for i := range weeklyResults { @@ -300,6 +323,66 @@ func (s *repportService) GetProductionResult(ctx *fiber.Ctx, params *validation. cumulativeKg += weeklyResults[i].KgJumlah weeklyResults[i].TotalKg = cumulativeKg + + if productionStandardID == 0 { + continue + } + + week := int(weeklyResults[i].Woa) + if s.ProductionStandardDetailRepo != nil { + detail, ok := standardDetailCache[week] + if !ok { + fetched, fetchErr := s.ProductionStandardDetailRepo.GetByStandardIDAndWeek(ctx.Context(), productionStandardID, week) + if fetchErr != nil { + if !errors.Is(fetchErr, gorm.ErrRecordNotFound) { + return nil, 0, fetchErr + } + } else { + detail = fetched + } + standardDetailCache[week] = detail + } + + if detail != nil { + if detail.TargetHenDayProduction != nil { + weeklyResults[i].HdStd = *detail.TargetHenDayProduction + } + if detail.TargetHenHouseProduction != nil { + weeklyResults[i].HhStd = *detail.TargetHenHouseProduction + } + if detail.TargetEggWeight != nil { + weeklyResults[i].EwStd = *detail.TargetEggWeight + } + if detail.TargetEggMass != nil { + weeklyResults[i].EmStd = *detail.TargetEggMass + } + if detail.StandardFCR != nil { + weeklyResults[i].FcrStd = *detail.StandardFCR + } + } + } + + if s.StandardGrowthDetailRepo != nil { + detail, ok := growthDetailCache[week] + if !ok { + fetched, fetchErr := s.StandardGrowthDetailRepo.GetByStandardIDAndWeek(ctx.Context(), productionStandardID, week) + if fetchErr != nil { + if !errors.Is(fetchErr, gorm.ErrRecordNotFound) { + return nil, 0, fetchErr + } + } else { + detail = fetched + } + growthDetailCache[week] = detail + } + + if detail != nil && detail.FeedIntake != nil { + weeklyResults[i].FiStd = *detail.FeedIntake + } + if detail != nil && detail.TargetMeanBw != nil { + weeklyResults[i].StdBw = *detail.TargetMeanBw + } + } } totalWeeks := int64(math.Ceil(float64(totalRecordings) / float64(recordsPerWeek))) @@ -314,17 +397,17 @@ func mapRecordingToProductionResultDTO(record entity.Recording) dto.ProductionRe StdUniformity: "90% up", DepKum: valueOrZero(record.CumDepletionRate), DepStd: valueOrZero(record.TotalDepletionQty), + Hd: valueOrZero(record.HenDay), + Fi: valueOrZero(record.FeedIntake), Fcr: valueOrZero(record.FcrValue), - Hh: valueOrZero(record.TotalChickQty), + Hh: valueOrZero(record.HenHouse), + Em: valueOrZero(record.EggMass), + Ew: valueOrZero(record.EggWeight), } if record.Day != nil { result.Woa = float64(*record.Day) } - if record.CumIntake != nil { - result.Fi = float64(*record.CumIntake) - } - // avgWeight := calculateAverageBodyWeight(record.BodyWeights) avgWeight := 1.0 if avgWeight > 0 { @@ -351,8 +434,6 @@ func mapRecordingToProductionResultDTO(record entity.Recording) dto.ProductionRe result.PersenPutih = roundFloat((float64(result.ButiranPutih)/total)*100, 2) result.PersenRetak = roundFloat((float64(result.ButiranRetak)/total)*100, 2) result.PersenPecah = roundFloat((float64(result.ButiranPecah)/total)*100, 2) - result.Ew = (eggSummary.TotalKg * 1000) / total - result.Em = eggSummary.TotalKg } return result @@ -464,13 +545,13 @@ func summarizeProductionResults(daily []dto.ProductionResultDTO, groupSize int) if end > len(daily) { end = len(daily) } - result = append(result, aggregateProductionResultGroup(daily[i:end])) + result = append(result, aggregateProductionResultGroup(daily[i:end], groupSize)) } return result } -func aggregateProductionResultGroup(group []dto.ProductionResultDTO) dto.ProductionResultDTO { +func aggregateProductionResultGroup(group []dto.ProductionResultDTO, groupSize int) dto.ProductionResultDTO { count := len(group) if count == 0 { return dto.ProductionResultDTO{} @@ -542,6 +623,10 @@ func aggregateProductionResultGroup(group []dto.ProductionResultDTO) dto.Product if divider == 0 { divider = 1 } + weeklyDivider := float64(groupSize) + if weeklyDivider == 0 { + weeklyDivider = divider + } agg.Bw = sumBw / divider agg.StdBw = sumStdBw / divider @@ -570,17 +655,17 @@ func aggregateProductionResultGroup(group []dto.ProductionResultDTO) dto.Product agg.PersenPecah = roundFloat(sumPersenPecah/percentDivider, 2) } - agg.Hd = sumHd / divider + agg.Hd = roundFloat(sumHd/weeklyDivider, 2) agg.HdStd = sumHdStd / divider - agg.Fi = sumFi / divider + agg.Fi = roundFloat(sumFi/weeklyDivider, 2) agg.FiStd = sumFiStd / divider - agg.Em = sumEm / divider + agg.Em = group[count-1].Em agg.EmStd = sumEmStd / divider - agg.Ew = sumEw / divider + agg.Ew = group[count-1].Ew agg.EwStd = sumEwStd / divider - agg.Fcr = sumFcr / divider + agg.Fcr = roundFloat(sumFcr/weeklyDivider, 2) agg.FcrStd = sumFcrStd / divider - agg.Hh = sumHh / divider + agg.Hh = roundFloat(sumHh/weeklyDivider, 2) agg.HhStd = sumHhStd / divider return agg From 37c26d58774409c5feac80f757ba799fe2f86508 Mon Sep 17 00:00:00 2001 From: giovanni Date: Thu, 15 Jan 2026 10:45:13 +0700 Subject: [PATCH 2/5] add daily checklist permission --- internal/middleware/permissions.go | 17 +++++++++-- internal/modules/daily-checklists/route.go | 28 +++++++++---------- .../modules/master/config-checklists/route.go | 10 +++---- internal/modules/master/employees/route.go | 10 +++---- .../modules/master/phase-activities/route.go | 10 +++---- internal/modules/master/phasess/route.go | 10 +++---- 6 files changed, 49 insertions(+), 36 deletions(-) diff --git a/internal/middleware/permissions.go b/internal/middleware/permissions.go index 5d5290d3..b5d3c727 100644 --- a/internal/middleware/permissions.go +++ b/internal/middleware/permissions.go @@ -1,8 +1,9 @@ package middleware -const( +const ( P_DashboardGetAll = "lti.dashboard.list" ) + // project-flock const ( P_ProjectFlockKandangsClosing = "lti.production.project_flock_kandangs.closing" @@ -151,7 +152,7 @@ const ( P_ProductsCreateOne = "lti.master.products.create" P_ProductsUpdateOne = "lti.master.products.update" P_ProductsDeleteOne = "lti.master.products.delete" - + P_SuppliersGetAll = "lti.master.suppliers.list" P_SuppliersGetOne = "lti.master.suppliers.detail" P_SuppliersCreateOne = "lti.master.suppliers.create" @@ -238,3 +239,15 @@ const ( P_UserGetAll = "lti.users.list" P_UserGetOne = "lti.users.detail" ) + +// daily-checklist +const ( + P_DailyChecklistDashboardList = "lti.daily_checklist.dashboard.list" + P_DailyChecklistCreateOne = "lti.daily_checklist.create" + P_DailyChecklistGetAll = "lti.daily_checklist.list" + P_DailyChecklistGetOne = "lti.daily_checklist.detail" + P_DailyChecklistReports = "lti.daily_checklist.reports" + P_DailyChecklistEmployee = "lti.daily_checklist.master_data.employee" + P_DailyChecklistActivity = "lti.daily_checklist.master_data.activity" + P_DailyChecklistActivityConfig = "lti.daily_checklist.master_data.configuration" +) diff --git a/internal/modules/daily-checklists/route.go b/internal/modules/daily-checklists/route.go index 9e576a05..0927486a 100644 --- a/internal/modules/daily-checklists/route.go +++ b/internal/modules/daily-checklists/route.go @@ -15,49 +15,49 @@ func DailyChecklistRoutes(v1 fiber.Router, u user.UserService, s dailyChecklist. route := v1.Group("/daily-checklists") route.Use(m.Auth(u)) - route.Get("/", ctrl.GetAll) - route.Get("/report", ctrl.GetReport) + route.Get("/", m.RequirePermissions(m.P_DailyChecklistGetAll), ctrl.GetAll) + route.Get("/report", m.RequirePermissions(m.P_DailyChecklistReports), ctrl.GetReport) - route.Get("/summary", ctrl.GetSummary) + route.Get("/summary", m.RequirePermissions(m.P_DailyChecklistDashboardList), ctrl.GetSummary) - route.Get("/report", ctrl.GetReport) + // route.Get("/report", ctrl.GetReport) // upsert daily checklist - route.Post("/", ctrl.CreateOne) + route.Post("/", m.RequirePermissions(m.P_DailyChecklistCreateOne), ctrl.CreateOne) // get detail data daily checklist by id - route.Get("/relation/:idDailyChecklist", ctrl.GetOne) + route.Get("/relation/:idDailyChecklist", m.RequirePermissions(m.P_DailyChecklistGetOne), ctrl.GetOne) // get phases by daily checklist id - route.Get("/phase/:idDailyChecklist", ctrl.GetPhaseByIdChecklist) + route.Get("/phase/:idDailyChecklist", m.RequirePermissions(m.P_DailyChecklistCreateOne), ctrl.GetPhaseByIdChecklist) // create task /* ketika add phase */ - route.Post("/phase/:idDailyChecklist", ctrl.CreateDailyChecklistPhase) + route.Post("/phase/:idDailyChecklist", m.RequirePermissions(m.P_DailyChecklistCreateOne), ctrl.CreateDailyChecklistPhase) // create assigment /* ketika add ABK */ - route.Post("/assignment/:idDailyChecklist", ctrl.CreateAssignment) + route.Post("/assignment/:idDailyChecklist", m.RequirePermissions(m.P_DailyChecklistCreateOne), ctrl.CreateAssignment) // remove assignment /* ketika remove ABK */ - route.Delete("/:idDailyChecklist/assignments/:idEmployee", ctrl.RemoveAssignment) + route.Delete("/:idDailyChecklist/assignments/:idEmployee", m.RequirePermissions(m.P_DailyChecklistCreateOne), ctrl.RemoveAssignment) //get all tasks - route.Get("/tasks", ctrl.GetAllTasks) + route.Get("/tasks", m.RequirePermissions(m.P_DailyChecklistCreateOne), ctrl.GetAllTasks) // update assignment /* ketika check dan uncheck tugas oleh ABK */ - route.Post("/assignment", ctrl.UpdateAssignment) + route.Post("/assignment", m.RequirePermissions(m.P_DailyChecklistCreateOne), ctrl.UpdateAssignment) - route.Patch("/:idDailyChecklist", ctrl.UpdateOne) - route.Delete("/:idDailyChecklist", ctrl.DeleteOne) + route.Patch("/:idDailyChecklist", m.RequirePermissions(m.P_DailyChecklistCreateOne), ctrl.UpdateOne) + route.Delete("/:idDailyChecklist", m.RequirePermissions(m.P_DailyChecklistCreateOne), ctrl.DeleteOne) } diff --git a/internal/modules/master/config-checklists/route.go b/internal/modules/master/config-checklists/route.go index 1b590067..a7e09500 100644 --- a/internal/modules/master/config-checklists/route.go +++ b/internal/modules/master/config-checklists/route.go @@ -15,9 +15,9 @@ func ConfigChecklistRoutes(v1 fiber.Router, u user.UserService, s configChecklis route := v1.Group("/config-checklists") route.Use(m.Auth(u)) - route.Get("/", ctrl.GetAll) - route.Post("/", ctrl.CreateOne) - route.Get("/:id", ctrl.GetOne) - route.Patch("/:id", ctrl.UpdateOne) - route.Delete("/:id", ctrl.DeleteOne) + route.Get("/", m.RequirePermissions(m.P_DailyChecklistActivityConfig), ctrl.GetAll) + route.Post("/", m.RequirePermissions(m.P_DailyChecklistActivityConfig), ctrl.CreateOne) + route.Get("/:id", m.RequirePermissions(m.P_DailyChecklistActivityConfig), ctrl.GetOne) + route.Patch("/:id", m.RequirePermissions(m.P_DailyChecklistActivityConfig), ctrl.UpdateOne) + route.Delete("/:id", m.RequirePermissions(m.P_DailyChecklistActivityConfig), ctrl.DeleteOne) } diff --git a/internal/modules/master/employees/route.go b/internal/modules/master/employees/route.go index 53974814..08fb4870 100644 --- a/internal/modules/master/employees/route.go +++ b/internal/modules/master/employees/route.go @@ -15,9 +15,9 @@ func EmployeesRoutes(v1 fiber.Router, u user.UserService, s employees.EmployeesS route := v1.Group("/employees") route.Use(m.Auth(u)) - route.Get("/", ctrl.GetAll) - route.Post("/", ctrl.CreateOne) - route.Get("/:id", ctrl.GetOne) - route.Patch("/:id", ctrl.UpdateOne) - route.Delete("/:id", ctrl.DeleteOne) + route.Get("/", m.RequirePermissions(m.P_DailyChecklistEmployee), ctrl.GetAll) + route.Post("/", m.RequirePermissions(m.P_DailyChecklistEmployee), ctrl.CreateOne) + route.Get("/:id", m.RequirePermissions(m.P_DailyChecklistEmployee), ctrl.GetOne) + route.Patch("/:id", m.RequirePermissions(m.P_DailyChecklistEmployee), ctrl.UpdateOne) + route.Delete("/:id", m.RequirePermissions(m.P_DailyChecklistEmployee), ctrl.DeleteOne) } diff --git a/internal/modules/master/phase-activities/route.go b/internal/modules/master/phase-activities/route.go index 6fcef558..723fd7bd 100644 --- a/internal/modules/master/phase-activities/route.go +++ b/internal/modules/master/phase-activities/route.go @@ -15,9 +15,9 @@ func PhaseActivityRoutes(v1 fiber.Router, u user.UserService, s phaseActivity.Ph route := v1.Group("/phase-activities") route.Use(m.Auth(u)) - route.Get("/", ctrl.GetAll) - route.Post("/", ctrl.CreateOne) - route.Get("/:id", ctrl.GetOne) - route.Patch("/:id", ctrl.UpdateOne) - route.Delete("/:id", ctrl.DeleteOne) + route.Get("/", m.RequirePermissions(m.P_DailyChecklistActivity), ctrl.GetAll) + route.Post("/", m.RequirePermissions(m.P_DailyChecklistActivity), ctrl.CreateOne) + route.Get("/:id", m.RequirePermissions(m.P_DailyChecklistActivity), ctrl.GetOne) + route.Patch("/:id", m.RequirePermissions(m.P_DailyChecklistActivity), ctrl.UpdateOne) + route.Delete("/:id", m.RequirePermissions(m.P_DailyChecklistActivity), ctrl.DeleteOne) } diff --git a/internal/modules/master/phasess/route.go b/internal/modules/master/phasess/route.go index b4ca202d..1da6aeeb 100644 --- a/internal/modules/master/phasess/route.go +++ b/internal/modules/master/phasess/route.go @@ -15,9 +15,9 @@ func PhasesRoutes(v1 fiber.Router, u user.UserService, s phases.PhasesService) { route := v1.Group("/phases") route.Use(m.Auth(u)) - route.Get("/", ctrl.GetAll) - route.Post("/", ctrl.CreateOne) - route.Get("/:id", ctrl.GetOne) - route.Patch("/:id", ctrl.UpdateOne) - route.Delete("/:id", ctrl.DeleteOne) + route.Get("/", m.RequirePermissions(m.P_DailyChecklistActivity), ctrl.GetAll) + route.Post("/", m.RequirePermissions(m.P_DailyChecklistActivity), ctrl.CreateOne) + route.Get("/:id", m.RequirePermissions(m.P_DailyChecklistActivity), ctrl.GetOne) + route.Patch("/:id", m.RequirePermissions(m.P_DailyChecklistActivity), ctrl.UpdateOne) + route.Delete("/:id", m.RequirePermissions(m.P_DailyChecklistActivity), ctrl.DeleteOne) } From 7f2401311b3cc458e8b72aaad513b248a337197a Mon Sep 17 00:00:00 2001 From: ragilap Date: Thu, 15 Jan 2026 13:48:00 +0700 Subject: [PATCH 3/5] [FIX/BE-US] add response warehouse and project flock kandang --- ...115062032_add_fifo_recording_eggs.down.sql | 3 + ...60115062032_add_fifo_recording_eggs.up.sql | 7 ++ internal/entities/recording_egg.go | 2 + .../controllers/projectflock.controller.go | 7 ++ .../dto/projectflock_kandang.dto.go | 30 ++++---- .../services/projectflock.service.go | 26 +++++++ .../recordings/dto/recording.dto.go | 4 +- .../modules/production/recordings/module.go | 16 ++++ .../recordings/services/recording.service.go | 74 ++++++++++++++++++- internal/utils/fifo/constants.go | 1 + 10 files changed, 150 insertions(+), 20 deletions(-) create mode 100644 internal/database/migrations/20260115062032_add_fifo_recording_eggs.down.sql create mode 100644 internal/database/migrations/20260115062032_add_fifo_recording_eggs.up.sql diff --git a/internal/database/migrations/20260115062032_add_fifo_recording_eggs.down.sql b/internal/database/migrations/20260115062032_add_fifo_recording_eggs.down.sql new file mode 100644 index 00000000..8fb42a96 --- /dev/null +++ b/internal/database/migrations/20260115062032_add_fifo_recording_eggs.down.sql @@ -0,0 +1,3 @@ +ALTER TABLE recording_eggs + DROP COLUMN IF EXISTS total_used, + DROP COLUMN IF EXISTS total_qty; diff --git a/internal/database/migrations/20260115062032_add_fifo_recording_eggs.up.sql b/internal/database/migrations/20260115062032_add_fifo_recording_eggs.up.sql new file mode 100644 index 00000000..dbadd9e3 --- /dev/null +++ b/internal/database/migrations/20260115062032_add_fifo_recording_eggs.up.sql @@ -0,0 +1,7 @@ +ALTER TABLE recording_eggs + ADD COLUMN total_qty NUMERIC(15, 3) DEFAULT 0 NOT NULL, + ADD COLUMN total_used NUMERIC(15, 3) DEFAULT 0 NOT NULL; + +UPDATE recording_eggs +SET total_qty = qty +WHERE total_qty = 0; diff --git a/internal/entities/recording_egg.go b/internal/entities/recording_egg.go index 68269728..b48c49ca 100644 --- a/internal/entities/recording_egg.go +++ b/internal/entities/recording_egg.go @@ -7,6 +7,8 @@ type RecordingEgg struct { RecordingId uint `gorm:"column:recording_id;not null;index"` ProductWarehouseId uint `gorm:"column:product_warehouse_id;not null"` Qty int `gorm:"column:qty;not null"` + TotalQty float64 `gorm:"column:total_qty"` + TotalUsed float64 `gorm:"column:total_used"` Weight *float64 `gorm:"column:weight"` CreatedBy uint `gorm:"column:created_by"` CreatedAt time.Time `gorm:"autoCreateTime"` diff --git a/internal/modules/production/project_flocks/controllers/projectflock.controller.go b/internal/modules/production/project_flocks/controllers/projectflock.controller.go index 4315b948..c13baab6 100644 --- a/internal/modules/production/project_flocks/controllers/projectflock.controller.go +++ b/internal/modules/production/project_flocks/controllers/projectflock.controller.go @@ -7,6 +7,7 @@ import ( "strconv" "strings" + warehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/dto" "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/dto" service "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/services" validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/validations" @@ -281,6 +282,12 @@ func (u *ProjectflockController) LookupProjectFlockKandang(c *fiber.Ctx) error { dtoResult := dto.ToProjectFlockKandangDTO(*result) dtoResult.AvailableQuantity = float64(availableStock) + if warehouse, werr := u.ProjectflockService.GetWarehouseByKandangID(c, result.KandangId); werr != nil { + return werr + } else if warehouse != nil { + mapped := warehouseDTO.ToWarehouseRelationDTO(*warehouse) + dtoResult.Warehouse = &mapped + } if withPopulation { population, err := u.ProjectflockService.GetProjectFlockKandangPopulation(c, result.Id) if err != nil { diff --git a/internal/modules/production/project_flocks/dto/projectflock_kandang.dto.go b/internal/modules/production/project_flocks/dto/projectflock_kandang.dto.go index 8dedaf15..b6bbf852 100644 --- a/internal/modules/production/project_flocks/dto/projectflock_kandang.dto.go +++ b/internal/modules/production/project_flocks/dto/projectflock_kandang.dto.go @@ -7,6 +7,7 @@ import ( kandangDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/dto" locationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/dto" productionStandardDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/dto" + warehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/dto" userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto" ) @@ -17,24 +18,25 @@ type KandangWithPivotDTO struct { type ProjectFlockWithPivotDTO struct { ProjectFlockRelationDTO - Area *areaDTO.AreaRelationDTO `json:"area,omitempty"` - Category string `json:"category"` - Fcr *fcrDTO.FcrRelationDTO `json:"fcr,omitempty"` + Area *areaDTO.AreaRelationDTO `json:"area,omitempty"` + Category string `json:"category"` + Fcr *fcrDTO.FcrRelationDTO `json:"fcr,omitempty"` ProductionStandard *productionStandardDTO.ProductionStandardRelationDTO `json:"production_standard,omitempty"` - Location *locationDTO.LocationRelationDTO `json:"location,omitempty"` - Kandangs []KandangWithPivotDTO `json:"kandangs,omitempty"` - CreatedUser *userDTO.UserRelationDTO `json:"created_user,omitempty"` + Location *locationDTO.LocationRelationDTO `json:"location,omitempty"` + Kandangs []KandangWithPivotDTO `json:"kandangs,omitempty"` + CreatedUser *userDTO.UserRelationDTO `json:"created_user,omitempty"` } type ProjectFlockKandangDTO struct { - Id uint `json:"id"` - ProjectFlockKandangId uint `json:"project_flock_kandang_id"` - ProjectFlockId uint `json:"project_flock_id"` - KandangId uint `json:"kandang_id"` - Kandang *kandangDTO.KandangRelationDTO `json:"kandang,omitempty"` - ProjectFlock *ProjectFlockWithPivotDTO `json:"project_flock,omitempty"` - AvailableQuantity float64 `json:"available_quantity"` - Population *float64 `json:"population,omitempty"` + Id uint `json:"id"` + ProjectFlockKandangId uint `json:"project_flock_kandang_id"` + ProjectFlockId uint `json:"project_flock_id"` + KandangId uint `json:"kandang_id"` + Kandang *kandangDTO.KandangRelationDTO `json:"kandang,omitempty"` + Warehouse *warehouseDTO.WarehouseRelationDTO `json:"warehouse,omitempty"` + ProjectFlock *ProjectFlockWithPivotDTO `json:"project_flock,omitempty"` + AvailableQuantity float64 `json:"available_quantity"` + Population *float64 `json:"population,omitempty"` } func ToProjectFlockKandangDTO(e entity.ProjectFlockKandang) ProjectFlockKandangDTO { diff --git a/internal/modules/production/project_flocks/services/projectflock.service.go b/internal/modules/production/project_flocks/services/projectflock.service.go index 3dbe3f4b..05e21894 100644 --- a/internal/modules/production/project_flocks/services/projectflock.service.go +++ b/internal/modules/production/project_flocks/services/projectflock.service.go @@ -38,6 +38,7 @@ type ProjectflockService interface { GetOne(ctx *fiber.Ctx, id uint) (*entity.ProjectFlock, *flockDTO.FlockRelationDTO, error) CreateOne(ctx *fiber.Ctx, req *validation.Create) (*entity.ProjectFlock, error) GetAvailableDocQuantity(ctx *fiber.Ctx, kandangID uint) (float64, error) + GetWarehouseByKandangID(ctx *fiber.Ctx, kandangID uint) (*entity.Warehouse, error) DeleteOne(ctx *fiber.Ctx, id uint) error GetProjectFlockKandangByProjectAndKandang(ctx *fiber.Ctx, projectFlockID uint, kandangID uint) (*entity.ProjectFlockKandang, float64, error) GetProjectFlockKandangPopulation(ctx *fiber.Ctx, projectFlockKandangID uint) (float64, error) @@ -518,6 +519,31 @@ func (s projectflockService) GetAvailableDocQuantity(ctx *fiber.Ctx, kandangID u return total, nil } +func (s projectflockService) GetWarehouseByKandangID(ctx *fiber.Ctx, kandangID uint) (*entity.Warehouse, error) { + if kandangID == 0 || s.WarehouseRepo == nil { + return nil, nil + } + + var warehouse entity.Warehouse + err := s.WarehouseRepo.DB().WithContext(ctx.Context()). + Preload("Area"). + Preload("Location"). + Preload("Kandang"). + Where("kandang_id = ?", kandangID). + Where("deleted_at IS NULL"). + Order("id DESC"). + First(&warehouse).Error + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + if err != nil { + s.Log.Errorf("Failed to fetch warehouse for kandang %d: %+v", kandangID, err) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch warehouse") + } + + return &warehouse, nil +} + func (s projectflockService) GetProjectPeriods(c *fiber.Ctx, projectIDs []uint) (map[uint]int, error) { if len(projectIDs) == 0 { return map[uint]int{}, nil diff --git a/internal/modules/production/recordings/dto/recording.dto.go b/internal/modules/production/recordings/dto/recording.dto.go index 4deb9f7e..ebb093ba 100644 --- a/internal/modules/production/recordings/dto/recording.dto.go +++ b/internal/modules/production/recordings/dto/recording.dto.go @@ -82,11 +82,11 @@ type RecordingListDTO struct { CreatedUser *userDTO.UserRelationDTO `json:"created_user,omitempty"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` + Warehouse *RecordingWarehouseDTO `json:"warehouse,omitempty"` } type RecordingDetailDTO struct { RecordingListDTO - Warehouse *RecordingWarehouseDTO `json:"warehouse,omitempty"` ProductCategory string `json:"product_category"` Depletions []RecordingDepletionDTO `json:"depletions"` Stocks []RecordingStockDTO `json:"stocks"` @@ -133,7 +133,6 @@ func ToRecordingDetailDTO(e entity.Recording) RecordingDetailDTO { return RecordingDetailDTO{ RecordingListDTO: listDTO, - Warehouse: recordingWarehouseDTO(e), ProductCategory: recordingProductCategory(e), Depletions: ToRecordingDepletionDTOs(e.Depletions), Stocks: ToRecordingStockDTOs(e.Stocks), @@ -203,6 +202,7 @@ func toRecordingListDTO(e entity.Recording) RecordingListDTO { CreatedAt: e.CreatedAt, UpdatedAt: e.UpdatedAt, CreatedUser: createdUser, + Warehouse: recordingWarehouseDTO(e), } } diff --git a/internal/modules/production/recordings/module.go b/internal/modules/production/recordings/module.go index 91b024ac..11a1e152 100644 --- a/internal/modules/production/recordings/module.go +++ b/internal/modules/production/recordings/module.go @@ -43,6 +43,22 @@ func (RecordingModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate ) fifoService := commonSvc.NewFifoService(db, stockAllocationRepo, productWarehouseRepo, utils.Log) + if err := fifoService.RegisterStockable(fifo.StockableConfig{ + Key: fifo.StockableKeyRecordingEgg, + Table: "recording_eggs", + Columns: fifo.StockableColumns{ + ID: "id", + ProductWarehouseID: "product_warehouse_id", + TotalQuantity: "total_qty", + TotalUsedQuantity: "total_used", + CreatedAt: "created_at", + }, + OrderBy: []string{"created_at ASC", "id ASC"}, + }); err != nil { + if !strings.Contains(strings.ToLower(err.Error()), "already registered") { + panic(fmt.Sprintf("failed to register recording egg stockable workflow: %v", err)) + } + } if err := fifoService.RegisterUsable(fifo.UsableConfig{ Key: fifo.UsableKeyRecordingStock, Table: "recording_stocks", diff --git a/internal/modules/production/recordings/services/recording.service.go b/internal/modules/production/recordings/services/recording.service.go index 88ed4cf7..5dabad9f 100644 --- a/internal/modules/production/recordings/services/recording.service.go +++ b/internal/modules/production/recordings/services/recording.service.go @@ -290,8 +290,19 @@ func (s *recordingService) CreateOne(c *fiber.Ctx, req *validation.Create) (*ent s.Log.Errorf("Failed to persist eggs: %+v", err) return err } + if s.FifoSvc != nil { + if err := s.replenishRecordingEggs(ctx, tx, mappedEggs); err != nil { + return err + } + } - if err := s.adjustProductWarehouseQuantities(ctx, tx, buildWarehouseDeltas(nil, mappedDepletions, nil, mappedEggs)); err != nil { + var warehouseDeltas map[uint]float64 + if s.FifoSvc != nil { + warehouseDeltas = buildWarehouseDeltas(nil, mappedDepletions, nil, nil) + } else { + warehouseDeltas = buildWarehouseDeltas(nil, mappedDepletions, nil, mappedEggs) + } + if err := s.adjustProductWarehouseQuantities(ctx, tx, warehouseDeltas); err != nil { s.Log.Errorf("Failed to adjust product warehouses: %+v", err) return err } @@ -438,6 +449,16 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin } if hasEggChanges { + if s.FifoSvc != nil { + if err := ensureRecordingEggsUnused(existingEggs); err != nil { + return err + } + if err := s.adjustProductWarehouseQuantities(ctx, tx, buildWarehouseDeltas(nil, nil, existingEggs, nil)); err != nil { + s.Log.Errorf("Failed to adjust product warehouses for eggs: %+v", err) + return err + } + } + if err := s.Repository.DeleteEggs(tx, recordingEntity.Id); err != nil { s.Log.Errorf("Failed to clear eggs: %+v", err) return err @@ -449,9 +470,15 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin return err } - if err := s.adjustProductWarehouseQuantities(ctx, tx, buildWarehouseDeltas(nil, nil, existingEggs, mappedEggs)); err != nil { - s.Log.Errorf("Failed to adjust product warehouses for eggs: %+v", err) - return err + if s.FifoSvc != nil { + if err := s.replenishRecordingEggs(ctx, tx, mappedEggs); err != nil { + return err + } + } else { + if err := s.adjustProductWarehouseQuantities(ctx, tx, buildWarehouseDeltas(nil, nil, existingEggs, mappedEggs)); err != nil { + s.Log.Errorf("Failed to adjust product warehouses for eggs: %+v", err) + return err + } } } @@ -626,6 +653,11 @@ func (s recordingService) DeleteOne(c *fiber.Ctx, id uint) error { s.Log.Errorf("Failed to list eggs before delete: %+v", err) return err } + if s.FifoSvc != nil { + if err := ensureRecordingEggsUnused(oldEggs); err != nil { + return err + } + } oldStocks, err := s.Repository.ListStocks(tx, id) if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { @@ -802,6 +834,32 @@ func (s *recordingService) adjustProductWarehouseQuantities(ctx context.Context, return s.ProductWarehouseRepo.AdjustQuantities(ctx, deltas, func(*gorm.DB) *gorm.DB { return tx }) } +func (s *recordingService) replenishRecordingEggs(ctx context.Context, tx *gorm.DB, eggs []entity.RecordingEgg) error { + if len(eggs) == 0 || s.FifoSvc == nil { + return nil + } + + for _, egg := range eggs { + if egg.Id == 0 || egg.ProductWarehouseId == 0 || egg.Qty <= 0 { + continue + } + note := fmt.Sprintf("Recording egg #%d", egg.Id) + if _, err := s.FifoSvc.Replenish(ctx, commonSvc.StockReplenishRequest{ + StockableKey: fifo.StockableKeyRecordingEgg, + StockableID: egg.Id, + ProductWarehouseID: egg.ProductWarehouseId, + Quantity: float64(egg.Qty), + Note: ¬e, + Tx: tx, + }); err != nil { + s.Log.Errorf("Failed to replenish FIFO stock for recording egg %d: %+v", egg.Id, err) + return err + } + } + + return nil +} + type desiredStock struct { Usage float64 Pending float64 @@ -922,6 +980,14 @@ type eggTotals struct { Weight float64 } +func ensureRecordingEggsUnused(eggs []entity.RecordingEgg) error { + for _, egg := range eggs { + if egg.TotalUsed > 0 { + return fiber.NewError(fiber.StatusBadRequest, "Recording egg sudah digunakan sehingga tidak dapat diubah") + } + } + return nil +} func stocksMatch(existing []entity.RecordingStock, incoming []validation.Stock) bool { hasPending := false diff --git a/internal/utils/fifo/constants.go b/internal/utils/fifo/constants.go index 03f61f82..076d960d 100644 --- a/internal/utils/fifo/constants.go +++ b/internal/utils/fifo/constants.go @@ -15,4 +15,5 @@ const ( StockableKeyAdjustmentIn StockableKey = "ADJUSTMENT_IN" StockableKeyPurchaseItems StockableKey = "PURCHASE_ITEMS" StockableKeyProjectFlockPopulation StockableKey = "PROJECT_FLOCK_POPULATION" + StockableKeyRecordingEgg StockableKey = "RECORDING_EGG" ) From f1032b44d17a7dc96487f35a7f0f0607b463feb1 Mon Sep 17 00:00:00 2001 From: ragilap Date: Thu, 15 Jan 2026 15:42:57 +0700 Subject: [PATCH 4/5] [FIX/BE-US] adjustment recording --- .../controllers/projectflock.controller.go | 12 +++++++----- .../dto/projectflock_kandang.dto.go | 18 ++++++++++-------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/internal/modules/production/project_flocks/controllers/projectflock.controller.go b/internal/modules/production/project_flocks/controllers/projectflock.controller.go index c13baab6..e82d3af5 100644 --- a/internal/modules/production/project_flocks/controllers/projectflock.controller.go +++ b/internal/modules/production/project_flocks/controllers/projectflock.controller.go @@ -279,9 +279,14 @@ func (u *ProjectflockController) LookupProjectFlockKandang(c *fiber.Ctx) error { if err != nil { return err } + _ = availableStock dtoResult := dto.ToProjectFlockKandangDTO(*result) - dtoResult.AvailableQuantity = float64(availableStock) + if population, err := u.ProjectflockService.GetProjectFlockKandangPopulation(c, result.Id); err != nil { + return err + } else { + dtoResult.AvailableQuantity = population + } if warehouse, werr := u.ProjectflockService.GetWarehouseByKandangID(c, result.KandangId); werr != nil { return werr } else if warehouse != nil { @@ -289,10 +294,7 @@ func (u *ProjectflockController) LookupProjectFlockKandang(c *fiber.Ctx) error { dtoResult.Warehouse = &mapped } if withPopulation { - population, err := u.ProjectflockService.GetProjectFlockKandangPopulation(c, result.Id) - if err != nil { - return err - } + population := dtoResult.AvailableQuantity dtoResult.Population = &population } diff --git a/internal/modules/production/project_flocks/dto/projectflock_kandang.dto.go b/internal/modules/production/project_flocks/dto/projectflock_kandang.dto.go index b6bbf852..c18f3f65 100644 --- a/internal/modules/production/project_flocks/dto/projectflock_kandang.dto.go +++ b/internal/modules/production/project_flocks/dto/projectflock_kandang.dto.go @@ -18,13 +18,14 @@ type KandangWithPivotDTO struct { type ProjectFlockWithPivotDTO struct { ProjectFlockRelationDTO - Area *areaDTO.AreaRelationDTO `json:"area,omitempty"` - Category string `json:"category"` - Fcr *fcrDTO.FcrRelationDTO `json:"fcr,omitempty"` - ProductionStandard *productionStandardDTO.ProductionStandardRelationDTO `json:"production_standard,omitempty"` - Location *locationDTO.LocationRelationDTO `json:"location,omitempty"` - Kandangs []KandangWithPivotDTO `json:"kandangs,omitempty"` - CreatedUser *userDTO.UserRelationDTO `json:"created_user,omitempty"` + Area *areaDTO.AreaRelationDTO `json:"area,omitempty"` + Category string `json:"category"` + Fcr *fcrDTO.FcrRelationDTO `json:"fcr,omitempty"` + ProductionStandard *productionStandardDTO.ProductionStandardRelationDTO `json:"production_standard,omitempty"` + ProductionStandardId uint `json:"production_standard_id"` + Location *locationDTO.LocationRelationDTO `json:"location,omitempty"` + Kandangs []KandangWithPivotDTO `json:"kandangs,omitempty"` + CreatedUser *userDTO.UserRelationDTO `json:"created_user,omitempty"` } type ProjectFlockKandangDTO struct { @@ -55,7 +56,8 @@ func ToProjectFlockKandangDTO(e entity.ProjectFlockKandang) ProjectFlockKandangD Period: e.Period, FlockName: e.ProjectFlock.FlockName, }, - Category: e.ProjectFlock.Category, + Category: e.ProjectFlock.Category, + ProductionStandardId: e.ProjectFlock.ProductionStandardId, } if e.ProjectFlock.Area.Id != 0 { From fe002c9602792ef43a8cc81dd65ad7fd1fa30d52 Mon Sep 17 00:00:00 2001 From: "Hafizh A. Y" Date: Thu, 15 Jan 2026 15:49:15 +0700 Subject: [PATCH 5/5] fix(BE): remove supplier price in master nonstock --- ...ove_price_from_nonstock_suppliers.down.sql | 3 ++ ...emove_price_from_nonstock_suppliers.up.sql | 3 ++ internal/entities/nonstock_supplier.go | 1 - .../master/nonstocks/dto/nonstock.dto.go | 2 - .../repositories/nonstock.repository.go | 16 ++----- .../nonstocks/services/nonstock.service.go | 44 +++++++++---------- .../validations/nonstock.validation.go | 21 ++++----- .../suppliers/dto/supplier_nonstock.dto.go | 2 - 8 files changed, 38 insertions(+), 54 deletions(-) create mode 100644 internal/database/migrations/20260115082849_remove_price_from_nonstock_suppliers.down.sql create mode 100644 internal/database/migrations/20260115082849_remove_price_from_nonstock_suppliers.up.sql diff --git a/internal/database/migrations/20260115082849_remove_price_from_nonstock_suppliers.down.sql b/internal/database/migrations/20260115082849_remove_price_from_nonstock_suppliers.down.sql new file mode 100644 index 00000000..503f592d --- /dev/null +++ b/internal/database/migrations/20260115082849_remove_price_from_nonstock_suppliers.down.sql @@ -0,0 +1,3 @@ +-- Rollback: add price back to nonstock_suppliers +ALTER TABLE nonstock_suppliers + ADD COLUMN IF NOT EXISTS price NUMERIC(15, 3) NOT NULL DEFAULT 0; diff --git a/internal/database/migrations/20260115082849_remove_price_from_nonstock_suppliers.up.sql b/internal/database/migrations/20260115082849_remove_price_from_nonstock_suppliers.up.sql new file mode 100644 index 00000000..07fdd009 --- /dev/null +++ b/internal/database/migrations/20260115082849_remove_price_from_nonstock_suppliers.up.sql @@ -0,0 +1,3 @@ +-- Migration: remove price from nonstock_suppliers +ALTER TABLE nonstock_suppliers + DROP COLUMN IF EXISTS price; diff --git a/internal/entities/nonstock_supplier.go b/internal/entities/nonstock_supplier.go index d666e3c8..2206390c 100644 --- a/internal/entities/nonstock_supplier.go +++ b/internal/entities/nonstock_supplier.go @@ -5,7 +5,6 @@ import "time" type NonstockSupplier struct { NonstockId uint `gorm:"not null"` SupplierId uint `gorm:"not null"` - Price float64 `gorm:"type:numeric(15,3);not null;default:0"` CreatedAt time.Time `gorm:"autoCreateTime"` Nonstock Nonstock `gorm:"foreignKey:NonstockId;references:Id"` diff --git a/internal/modules/master/nonstocks/dto/nonstock.dto.go b/internal/modules/master/nonstocks/dto/nonstock.dto.go index fa102b9c..8182da21 100644 --- a/internal/modules/master/nonstocks/dto/nonstock.dto.go +++ b/internal/modules/master/nonstocks/dto/nonstock.dto.go @@ -37,7 +37,6 @@ type NonstockSupplierDTO struct { Name string `json:"name"` Alias string `json:"alias"` Category string `json:"category"` - Price float64 `json:"price"` } // === Mapper Functions === @@ -121,7 +120,6 @@ func toNonstockSupplierDTOs(relations []entity.NonstockSupplier) []NonstockSuppl Name: relation.Supplier.Name, Alias: relation.Supplier.Alias, Category: relation.Supplier.Category, - Price: relation.Price, }) } diff --git a/internal/modules/master/nonstocks/repositories/nonstock.repository.go b/internal/modules/master/nonstocks/repositories/nonstock.repository.go index 16260272..56ef39b8 100644 --- a/internal/modules/master/nonstocks/repositories/nonstock.repository.go +++ b/internal/modules/master/nonstocks/repositories/nonstock.repository.go @@ -61,30 +61,20 @@ func (r *NonstockRepositoryImpl) SyncSuppliersDiff(ctx context.Context, tx *gorm return err } - existingMap := make(map[uint]entity.NonstockSupplier, len(existing)) + existingMap := make(map[uint]struct{}, len(existing)) for _, rel := range existing { - existingMap[rel.SupplierId] = rel + existingMap[rel.SupplierId] = struct{}{} } incomingMap := make(map[uint]struct{}, len(suppliers)) for _, rel := range suppliers { incomingMap[rel.SupplierId] = struct{}{} - if existingRel, exists := existingMap[rel.SupplierId]; exists { - if existingRel.Price != rel.Price { - if err := db.WithContext(ctx). - Model(&entity.NonstockSupplier{}). - Where("nonstock_id = ? AND supplier_id = ?", nonstockID, rel.SupplierId). - Update("price", rel.Price). - Error; err != nil { - return err - } - } + if _, exists := existingMap[rel.SupplierId]; exists { continue } record := entity.NonstockSupplier{ NonstockId: nonstockID, SupplierId: rel.SupplierId, - Price: rel.Price, } if err := db.WithContext(ctx).Create(&record).Error; err != nil { return err diff --git a/internal/modules/master/nonstocks/services/nonstock.service.go b/internal/modules/master/nonstocks/services/nonstock.service.go index e1cc5495..1b6a409c 100644 --- a/internal/modules/master/nonstocks/services/nonstock.service.go +++ b/internal/modules/master/nonstocks/services/nonstock.service.go @@ -115,19 +115,18 @@ func (s *nonstockService) CreateOne(c *fiber.Ctx, req *validation.Create) (*enti supplierLinks []entity.NonstockSupplier supplierIDs []uint ) - if len(req.Suppliers) > 0 { - seen := make(map[uint]struct{}, len(req.Suppliers)) - supplierLinks = make([]entity.NonstockSupplier, 0, len(req.Suppliers)) - supplierIDs = make([]uint, 0, len(req.Suppliers)) - for _, supplier := range req.Suppliers { - if _, exists := seen[supplier.SupplierID]; exists { - return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Duplicate supplier_id %d", supplier.SupplierID)) + if len(req.SupplierIDs) > 0 { + seen := make(map[uint]struct{}, len(req.SupplierIDs)) + supplierLinks = make([]entity.NonstockSupplier, 0, len(req.SupplierIDs)) + supplierIDs = make([]uint, 0, len(req.SupplierIDs)) + for _, supplierID := range req.SupplierIDs { + if _, exists := seen[supplierID]; exists { + return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Duplicate supplier_id %d", supplierID)) } - seen[supplier.SupplierID] = struct{}{} - supplierIDs = append(supplierIDs, supplier.SupplierID) + seen[supplierID] = struct{}{} + supplierIDs = append(supplierIDs, supplierID) supplierLinks = append(supplierLinks, entity.NonstockSupplier{ - SupplierId: supplier.SupplierID, - Price: supplier.Price, + SupplierId: supplierID, }) } supplierList, supplierErr := s.Repository.GetSuppliersByIDs(ctx, supplierIDs) @@ -212,21 +211,20 @@ func (s nonstockService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint var supplierLinks []entity.NonstockSupplier var supplierUpdate bool - if req.Suppliers != nil { + if req.SupplierIDs != nil { supplierUpdate = true - if len(*req.Suppliers) > 0 { - seen := make(map[uint]struct{}, len(*req.Suppliers)) - supplierLinks = make([]entity.NonstockSupplier, 0, len(*req.Suppliers)) - supplierIDs := make([]uint, 0, len(*req.Suppliers)) - for _, supplier := range *req.Suppliers { - if _, exists := seen[supplier.SupplierID]; exists { - return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Duplicate supplier_id %d", supplier.SupplierID)) + if len(*req.SupplierIDs) > 0 { + seen := make(map[uint]struct{}, len(*req.SupplierIDs)) + supplierLinks = make([]entity.NonstockSupplier, 0, len(*req.SupplierIDs)) + supplierIDs := make([]uint, 0, len(*req.SupplierIDs)) + for _, supplierID := range *req.SupplierIDs { + if _, exists := seen[supplierID]; exists { + return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Duplicate supplier_id %d", supplierID)) } - seen[supplier.SupplierID] = struct{}{} - supplierIDs = append(supplierIDs, supplier.SupplierID) + seen[supplierID] = struct{}{} + supplierIDs = append(supplierIDs, supplierID) supplierLinks = append(supplierLinks, entity.NonstockSupplier{ - SupplierId: supplier.SupplierID, - Price: supplier.Price, + SupplierId: supplierID, }) } diff --git a/internal/modules/master/nonstocks/validations/nonstock.validation.go b/internal/modules/master/nonstocks/validations/nonstock.validation.go index c5491991..6378ac18 100644 --- a/internal/modules/master/nonstocks/validations/nonstock.validation.go +++ b/internal/modules/master/nonstocks/validations/nonstock.validation.go @@ -1,22 +1,17 @@ package validation -type SupplierPrice struct { - SupplierID uint `json:"supplier_id" validate:"required,gt=0"` - Price float64 `json:"price" validate:"required,gte=0"` -} - type Create struct { - Name string `json:"name" validate:"required_strict,min=3,max=50"` - UomID uint `json:"uom_id" validate:"required,gt=0"` - Suppliers []SupplierPrice `json:"suppliers,omitempty" validate:"omitempty,dive"` - Flags []string `json:"flags" validate:"dive,max=50"` + Name string `json:"name" validate:"required_strict,min=3,max=50"` + UomID uint `json:"uom_id" validate:"required,gt=0"` + SupplierIDs []uint `json:"supplier_ids,omitempty" validate:"omitempty,dive,gt=0"` + Flags []string `json:"flags" validate:"dive,max=50"` } type Update struct { - Name *string `json:"name,omitempty" validate:"omitempty,min=3,max=50"` - UomID *uint `json:"uom_id,omitempty" validate:"omitempty,gt=0"` - Suppliers *[]SupplierPrice `json:"suppliers,omitempty" validate:"omitempty,dive"` - Flags *[]string `json:"flags,omitempty" validate:"omitempty,dive,max=50"` + Name *string `json:"name,omitempty" validate:"omitempty,min=3,max=50"` + UomID *uint `json:"uom_id,omitempty" validate:"omitempty,gt=0"` + SupplierIDs *[]uint `json:"supplier_ids,omitempty" validate:"omitempty,dive,gt=0"` + Flags *[]string `json:"flags,omitempty" validate:"omitempty,dive,max=50"` } type Query struct { diff --git a/internal/modules/master/suppliers/dto/supplier_nonstock.dto.go b/internal/modules/master/suppliers/dto/supplier_nonstock.dto.go index 8c5e0082..828063eb 100644 --- a/internal/modules/master/suppliers/dto/supplier_nonstock.dto.go +++ b/internal/modules/master/suppliers/dto/supplier_nonstock.dto.go @@ -10,7 +10,6 @@ import ( type SupplierNonstockDTO struct { Id uint `json:"id"` Name string `json:"name"` - Price float64 `json:"price"` Uom *uomDTO.UomRelationDTO `json:"uom,omitempty"` Flags []string `json:"flags"` } @@ -43,7 +42,6 @@ func toSupplierNonstockDTOs(relations []entity.NonstockSupplier) []SupplierNonst result = append(result, SupplierNonstockDTO{ Id: Nonstock.Id, Name: Nonstock.Name, - Price: relation.Price, Uom: uomRef, Flags: flags, })