mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
feat/BE/US-76/US-78/US-79/TASK-112,120,133,121-Recording growing/TASK-187,189,202,190-Recording Laying/TASK-191,192,194,197,203-Grading Telur
This commit is contained in:
@@ -208,7 +208,6 @@ func (s *projectflockService) CreateOne(c *fiber.Ctx, req *validation.Create) (*
|
||||
}
|
||||
|
||||
createBody := &entity.ProjectFlock{
|
||||
FlockName: "",
|
||||
AreaId: req.AreaId,
|
||||
Category: cat,
|
||||
FcrId: req.FcrId,
|
||||
|
||||
@@ -170,104 +170,90 @@ func (s *recordingService) CreateOne(c *fiber.Ctx, req *validation.Create) (*ent
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tx := s.Repository.DB().WithContext(ctx).Begin()
|
||||
if tx.Error != nil {
|
||||
s.Log.Errorf("Failed to start recording transaction: %+v", tx.Error)
|
||||
return nil, tx.Error
|
||||
}
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
_ = tx.Rollback()
|
||||
panic(r)
|
||||
var createdRecording entity.Recording
|
||||
transactionErr := s.Repository.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
nextDay, err := s.Repository.GenerateNextDay(tx, req.ProjectFlockKandangId)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to determine recording day: %+v", err)
|
||||
return err
|
||||
}
|
||||
}()
|
||||
|
||||
nextDay, err := s.Repository.GenerateNextDay(tx, req.ProjectFlockKandangId)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to determine recording day: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
recordTime := time.Now().UTC()
|
||||
|
||||
existsToday, err := s.Repository.ExistsOnDate(ctx, req.ProjectFlockKandangId, recordTime)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to verify existing recording on date: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
if existsToday {
|
||||
_ = tx.Rollback()
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Recording for this project flock today already exists")
|
||||
}
|
||||
|
||||
recording := &entity.Recording{
|
||||
ProjectFlockKandangId: req.ProjectFlockKandangId,
|
||||
RecordDatetime: recordTime,
|
||||
Day: &nextDay,
|
||||
CreatedBy: 1, // TODO: replace with authenticated user
|
||||
}
|
||||
|
||||
if err := s.Repository.CreateOne(ctx, recording, func(*gorm.DB) *gorm.DB { return tx }); err != nil {
|
||||
_ = tx.Rollback()
|
||||
if errors.Is(err, gorm.ErrDuplicatedKey) {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Recording for project flock kandang %d already exists", req.ProjectFlockKandangId))
|
||||
recordTime := time.Now().UTC()
|
||||
existsToday, err := s.Repository.ExistsOnDate(ctx, req.ProjectFlockKandangId, recordTime)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to verify existing recording on date: %+v", err)
|
||||
return err
|
||||
}
|
||||
s.Log.Errorf("Failed to create recording: %+v", err)
|
||||
return nil, err
|
||||
if existsToday {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Recording for this project flock today already exists")
|
||||
}
|
||||
|
||||
day := nextDay
|
||||
createdRecording = entity.Recording{
|
||||
ProjectFlockKandangId: req.ProjectFlockKandangId,
|
||||
RecordDatetime: recordTime,
|
||||
Day: &day,
|
||||
CreatedBy: 1, // TODO: replace with authenticated user
|
||||
}
|
||||
|
||||
if err := s.Repository.CreateOne(ctx, &createdRecording, func(*gorm.DB) *gorm.DB { return tx }); err != nil {
|
||||
if errors.Is(err, gorm.ErrDuplicatedKey) {
|
||||
return fiber.NewError(
|
||||
fiber.StatusBadRequest,
|
||||
fmt.Sprintf("Recording for project flock kandang %d already exists", req.ProjectFlockKandangId),
|
||||
)
|
||||
}
|
||||
s.Log.Errorf("Failed to create recording: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
mappedBodyWeights := recordingutil.MapBodyWeights(createdRecording.Id, req.BodyWeights)
|
||||
if err := s.Repository.CreateBodyWeights(tx, mappedBodyWeights); err != nil {
|
||||
s.Log.Errorf("Failed to persist body weights: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
mappedStocks := recordingutil.MapStocks(createdRecording.Id, req.Stocks)
|
||||
if err := s.Repository.CreateStocks(tx, mappedStocks); err != nil {
|
||||
s.Log.Errorf("Failed to persist stocks: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
mappedDepletions := recordingutil.MapDepletions(createdRecording.Id, req.Depletions)
|
||||
if err := s.Repository.CreateDepletions(tx, mappedDepletions); err != nil {
|
||||
s.Log.Errorf("Failed to persist depletions: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
mappedEggs := recordingutil.MapEggs(createdRecording.Id, createdRecording.CreatedBy, req.Eggs)
|
||||
if err := s.Repository.CreateEggs(tx, mappedEggs); err != nil {
|
||||
s.Log.Errorf("Failed to persist eggs: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.adjustProductWarehouseQuantities(ctx, tx, buildWarehouseDeltas(nil, mappedDepletions, nil, mappedStocks, nil, mappedEggs)); err != nil {
|
||||
s.Log.Errorf("Failed to adjust product warehouses: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.computeAndUpdateMetrics(ctx, tx, &createdRecording); err != nil {
|
||||
s.Log.Errorf("Failed to compute recording metrics: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
action := entity.ApprovalActionCreated
|
||||
if err := s.createRecordingApproval(ctx, tx, createdRecording.Id, utils.RecordingStepGradingTelur, action, createdRecording.CreatedBy, nil); err != nil {
|
||||
s.Log.Errorf("Failed to create recording approval for %d: %+v", createdRecording.Id, err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if transactionErr != nil {
|
||||
return nil, transactionErr
|
||||
}
|
||||
|
||||
mappedBodyWeights := recordingutil.MapBodyWeights(recording.Id, req.BodyWeights)
|
||||
if err := s.Repository.CreateBodyWeights(tx, mappedBodyWeights); err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to persist body weights: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
mappedStocks := recordingutil.MapStocks(recording.Id, req.Stocks)
|
||||
if err := s.Repository.CreateStocks(tx, mappedStocks); err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to persist stocks: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
mappedDepletions := recordingutil.MapDepletions(recording.Id, req.Depletions)
|
||||
if err := s.Repository.CreateDepletions(tx, mappedDepletions); err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to persist depletions: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
mappedEggs := recordingutil.MapEggs(recording.Id, recording.CreatedBy, req.Eggs)
|
||||
if err := s.Repository.CreateEggs(tx, mappedEggs); err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to persist eggs: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := s.adjustProductWarehouseQuantities(ctx, tx, buildWarehouseDeltas(nil, mappedDepletions, nil, mappedStocks, nil, mappedEggs)); err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to adjust product warehouses: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := s.computeAndUpdateMetrics(ctx, tx, recording); err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to compute recording metrics: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
action := entity.ApprovalActionCreated
|
||||
if err := s.createRecordingApproval(ctx, tx, recording.Id, utils.RecordingStepGradingTelur, action, recording.CreatedBy, nil); err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to create recording approval for %d: %+v", recording.Id, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
s.Log.Errorf("Failed to commit recording transaction: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.GetOne(c, recording.Id)
|
||||
return s.GetOne(c, createdRecording.Id)
|
||||
}
|
||||
|
||||
func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint) (*entity.Recording, error) {
|
||||
@@ -277,165 +263,146 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin
|
||||
|
||||
ctx := c.Context()
|
||||
|
||||
tx := s.Repository.DB().WithContext(ctx).Begin()
|
||||
if tx.Error != nil {
|
||||
s.Log.Errorf("Failed to start recording transaction: %+v", tx.Error)
|
||||
return nil, tx.Error
|
||||
}
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
_ = tx.Rollback()
|
||||
panic(r)
|
||||
var recordingEntity *entity.Recording
|
||||
transactionErr := s.Repository.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
recording, err := s.Repository.GetByID(ctx, id, func(db *gorm.DB) *gorm.DB {
|
||||
return s.Repository.WithRelations(tx)
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return fiber.NewError(fiber.StatusNotFound, "Recording not found")
|
||||
}
|
||||
s.Log.Errorf("Failed to find recording: %+v", err)
|
||||
return err
|
||||
}
|
||||
}()
|
||||
recordingEntity = recording
|
||||
|
||||
recording, err := s.Repository.GetByID(ctx, id, func(db *gorm.DB) *gorm.DB {
|
||||
return s.Repository.WithRelations(tx)
|
||||
var category string
|
||||
if recordingEntity.ProjectFlockKandang != nil && recordingEntity.ProjectFlockKandang.ProjectFlock.Id != 0 {
|
||||
category = strings.ToUpper(recordingEntity.ProjectFlockKandang.ProjectFlock.Category)
|
||||
}
|
||||
isLaying := category == strings.ToUpper(string(utils.ProjectFlockCategoryLaying))
|
||||
if req.Eggs != nil {
|
||||
if !isLaying && len(req.Eggs) > 0 {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Egg details permitted only for laying project flocks")
|
||||
}
|
||||
if isLaying && len(req.Eggs) == 0 {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Egg details are required for laying project flocks")
|
||||
}
|
||||
}
|
||||
|
||||
if req.BodyWeights != nil {
|
||||
if err := s.Repository.DeleteBodyWeights(tx, recordingEntity.Id); err != nil {
|
||||
s.Log.Errorf("Failed to clear body weights: %+v", err)
|
||||
return err
|
||||
}
|
||||
if err := s.Repository.CreateBodyWeights(tx, recordingutil.MapBodyWeights(recordingEntity.Id, req.BodyWeights)); err != nil {
|
||||
s.Log.Errorf("Failed to update body weights: %+v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if req.Stocks != nil {
|
||||
if err := s.ensureProductWarehousesExist(c, req.Stocks, nil, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
existingStocks, err := s.Repository.ListStocks(tx, recordingEntity.Id)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to list existing stocks: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.Repository.DeleteStocks(tx, recordingEntity.Id); err != nil {
|
||||
s.Log.Errorf("Failed to clear stocks: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
mappedStocks := recordingutil.MapStocks(recordingEntity.Id, req.Stocks)
|
||||
if err := s.Repository.CreateStocks(tx, mappedStocks); err != nil {
|
||||
s.Log.Errorf("Failed to update stocks: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.adjustProductWarehouseQuantities(ctx, tx, buildWarehouseDeltas(nil, nil, existingStocks, mappedStocks, nil, nil)); err != nil {
|
||||
s.Log.Errorf("Failed to adjust product warehouses for stocks: %+v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if req.Eggs != nil && req.Depletions == nil {
|
||||
if err := s.ensureProductWarehousesExist(c, nil, nil, req.Eggs); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if req.Depletions != nil {
|
||||
if err := s.ensureProductWarehousesExist(c, nil, req.Depletions, req.Eggs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
existingDepletions, err := s.Repository.ListDepletions(tx, recordingEntity.Id)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to list existing depletions: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.Repository.DeleteDepletions(tx, recordingEntity.Id); err != nil {
|
||||
s.Log.Errorf("Failed to clear depletions: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
mappedDepletions := recordingutil.MapDepletions(recordingEntity.Id, req.Depletions)
|
||||
if err := s.Repository.CreateDepletions(tx, mappedDepletions); err != nil {
|
||||
s.Log.Errorf("Failed to update depletions: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.adjustProductWarehouseQuantities(ctx, tx, buildWarehouseDeltas(existingDepletions, mappedDepletions, nil, nil, nil, nil)); err != nil {
|
||||
s.Log.Errorf("Failed to adjust product warehouses for depletions: %+v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if req.Eggs != nil {
|
||||
existingEggs, err := s.Repository.ListEggs(tx, recordingEntity.Id)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to list existing 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
|
||||
}
|
||||
|
||||
mappedEggs := recordingutil.MapEggs(recordingEntity.Id, recordingEntity.CreatedBy, req.Eggs)
|
||||
if err := s.Repository.CreateEggs(tx, mappedEggs); err != nil {
|
||||
s.Log.Errorf("Failed to update eggs: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.adjustProductWarehouseQuantities(ctx, tx, buildWarehouseDeltas(nil, nil, nil, nil, existingEggs, mappedEggs)); err != nil {
|
||||
s.Log.Errorf("Failed to adjust product warehouses for eggs: %+v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.computeAndUpdateMetrics(ctx, tx, recordingEntity); err != nil {
|
||||
s.Log.Errorf("Failed to recompute recording metrics: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
action := entity.ApprovalActionUpdated
|
||||
if err := s.createRecordingApproval(ctx, tx, recordingEntity.Id, utils.RecordingStepPengajuan, action, recordingEntity.CreatedBy, nil); err != nil {
|
||||
s.Log.Errorf("Failed to create approval after recording update %d: %+v", recordingEntity.Id, err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Recording not found")
|
||||
}
|
||||
s.Log.Errorf("Failed to find recording: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
recordingEntity := recording
|
||||
|
||||
var category string
|
||||
if recordingEntity.ProjectFlockKandang != nil && recordingEntity.ProjectFlockKandang.ProjectFlock.Id != 0 {
|
||||
category = strings.ToUpper(recordingEntity.ProjectFlockKandang.ProjectFlock.Category)
|
||||
}
|
||||
isLaying := category == strings.ToUpper(string(utils.ProjectFlockCategoryLaying))
|
||||
if req.Eggs != nil {
|
||||
if !isLaying && len(req.Eggs) > 0 {
|
||||
_ = tx.Rollback()
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Egg details permitted only for laying project flocks")
|
||||
}
|
||||
if isLaying && len(req.Eggs) == 0 {
|
||||
_ = tx.Rollback()
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Egg details are required for laying project flocks")
|
||||
}
|
||||
}
|
||||
|
||||
if req.BodyWeights != nil {
|
||||
if err := s.Repository.DeleteBodyWeights(tx, recordingEntity.Id); err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to clear body weights: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
if err := s.Repository.CreateBodyWeights(tx, recordingutil.MapBodyWeights(recordingEntity.Id, req.BodyWeights)); err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to update body weights: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if req.Stocks != nil {
|
||||
if err := s.ensureProductWarehousesExist(c, req.Stocks, nil, nil); err != nil {
|
||||
_ = tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
existingStocks, err := s.Repository.ListStocks(tx, recordingEntity.Id)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to list existing stocks: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := s.Repository.DeleteStocks(tx, recordingEntity.Id); err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to clear stocks: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
mappedStocks := recordingutil.MapStocks(recordingEntity.Id, req.Stocks)
|
||||
if err := s.Repository.CreateStocks(tx, mappedStocks); err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to update stocks: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
if err := s.adjustProductWarehouseQuantities(ctx, tx, buildWarehouseDeltas(nil, nil, existingStocks, mappedStocks, nil, nil)); err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to adjust product warehouses for stocks: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if req.Eggs != nil && req.Depletions == nil {
|
||||
if err := s.ensureProductWarehousesExist(c, nil, nil, req.Eggs); err != nil {
|
||||
_ = tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var existingDepletions []entity.RecordingDepletion
|
||||
if req.Depletions != nil {
|
||||
if err := s.ensureProductWarehousesExist(c, nil, req.Depletions, req.Eggs); err != nil {
|
||||
_ = tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
var err error
|
||||
existingDepletions, err = s.Repository.ListDepletions(tx, recordingEntity.Id)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to list existing depletions: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
if err := s.Repository.DeleteDepletions(tx, recordingEntity.Id); err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to clear depletions: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
mappedDepletions := recordingutil.MapDepletions(recordingEntity.Id, req.Depletions)
|
||||
if err := s.Repository.CreateDepletions(tx, mappedDepletions); err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to update depletions: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
if err := s.adjustProductWarehouseQuantities(ctx, tx, buildWarehouseDeltas(existingDepletions, mappedDepletions, nil, nil, nil, nil)); err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to adjust product warehouses for depletions: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if req.Eggs != nil {
|
||||
existingEggs, err := s.Repository.ListEggs(tx, recordingEntity.Id)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to list existing eggs: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
if err := s.Repository.DeleteEggs(tx, recordingEntity.Id); err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to clear eggs: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
mappedEggs := recordingutil.MapEggs(recordingEntity.Id, recordingEntity.CreatedBy, req.Eggs)
|
||||
if err := s.Repository.CreateEggs(tx, mappedEggs); err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to update eggs: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
if err := s.adjustProductWarehouseQuantities(ctx, tx, buildWarehouseDeltas(nil, nil, nil, nil, existingEggs, mappedEggs)); err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to adjust product warehouses for eggs: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.computeAndUpdateMetrics(ctx, tx, recordingEntity); err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to recompute recording metrics: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
action := entity.ApprovalActionUpdated
|
||||
if err := s.createRecordingApproval(ctx, tx, recordingEntity.Id, utils.RecordingStepPengajuan, action, recordingEntity.CreatedBy, nil); err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to create approval after recording update %d: %+v", recordingEntity.Id, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
s.Log.Errorf("Failed to commit recording transaction: %+v", err)
|
||||
return nil, err
|
||||
if transactionErr != nil {
|
||||
return nil, transactionErr
|
||||
}
|
||||
|
||||
return s.GetOne(c, id)
|
||||
@@ -446,107 +413,102 @@ func (s *recordingService) SubmitGrading(c *fiber.Ctx, req *validation.SubmitGra
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx := c.Context()
|
||||
tx := s.Repository.DB().WithContext(ctx).Begin()
|
||||
if tx.Error != nil {
|
||||
s.Log.Errorf("Failed to start grading transaction: %+v", tx.Error)
|
||||
return nil, tx.Error
|
||||
if len(req.EggsGrading) == 0 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "eggs_grading must contain at least one item")
|
||||
}
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
_ = tx.Rollback()
|
||||
panic(r)
|
||||
|
||||
recordingEggID := req.EggsGrading[0].RecordingEggId
|
||||
for _, grading := range req.EggsGrading[1:] {
|
||||
if grading.RecordingEggId != recordingEggID {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "semua grading harus untuk recording egg yang sama")
|
||||
}
|
||||
}()
|
||||
|
||||
recordingEgg, err := s.Repository.GetRecordingEggByID(ctx, req.RecordingEggId, func(db *gorm.DB) *gorm.DB {
|
||||
return tx
|
||||
})
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
_ = tx.Rollback()
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Recording egg not found")
|
||||
}
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to get recording egg %d: %+v", req.RecordingEggId, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var category string
|
||||
if recordingEgg.Recording.ProjectFlockKandang != nil && recordingEgg.Recording.ProjectFlockKandang.ProjectFlock.Id != 0 {
|
||||
category = strings.ToUpper(recordingEgg.Recording.ProjectFlockKandang.ProjectFlock.Category)
|
||||
}
|
||||
if category != strings.ToUpper(string(utils.ProjectFlockCategoryLaying)) {
|
||||
_ = tx.Rollback()
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Grading eggs hanya diperbolehkan pada project flock dengan kategori laying")
|
||||
}
|
||||
ctx := c.Context()
|
||||
var recordingID uint
|
||||
transactionErr := s.Repository.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
recordingEgg, err := s.Repository.GetRecordingEggByID(ctx, recordingEggID, func(db *gorm.DB) *gorm.DB {
|
||||
return tx
|
||||
})
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return fiber.NewError(fiber.StatusNotFound, "Recording egg not found")
|
||||
}
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to get recording egg %d: %+v", recordingEggID, err)
|
||||
return err
|
||||
}
|
||||
|
||||
totalGradingQty := 0.0
|
||||
for _, grading := range req.EggsGrading {
|
||||
totalGradingQty += grading.Qty
|
||||
}
|
||||
var category string
|
||||
if recordingEgg.Recording.ProjectFlockKandang != nil && recordingEgg.Recording.ProjectFlockKandang.ProjectFlock.Id != 0 {
|
||||
category = strings.ToUpper(recordingEgg.Recording.ProjectFlockKandang.ProjectFlock.Category)
|
||||
}
|
||||
if category != strings.ToUpper(string(utils.ProjectFlockCategoryLaying)) {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Grading eggs hanya diperbolehkan pada project flock dengan kategori laying")
|
||||
}
|
||||
|
||||
availableRecorded := float64(recordingEgg.Qty)
|
||||
if totalGradingQty > availableRecorded {
|
||||
_ = tx.Rollback()
|
||||
return nil, fiber.NewError(
|
||||
fiber.StatusBadRequest,
|
||||
fmt.Sprintf("Total grading (%.2f) melebihi jumlah telur tercatat (%.2f)", totalGradingQty, availableRecorded),
|
||||
)
|
||||
}
|
||||
totalGradingQty := 0.0
|
||||
for _, grading := range req.EggsGrading {
|
||||
totalGradingQty += grading.Qty
|
||||
}
|
||||
|
||||
if recordingEgg.ProductWarehouse.Id != 0 {
|
||||
availableWarehouse := recordingEgg.ProductWarehouse.Quantity
|
||||
if totalGradingQty > availableWarehouse {
|
||||
_ = tx.Rollback()
|
||||
return nil, fiber.NewError(
|
||||
availableRecorded := float64(recordingEgg.Qty)
|
||||
if totalGradingQty > availableRecorded {
|
||||
return fiber.NewError(
|
||||
fiber.StatusBadRequest,
|
||||
fmt.Sprintf("Total grading (%.2f) melebihi stok telur baik (%.2f)", totalGradingQty, availableWarehouse),
|
||||
fmt.Sprintf("Total grading (%.2f) melebihi jumlah telur tercatat (%.2f)", totalGradingQty, availableRecorded),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.Repository.DeleteGradingEggs(tx, recordingEgg.Id); err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to clear grading eggs for recording egg %d: %+v", recordingEgg.Id, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gradings := make([]entity.GradingEgg, 0, len(req.EggsGrading))
|
||||
createdBy := recordingEgg.CreatedBy
|
||||
if createdBy == 0 {
|
||||
createdBy = recordingEgg.Recording.CreatedBy
|
||||
}
|
||||
for _, item := range req.EggsGrading {
|
||||
gradings = append(gradings, entity.GradingEgg{
|
||||
RecordingEggId: recordingEgg.Id,
|
||||
Grade: strings.TrimSpace(item.Grade),
|
||||
Qty: item.Qty,
|
||||
CreatedBy: createdBy,
|
||||
})
|
||||
}
|
||||
|
||||
if len(gradings) > 0 {
|
||||
if err := s.Repository.CreateGradingEggs(tx, gradings); err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to persist grading eggs for recording egg %d: %+v", recordingEgg.Id, err)
|
||||
return nil, err
|
||||
if recordingEgg.ProductWarehouse.Id != 0 {
|
||||
availableWarehouse := recordingEgg.ProductWarehouse.Quantity
|
||||
if totalGradingQty > availableWarehouse {
|
||||
return fiber.NewError(
|
||||
fiber.StatusBadRequest,
|
||||
fmt.Sprintf("Total grading (%.2f) melebihi stok telur baik (%.2f)", totalGradingQty, availableWarehouse),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.Repository.DeleteGradingEggs(tx, recordingEgg.Id); err != nil {
|
||||
s.Log.Errorf("Failed to clear grading eggs for recording egg %d: %+v", recordingEgg.Id, err)
|
||||
return err
|
||||
}
|
||||
|
||||
gradings := make([]entity.GradingEgg, 0, len(req.EggsGrading))
|
||||
createdBy := recordingEgg.CreatedBy
|
||||
if createdBy == 0 {
|
||||
createdBy = recordingEgg.Recording.CreatedBy
|
||||
}
|
||||
for _, item := range req.EggsGrading {
|
||||
gradings = append(gradings, entity.GradingEgg{
|
||||
RecordingEggId: recordingEgg.Id,
|
||||
Grade: strings.TrimSpace(item.Grade),
|
||||
Qty: item.Qty,
|
||||
CreatedBy: createdBy,
|
||||
})
|
||||
}
|
||||
|
||||
if len(gradings) > 0 {
|
||||
if err := s.Repository.CreateGradingEggs(tx, gradings); err != nil {
|
||||
s.Log.Errorf("Failed to persist grading eggs for recording egg %d: %+v", recordingEgg.Id, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
action := entity.ApprovalActionUpdated
|
||||
if err := s.createRecordingApproval(ctx, tx, recordingEgg.RecordingId, utils.RecordingStepPengajuan, action, createdBy, nil); err != nil {
|
||||
s.Log.Errorf("Failed to create approval after grading for recording %d: %+v", recordingEgg.RecordingId, err)
|
||||
return err
|
||||
}
|
||||
|
||||
recordingID = recordingEgg.RecordingId
|
||||
return nil
|
||||
})
|
||||
if transactionErr != nil {
|
||||
return nil, transactionErr
|
||||
}
|
||||
|
||||
action := entity.ApprovalActionUpdated
|
||||
if err := s.createRecordingApproval(ctx, tx, recordingEgg.RecordingId, utils.RecordingStepPengajuan, action, createdBy, nil); err != nil {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to create approval after grading for recording %d: %+v", recordingEgg.RecordingId, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
s.Log.Errorf("Failed to commit grading transaction: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.GetOne(c, recordingEgg.RecordingId)
|
||||
return s.GetOne(c, recordingID)
|
||||
}
|
||||
|
||||
func (s recordingService) Approval(c *fiber.Ctx, req *validation.Approve) ([]entity.Recording, error) {
|
||||
@@ -629,49 +591,39 @@ func (s recordingService) Approval(c *fiber.Ctx, req *validation.Approve) ([]ent
|
||||
func (s recordingService) DeleteOne(c *fiber.Ctx, id uint) error {
|
||||
ctx := c.Context()
|
||||
|
||||
tx := s.Repository.DB().WithContext(ctx).Begin()
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
|
||||
oldDepletions, err := s.Repository.ListDepletions(tx, id)
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to list depletions before delete: %+v", err)
|
||||
return err
|
||||
}
|
||||
oldEggs, err := s.Repository.ListEggs(tx, id)
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to list eggs before delete: %+v", err)
|
||||
return err
|
||||
}
|
||||
oldStocks, err := s.Repository.ListStocks(tx, id)
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
_ = tx.Rollback()
|
||||
s.Log.Errorf("Failed to list stocks before delete: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.adjustProductWarehouseQuantities(ctx, tx, buildWarehouseDeltas(oldDepletions, nil, oldStocks, nil, oldEggs, nil)); err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.Repository.WithTx(tx).DeleteOne(ctx, id); err != nil {
|
||||
_ = tx.Rollback()
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return fiber.NewError(fiber.StatusNotFound, "Recording not found")
|
||||
return s.Repository.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
oldDepletions, err := s.Repository.ListDepletions(tx, id)
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
s.Log.Errorf("Failed to list depletions before delete: %+v", err)
|
||||
return err
|
||||
}
|
||||
s.Log.Errorf("Failed to delete recording: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
s.Log.Errorf("Failed to commit delete recording transaction: %+v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
oldEggs, err := s.Repository.ListEggs(tx, id)
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
s.Log.Errorf("Failed to list eggs before delete: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
oldStocks, err := s.Repository.ListStocks(tx, id)
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
s.Log.Errorf("Failed to list stocks before delete: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.adjustProductWarehouseQuantities(ctx, tx, buildWarehouseDeltas(oldDepletions, nil, oldStocks, nil, oldEggs, nil)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.Repository.WithTx(tx).DeleteOne(ctx, id); err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return fiber.NewError(fiber.StatusNotFound, "Recording not found")
|
||||
}
|
||||
s.Log.Errorf("Failed to delete recording: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// === Persistence Helpers ===
|
||||
|
||||
@@ -9,7 +9,7 @@ type (
|
||||
|
||||
Stock struct {
|
||||
ProductWarehouseId uint `json:"product_warehouse_id" validate:"required,number,min=1"`
|
||||
UsageAmount *float64 `json:"usage_amount,omitempty" validate:"omitempty,gte=0"`
|
||||
Qty *float64 `json:"qty,omitempty" validate:"required_without=UsageAmount,gte=0"`
|
||||
PendingQty *float64 `json:"pending_qty,omitempty" validate:"omitempty,gte=0"`
|
||||
}
|
||||
|
||||
@@ -46,13 +46,13 @@ type Query struct {
|
||||
}
|
||||
|
||||
type EggGrading struct {
|
||||
Grade string `json:"grade" validate:"required"`
|
||||
Qty float64 `json:"qty" validate:"required,gte=0"`
|
||||
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 {
|
||||
RecordingEggId uint `json:"recording_egg_id" validate:"required,number,min=1"`
|
||||
EggsGrading []EggGrading `json:"eggs_grading" validate:"required,dive"`
|
||||
EggsGrading []EggGrading `json:"eggs_grading" validate:"required,dive"`
|
||||
}
|
||||
|
||||
type Approve struct {
|
||||
|
||||
@@ -35,11 +35,21 @@ func MapStocks(recordingID uint, items []validation.Stock) []entity.RecordingSto
|
||||
|
||||
result := make([]entity.RecordingStock, 0, len(items))
|
||||
for _, item := range items {
|
||||
var usageAmount float64
|
||||
if item.Qty != nil {
|
||||
usageAmount = *item.Qty
|
||||
}
|
||||
usagePtr := new(float64)
|
||||
*usagePtr = usageAmount
|
||||
pending := item.PendingQty
|
||||
if pending == nil {
|
||||
pending = new(float64)
|
||||
}
|
||||
result = append(result, entity.RecordingStock{
|
||||
RecordingId: recordingID,
|
||||
ProductWarehouseId: item.ProductWarehouseId,
|
||||
UsageQty: item.UsageAmount,
|
||||
PendingQty: item.PendingQty,
|
||||
UsageQty: usagePtr,
|
||||
PendingQty: pending,
|
||||
})
|
||||
}
|
||||
return result
|
||||
|
||||
Reference in New Issue
Block a user