diff --git a/internal/database/migrations/20260128034158_create_field_stock.down.sql b/internal/database/migrations/20260128034158_create_field_stock.down.sql new file mode 100644 index 00000000..7a508600 --- /dev/null +++ b/internal/database/migrations/20260128034158_create_field_stock.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE stock_logs +DROP COLUMN stock; diff --git a/internal/database/migrations/20260128034158_create_field_stock.up.sql b/internal/database/migrations/20260128034158_create_field_stock.up.sql new file mode 100644 index 00000000..9b7847f1 --- /dev/null +++ b/internal/database/migrations/20260128034158_create_field_stock.up.sql @@ -0,0 +1,18 @@ +ALTER TABLE stock_logs +ADD COLUMN stock NUMERIC(15, 3) NOT NULL DEFAULT 0; + +WITH calc AS ( + SELECT + id, + SUM(COALESCE(increase, 0) - COALESCE(decrease, 0)) + OVER ( + PARTITION BY product_warehouse_id + ORDER BY id + ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW + ) AS running_stock + FROM stock_logs +) +UPDATE stock_logs t +SET stock = c.running_stock +FROM calc c +WHERE t.id = c.id; diff --git a/internal/entities/stock_log.go b/internal/entities/stock_log.go index d6acafb8..1bebdd73 100644 --- a/internal/entities/stock_log.go +++ b/internal/entities/stock_log.go @@ -9,6 +9,7 @@ type StockLog struct { Increase float64 `gorm:"column:increase;type:numeric(15,3);default:0"` Decrease float64 `gorm:"column:decrease;type:numeric(15,3);default:0"` + Stock float64 `gorm:"column:stock;type:numeric(15,3);not null;default:0"` LoggableType string `gorm:"column:loggable_type;type:varchar(50);not null"` LoggableId uint `gorm:"column:loggable_id;not null"` diff --git a/internal/modules/daily-checklists/services/daily-checklist.service.go b/internal/modules/daily-checklists/services/daily-checklist.service.go index e2974039..6913f587 100644 --- a/internal/modules/daily-checklists/services/daily-checklist.service.go +++ b/internal/modules/daily-checklists/services/daily-checklist.service.go @@ -3,6 +3,7 @@ package service import ( "errors" "math" + "regexp" "sort" "strconv" "strings" @@ -259,8 +260,9 @@ func (s dailyChecklistService) GetAll(c *fiber.Ctx, params *validation.Query) ([ } if params.Search != "" { - like := "%" + params.Search + "%" - db = db.Where("(k.name ILIKE ? OR dc.category::text ILIKE ?)", like, like) + re := regexp.MustCompile("[^a-zA-Z0-9]") + like := re.ReplaceAll([]byte("%"+params.Search+"%"), []byte("")) + db = db.Where("(regexp_replace(k.name, '[^a-zA-Z0-9]', '', 'g') ILIKE ? OR regexp_replace(dc.category::text, '[^a-zA-Z0-9]', '', 'g') ILIKE ?)", string(like), string(like)) } countDB := db.Session(&gorm.Session{}) diff --git a/internal/modules/inventory/adjustments/services/adjustment.service.go b/internal/modules/inventory/adjustments/services/adjustment.service.go index 0cad4ff4..ceefcb1e 100644 --- a/internal/modules/inventory/adjustments/services/adjustment.service.go +++ b/internal/modules/inventory/adjustments/services/adjustment.service.go @@ -169,15 +169,30 @@ func (s *adjustmentService) Adjustment(c *fiber.Ctx, req *validation.Create) (*e CreatedBy: actorID, } + stockLogs, err := s.StockLogsRepository.GetByProductWarehouse(ctx, productWarehouse.Id, 1) + if err != nil { + s.Log.Errorf("Failed to get stock logs: %+v", err) + return fiber.NewError(fiber.StatusInternalServerError, "Failed to get stock logs") + } + + if len(stockLogs) > 0 { + latestStockLog := stockLogs[0] + newLog.Stock = latestStockLog.Stock + } else { + newLog.Stock = 0 + } + if transactionType == string(utils.StockLogTransactionTypeIncrease) { afterQuantity += req.Quantity newLog.Increase = req.Quantity + newLog.Stock += newLog.Increase } else { if productWarehouse.Quantity < req.Quantity { return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Stok tidak mencukupi untuk pengurangan. Stok saat ini: %.2f, Jumlah yang akan dikurangi: %.2f", productWarehouse.Quantity, req.Quantity)) } afterQuantity -= req.Quantity newLog.Decrease = req.Quantity + newLog.Stock -= newLog.Decrease } if err := s.StockLogsRepository.WithTx(tx).CreateOne(ctx, newLog, nil); err != nil { diff --git a/internal/modules/inventory/transfers/services/transfer.service.go b/internal/modules/inventory/transfers/services/transfer.service.go index 1d69de6f..aa5a6069 100644 --- a/internal/modules/inventory/transfers/services/transfer.service.go +++ b/internal/modules/inventory/transfers/services/transfer.service.go @@ -476,6 +476,18 @@ func (s *transferService) CreateOne(c *fiber.Ctx, req *validation.TransferReques LoggableId: uint(detail.Id), Notes: "", } + stockLogs, err := s.StockLogsRepository.GetByProductWarehouse(c.Context(), uint(*detail.SourceProductWarehouseID), 1) + if err != nil { + s.Log.Errorf("Failed to get stock logs: %+v", err) + return fiber.NewError(fiber.StatusInternalServerError, "Failed to get stock logs") + } + if len(stockLogs) > 0 { + latestStockLog := stockLogs[0] + stockLogDecrease.Stock -= latestStockLog.Stock - stockLogDecrease.Decrease + } else { + stockLogDecrease.Stock -= stockLogDecrease.Decrease + } + if err := stocklogsRepoTx.CreateOne(c.Context(), stockLogDecrease, nil); err != nil { return fiber.NewError(fiber.StatusInternalServerError, "Gagal membuat log stok keluar") } @@ -512,6 +524,17 @@ func (s *transferService) CreateOne(c *fiber.Ctx, req *validation.TransferReques LoggableId: uint(detail.Id), Notes: "", } + stockLogs, err = s.StockLogsRepository.GetByProductWarehouse(c.Context(), uint(*detail.DestProductWarehouseID), 1) + if err != nil { + s.Log.Errorf("Failed to get stock logs: %+v", err) + return fiber.NewError(fiber.StatusInternalServerError, "Failed to get stock logs") + } + if len(stockLogs) > 0 { + latestStockLog := stockLogs[0] + stockLogIncrease.Stock = latestStockLog.Stock + stockLogIncrease.Increase + } else { + stockLogIncrease.Stock += stockLogIncrease.Increase + } if err := stocklogsRepoTx.CreateOne(c.Context(), stockLogIncrease, nil); err != nil { return fiber.NewError(fiber.StatusInternalServerError, "Gagal membuat log stok masuk") } diff --git a/internal/modules/marketing/services/deliveryorder.service.go b/internal/modules/marketing/services/deliveryorder.service.go index b145fc98..51e37465 100644 --- a/internal/modules/marketing/services/deliveryorder.service.go +++ b/internal/modules/marketing/services/deliveryorder.service.go @@ -566,6 +566,17 @@ func (s deliveryOrdersService) consumeDeliveryStock(ctx context.Context, tx *gor CreatedBy: actorID, Notes: notes, } + stockLogs, err := s.StockLogRepo.GetByProductWarehouse(ctx, marketingProduct.ProductWarehouseId, 1) + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, "Failed to get stock logs") + } + if len(stockLogs) > 0 { + latestStockLog := stockLogs[0] + decreaseLog.Stock = latestStockLog.Stock + decreaseLog.Stock -= decreaseLog.Decrease + } else { + decreaseLog.Stock -= decreaseLog.Decrease + } s.StockLogRepo.WithTx(tx).CreateOne(ctx, decreaseLog, nil) } @@ -608,6 +619,18 @@ func (s deliveryOrdersService) releaseDeliveryStock(ctx context.Context, tx *gor CreatedBy: actorID, Notes: "", } + stockLogs, err := s.StockLogRepo.GetByProductWarehouse(ctx, marketingProduct.ProductWarehouseId, 1) + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, "Failed to get stock logs") + } + if len(stockLogs) > 0 { + latestStockLog := stockLogs[0] + increaseLog.Stock = latestStockLog.Stock + increaseLog.Stock += increaseLog.Increase + } else { + increaseLog.Stock += increaseLog.Increase + } + s.StockLogRepo.WithTx(tx).CreateOne(ctx, increaseLog, nil) } diff --git a/internal/modules/production/chickins/services/chickin.service.go b/internal/modules/production/chickins/services/chickin.service.go index b39dca78..971ee072 100644 --- a/internal/modules/production/chickins/services/chickin.service.go +++ b/internal/modules/production/chickins/services/chickin.service.go @@ -645,6 +645,18 @@ func (s *chickinService) ConsumeChickinStocks(ctx context.Context, tx *gorm.DB, CreatedBy: actorID, Notes: fmt.Sprintf("Chickin #%d", chickin.Id), } + stockLogs, err := s.StockLogRepo.GetByProductWarehouse(ctx, chickin.ProductWarehouseId, 1) + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, "Failed to get stock logs") + } + if len(stockLogs) > 0 { + latestStockLog := stockLogs[0] + decreaseLog.Stock = latestStockLog.Stock + decreaseLog.Stock -= decreaseLog.Decrease + } else { + decreaseLog.Stock -= decreaseLog.Decrease + } + s.StockLogRepo.CreateOne(ctx, decreaseLog, nil) } @@ -701,6 +713,18 @@ func (s *chickinService) ReleaseChickinStocks(ctx context.Context, tx *gorm.DB, CreatedBy: actorID, Notes: fmt.Sprintf("Chickin #%d - Stock released", chickin.Id), } + stockLogs, err := s.StockLogRepo.GetByProductWarehouse(ctx, chickin.ProductWarehouseId, 1) + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, "Failed to get stock logs") + } + if len(stockLogs) > 0 { + latestStockLog := stockLogs[0] + increaseLog.Stock = latestStockLog.Stock + increaseLog.Stock += increaseLog.Increase + } else { + increaseLog.Stock += increaseLog.Increase + } + s.StockLogRepo.CreateOne(ctx, increaseLog, nil) } diff --git a/internal/modules/production/recordings/services/recording.service.go b/internal/modules/production/recordings/services/recording.service.go index c3e21770..7a63d5da 100644 --- a/internal/modules/production/recordings/services/recording.service.go +++ b/internal/modules/production/recordings/services/recording.service.go @@ -4,6 +4,10 @@ import ( "context" "errors" "fmt" + "math" + "strings" + "time" + commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository" commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service" entity "gitlab.com/mbugroup/lti-api.git/internal/entities" @@ -19,9 +23,6 @@ import ( approvalutils "gitlab.com/mbugroup/lti-api.git/internal/utils/approvals" "gitlab.com/mbugroup/lti-api.git/internal/utils/fifo" recordingutil "gitlab.com/mbugroup/lti-api.git/internal/utils/recording" - "math" - "strings" - "time" "github.com/go-playground/validator/v10" "github.com/gofiber/fiber/v2" @@ -535,6 +536,17 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin if egg.ProductWarehouseId == 0 || egg.Qty <= 0 { continue } + stockLogs, err := s.StockLogRepo.GetByProductWarehouse(ctx, egg.ProductWarehouseId, 1) + if err != nil { + s.Log.Errorf("Failed to get stock logs: %+v", err) + return fiber.NewError(fiber.StatusInternalServerError, "Failed to get stock logs") + } + latestStockLog := &entity.StockLog{} + if len(stockLogs) > 0 { + latestStockLog = stockLogs[0] + } else { + latestStockLog.Stock = 0 + } logs = append(logs, &entity.StockLog{ ProductWarehouseId: egg.ProductWarehouseId, CreatedBy: actorID, @@ -542,6 +554,7 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin LoggableType: string(utils.StockLogTypeRecording), LoggableId: recordingEntity.Id, Notes: note, + Stock: latestStockLog.Stock - float64(egg.Qty), }) } if len(logs) > 0 { @@ -937,6 +950,18 @@ func (s *recordingService) consumeRecordingStocks( LoggableId: stock.RecordingId, Notes: note, } + stockLogs, err := s.StockLogRepo.GetByProductWarehouse(ctx, stock.ProductWarehouseId, 1) + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, "Failed to get stock logs") + } + if len(stockLogs) > 0 { + latestStockLog := stockLogs[0] + log.Stock = latestStockLog.Stock + log.Stock -= log.Decrease + } else { + log.Stock -= log.Decrease + } + if err := s.StockLogRepo.WithTx(tx).CreateOne(ctx, log, nil); err != nil { return err } @@ -1004,6 +1029,18 @@ func (s *recordingService) consumeRecordingDepletions( LoggableId: depletion.RecordingId, Notes: note, } + stockLogs, err := s.StockLogRepo.GetByProductWarehouse(ctx, sourceWarehouseID, 1) + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, "Failed to get stock logs") + } + if len(stockLogs) > 0 { + latestStockLog := stockLogs[0] + log.Stock = latestStockLog.Stock + log.Stock -= log.Decrease + } else { + log.Stock -= log.Decrease + } + if err := s.StockLogRepo.WithTx(tx).CreateOne(ctx, log, nil); err != nil { return err } @@ -1022,6 +1059,18 @@ func (s *recordingService) consumeRecordingDepletions( LoggableId: depletion.RecordingId, Notes: note, } + stockLogs, err := s.StockLogRepo.GetByProductWarehouse(ctx, depletion.ProductWarehouseId, 1) + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, "Failed to get stock logs") + } + if len(stockLogs) > 0 { + latestStockLog := stockLogs[0] + log.Stock = latestStockLog.Stock + log.Stock += log.Increase + } else { + log.Stock += log.Increase + } + if err := s.StockLogRepo.WithTx(tx).CreateOne(ctx, log, nil); err != nil { return err } @@ -1082,6 +1131,18 @@ func (s *recordingService) releaseRecordingStocks( LoggableId: stock.RecordingId, Notes: note, } + stockLogs, err := s.StockLogRepo.GetByProductWarehouse(ctx, stock.ProductWarehouseId, 1) + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, "Failed to get stock logs") + } + if len(stockLogs) > 0 { + latestStockLog := stockLogs[0] + log.Stock = latestStockLog.Stock + log.Stock += log.Increase + } else { + log.Stock += log.Increase + } + if err := s.StockLogRepo.WithTx(tx).CreateOne(ctx, log, nil); err != nil { return err } @@ -1144,6 +1205,18 @@ func (s *recordingService) releaseRecordingDepletions( LoggableId: depletion.RecordingId, Notes: note, } + stockLogs, err := s.StockLogRepo.GetByProductWarehouse(ctx, sourceWarehouseID, 1) + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, "Failed to get stock logs") + } + if len(stockLogs) > 0 { + latestStockLog := stockLogs[0] + log.Stock = latestStockLog.Stock + log.Stock += log.Increase + } else { + log.Stock += log.Increase + } + if err := s.StockLogRepo.WithTx(tx).CreateOne(ctx, log, nil); err != nil { return err } @@ -1162,6 +1235,18 @@ func (s *recordingService) releaseRecordingDepletions( LoggableId: depletion.RecordingId, Notes: note, } + stockLogs, err := s.StockLogRepo.GetByProductWarehouse(ctx, depletion.ProductWarehouseId, 1) + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, "Failed to get stock logs") + } + if len(stockLogs) > 0 { + latestStockLog := stockLogs[0] + log.Stock = latestStockLog.Stock + log.Stock -= log.Decrease + } else { + log.Stock -= log.Decrease + } + if err := s.StockLogRepo.WithTx(tx).CreateOne(ctx, log, nil); err != nil { return err } @@ -1309,6 +1394,18 @@ func (s *recordingService) replenishRecordingEggs( LoggableId: egg.RecordingId, Notes: note, } + stockLogs, err := s.StockLogRepo.GetByProductWarehouse(ctx, egg.ProductWarehouseId, 1) + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, "Failed to get stock logs") + } + if len(stockLogs) > 0 { + latestStockLog := stockLogs[0] + log.Stock = latestStockLog.Stock + log.Stock += log.Increase + } else { + log.Stock += log.Increase + } + if err := s.StockLogRepo.WithTx(tx).CreateOne(ctx, log, nil); err != nil { return err } diff --git a/internal/modules/production/transfer_layings/services/transfer_laying.service.go b/internal/modules/production/transfer_layings/services/transfer_laying.service.go index 396944e1..e6e9a862 100644 --- a/internal/modules/production/transfer_layings/services/transfer_laying.service.go +++ b/internal/modules/production/transfer_layings/services/transfer_laying.service.go @@ -826,6 +826,18 @@ func (s transferLayingService) Approval(c *fiber.Ctx, req *validation.Approve) ( LoggableId: approvableID, Notes: fmt.Sprintf("TL #%s", transfer.TransferNumber), } + stockLogs, err := stockLogRepoTx.GetByProductWarehouse(c.Context(), *source.ProductWarehouseId, 1) + if err != nil { + s.Log.Errorf("Failed to get stock logs: %+v", err) + return fiber.NewError(fiber.StatusInternalServerError, "Failed to get stock logs") + } + if len(stockLogs) > 0 { + latestStockLog := stockLogs[0] + stockLogDecrease.Stock = latestStockLog.Stock - stockLogDecrease.Decrease + } else { + stockLogDecrease.Stock -= stockLogDecrease.Decrease + } + if err := stockLogRepoTx.CreateOne(c.Context(), stockLogDecrease, nil); err != nil { return fiber.NewError(fiber.StatusInternalServerError, "Gagal membuat log stok keluar") } @@ -864,6 +876,18 @@ func (s transferLayingService) Approval(c *fiber.Ctx, req *validation.Approve) ( LoggableId: approvableID, Notes: fmt.Sprintf("TL #%s", transfer.TransferNumber), } + stockLogs, err := stockLogRepoTx.GetByProductWarehouse(c.Context(), *target.ProductWarehouseId, 1) + if err != nil { + s.Log.Errorf("Failed to get stock logs: %+v", err) + return fiber.NewError(fiber.StatusInternalServerError, "Failed to get stock logs") + } + if len(stockLogs) > 0 { + latestStockLog := stockLogs[0] + stockLogIncrease.Stock = latestStockLog.Stock + stockLogIncrease.Increase + } else { + stockLogIncrease.Stock += stockLogIncrease.Increase + } + if err := stockLogRepoTx.CreateOne(c.Context(), stockLogIncrease, nil); err != nil { return fiber.NewError(fiber.StatusInternalServerError, "Gagal membuat log stok masuk") } diff --git a/internal/modules/purchases/services/purchase.service.go b/internal/modules/purchases/services/purchase.service.go index b789ef34..dabfad39 100644 --- a/internal/modules/purchases/services/purchase.service.go +++ b/internal/modules/purchases/services/purchase.service.go @@ -1081,10 +1081,25 @@ func (s *purchaseService) ReceiveProducts(c *fiber.Ctx, id uint, req *validation LoggableId: purchase.Id, Notes: receiveNote, } + stockLogs, err := stockLogRepoTx.GetByProductWarehouse(ctx, entry.pwID, 1) + if err != nil { + s.Log.Errorf("Failed to get stock logs: %+v", err) + return fiber.NewError(fiber.StatusInternalServerError, "Failed to get stock logs") + } + + if len(stockLogs) > 0 { + latestStockLog := stockLogs[0] + log.Stock = latestStockLog.Stock + } else { + log.Stock = 0 + } + if entry.delta > 0 { log.Increase = entry.delta + log.Stock += log.Increase } else { log.Decrease = -entry.delta + log.Stock -= log.Decrease } logs = append(logs, log) }