mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
feat(BE): fixing fifo system recording
This commit is contained in:
@@ -194,13 +194,17 @@ func (s *adjustmentService) Adjustment(c *fiber.Ctx, req *validation.Create) (*e
|
||||
StockLogId: newLog.Id,
|
||||
ProductWarehouseId: productWarehouse.Id,
|
||||
}
|
||||
if err := s.AdjustmentStockRepository.WithTx(tx).CreateOne(ctx, adjustmentStock, nil); err != nil {
|
||||
s.Log.Errorf("Failed to create adjustment stock: %+v", err)
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create adjustment stock record")
|
||||
}
|
||||
|
||||
if transactionType == string(utils.StockLogTransactionTypeIncrease) {
|
||||
// Adjustment INCREASE → Replenish stock (Stockable)
|
||||
note := fmt.Sprintf("Stock Adjustment IN #%d", newLog.Id)
|
||||
replenishResult, err := s.FifoSvc.Replenish(ctx, common.StockReplenishRequest{
|
||||
_, err := s.FifoSvc.Replenish(ctx, common.StockReplenishRequest{
|
||||
StockableKey: "ADJUSTMENT_IN",
|
||||
StockableID: newLog.Id,
|
||||
StockableID: adjustmentStock.Id,
|
||||
ProductWarehouseID: uint(productWarehouse.Id),
|
||||
Quantity: req.Quantity,
|
||||
Note: ¬e,
|
||||
@@ -210,15 +214,11 @@ func (s *adjustmentService) Adjustment(c *fiber.Ctx, req *validation.Create) (*e
|
||||
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Failed to replenish stock via FIFO: %v", err))
|
||||
}
|
||||
|
||||
// Update stockable tracking fields
|
||||
adjustmentStock.TotalQty = replenishResult.AddedQuantity
|
||||
adjustmentStock.TotalUsed = 0
|
||||
|
||||
} else {
|
||||
// Adjustment DECREASE → Consume stock (Usable)
|
||||
consumeResult, err := s.FifoSvc.Consume(ctx, common.StockConsumeRequest{
|
||||
_, err := s.FifoSvc.Consume(ctx, common.StockConsumeRequest{
|
||||
UsableKey: "ADJUSTMENT_OUT",
|
||||
UsableID: newLog.Id,
|
||||
UsableID: adjustmentStock.Id,
|
||||
ProductWarehouseID: uint(productWarehouse.Id),
|
||||
Quantity: req.Quantity,
|
||||
AllowPending: false, // Don't allow pending for adjustment
|
||||
@@ -227,16 +227,6 @@ func (s *adjustmentService) Adjustment(c *fiber.Ctx, req *validation.Create) (*e
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Failed to consume stock via FIFO: %v", err))
|
||||
}
|
||||
|
||||
// Update usable tracking fields
|
||||
adjustmentStock.UsageQty = consumeResult.UsageQuantity
|
||||
adjustmentStock.PendingQty = consumeResult.PendingQuantity
|
||||
}
|
||||
|
||||
// Save AdjustmentStock record
|
||||
if err := s.AdjustmentStockRepository.WithTx(tx).CreateOne(ctx, adjustmentStock, nil); err != nil {
|
||||
s.Log.Errorf("Failed to create adjustment stock: %+v", err)
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create adjustment stock record")
|
||||
}
|
||||
|
||||
// Update ProductWarehouse quantity (for backward compatibility/reporting)
|
||||
|
||||
@@ -295,16 +295,17 @@ func (r *RecordingRepositoryImpl) GetTotalChickinByProjectFlockKandang(tx *gorm.
|
||||
|
||||
func (r *RecordingRepositoryImpl) GetFeedUsageInGrams(tx *gorm.DB, recordingID uint) (float64, error) {
|
||||
var rows []struct {
|
||||
UsageQty float64
|
||||
TotalQty float64
|
||||
UomName string
|
||||
}
|
||||
|
||||
if err := tx.
|
||||
Table("recording_stocks").
|
||||
Select("COALESCE(recording_stocks.usage_qty, 0) AS usage_qty, LOWER(uoms.name) AS uom_name").
|
||||
Select("COALESCE(recording_stocks.usage_qty, 0) + COALESCE(recording_stocks.pending_qty, 0) AS total_qty, LOWER(uoms.name) AS uom_name").
|
||||
Joins("JOIN product_warehouses ON product_warehouses.id = recording_stocks.product_warehouse_id").
|
||||
Joins("JOIN products ON products.id = product_warehouses.product_id").
|
||||
Joins("JOIN uoms ON uoms.id = products.uom_id").
|
||||
Joins("JOIN flags ON flags.flagable_id = products.id AND flags.flagable_type = ? AND UPPER(flags.name) = ?", entity.FlagableTypeProduct, "PAKAN").
|
||||
Where("recording_stocks.recording_id = ?", recordingID).
|
||||
Scan(&rows).Error; err != nil {
|
||||
return 0, err
|
||||
@@ -312,16 +313,16 @@ func (r *RecordingRepositoryImpl) GetFeedUsageInGrams(tx *gorm.DB, recordingID u
|
||||
|
||||
var total float64
|
||||
for _, row := range rows {
|
||||
if row.UsageQty <= 0 {
|
||||
if row.TotalQty <= 0 {
|
||||
continue
|
||||
}
|
||||
switch strings.TrimSpace(row.UomName) {
|
||||
case "kilogram", "kg", "kilograms", "kilo":
|
||||
total += row.UsageQty * 1000
|
||||
total += row.TotalQty * 1000
|
||||
case "gram", "g", "grams":
|
||||
total += row.UsageQty
|
||||
total += row.TotalQty
|
||||
default:
|
||||
total += row.UsageQty
|
||||
total += row.TotalQty
|
||||
}
|
||||
}
|
||||
return total, nil
|
||||
|
||||
@@ -247,11 +247,13 @@ func (s *recordingService) CreateOne(c *fiber.Ctx, req *validation.Create) (*ent
|
||||
}
|
||||
|
||||
mappedStocks := recordingutil.MapStocks(createdRecording.Id, req.Stocks)
|
||||
stockDesired := resetStockQuantitiesForFIFO(mappedStocks, s.FifoSvc != nil)
|
||||
if err := s.Repository.CreateStocks(tx, mappedStocks); err != nil {
|
||||
s.Log.Errorf("Failed to persist stocks: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
applyStockDesiredQuantities(mappedStocks, stockDesired, s.FifoSvc != nil)
|
||||
if err := s.consumeRecordingStocks(ctx, tx, mappedStocks); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -324,6 +326,49 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin
|
||||
hasDepletionChanges := req.Depletions != nil
|
||||
hasEggChanges := req.Eggs != nil
|
||||
|
||||
var existingStocks []entity.RecordingStock
|
||||
if hasStockChanges {
|
||||
existingStocks, err = s.Repository.ListStocks(tx, recordingEntity.Id)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to list existing stocks: %+v", err)
|
||||
return err
|
||||
}
|
||||
if s.Log != nil && s.Log.IsLevelEnabled(logrus.DebugLevel) {
|
||||
s.Log.WithFields(logrus.Fields{
|
||||
"recording_id": recordingEntity.Id,
|
||||
"existing": summarizeExistingStocks(existingStocks),
|
||||
"incoming": summarizeIncomingStocks(req.Stocks),
|
||||
}).Debug("recording update stock comparison")
|
||||
}
|
||||
if stocksMatch(existingStocks, req.Stocks) {
|
||||
hasStockChanges = false
|
||||
}
|
||||
}
|
||||
|
||||
var existingDepletions []entity.RecordingDepletion
|
||||
if hasDepletionChanges {
|
||||
existingDepletions, err = s.Repository.ListDepletions(tx, recordingEntity.Id)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to list existing depletions: %+v", err)
|
||||
return err
|
||||
}
|
||||
if depletionsMatch(existingDepletions, req.Depletions) {
|
||||
hasDepletionChanges = false
|
||||
}
|
||||
}
|
||||
|
||||
var existingEggs []entity.RecordingEgg
|
||||
if hasEggChanges {
|
||||
existingEggs, err = s.Repository.ListEggs(tx, recordingEntity.Id)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to list existing eggs: %+v", err)
|
||||
return err
|
||||
}
|
||||
if eggsMatch(existingEggs, req.Eggs) {
|
||||
hasEggChanges = false
|
||||
}
|
||||
}
|
||||
|
||||
if !hasStockChanges && !hasDepletionChanges && !hasEggChanges {
|
||||
return nil
|
||||
}
|
||||
@@ -355,39 +400,12 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin
|
||||
}
|
||||
|
||||
if hasStockChanges {
|
||||
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.releaseRecordingStocks(ctx, tx, existingStocks); err != nil {
|
||||
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.consumeRecordingStocks(ctx, tx, mappedStocks); err != nil {
|
||||
if err := s.syncRecordingStocks(ctx, tx, recordingEntity.Id, existingStocks, req.Stocks); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if hasDepletionChanges {
|
||||
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
|
||||
@@ -406,12 +424,6 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin
|
||||
}
|
||||
|
||||
if hasEggChanges {
|
||||
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
|
||||
@@ -429,7 +441,7 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin
|
||||
}
|
||||
}
|
||||
|
||||
if hasStockChanges || hasDepletionChanges {
|
||||
if hasStockChanges || hasDepletionChanges || hasEggChanges {
|
||||
if err := s.computeAndUpdateMetrics(ctx, tx, recordingEntity); err != nil {
|
||||
s.Log.Errorf("Failed to recompute recording metrics: %+v", err)
|
||||
return err
|
||||
@@ -680,12 +692,27 @@ func (s *recordingService) consumeRecordingStocks(ctx context.Context, tx *gorm.
|
||||
if stock.UsageQty != nil {
|
||||
desired = *stock.UsageQty
|
||||
}
|
||||
var pending float64
|
||||
if stock.PendingQty != nil {
|
||||
pending = *stock.PendingQty
|
||||
}
|
||||
desiredTotal := desired + pending
|
||||
|
||||
if s.Log != nil && s.Log.IsLevelEnabled(logrus.DebugLevel) {
|
||||
s.Log.WithFields(logrus.Fields{
|
||||
"recording_stock_id": stock.Id,
|
||||
"product_warehouse_id": stock.ProductWarehouseId,
|
||||
"desired_usage_qty": desired,
|
||||
"desired_pending_qty": pending,
|
||||
"desired_total_qty": desiredTotal,
|
||||
}).Debug("recording fifo consume start")
|
||||
}
|
||||
|
||||
result, err := s.FifoSvc.Consume(ctx, commonSvc.StockConsumeRequest{
|
||||
UsableKey: recordingStockUsableKey,
|
||||
UsableID: stock.Id,
|
||||
ProductWarehouseID: stock.ProductWarehouseId,
|
||||
Quantity: desired,
|
||||
Quantity: desiredTotal,
|
||||
AllowPending: true,
|
||||
Tx: tx,
|
||||
})
|
||||
@@ -694,6 +721,17 @@ func (s *recordingService) consumeRecordingStocks(ctx context.Context, tx *gorm.
|
||||
return err
|
||||
}
|
||||
|
||||
if s.Log != nil && s.Log.IsLevelEnabled(logrus.DebugLevel) {
|
||||
s.Log.WithFields(logrus.Fields{
|
||||
"recording_stock_id": stock.Id,
|
||||
"product_warehouse_id": stock.ProductWarehouseId,
|
||||
"result_usage_qty": result.UsageQuantity,
|
||||
"result_pending_qty": result.PendingQuantity,
|
||||
"released_qty": result.ReleasedQuantity,
|
||||
"added_allocations": len(result.AddedAllocations),
|
||||
}).Debug("recording fifo consume result")
|
||||
}
|
||||
|
||||
if err := s.Repository.UpdateStockUsage(tx, stock.Id, result.UsageQuantity, result.PendingQuantity); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -716,6 +754,23 @@ func (s *recordingService) releaseRecordingStocks(ctx context.Context, tx *gorm.
|
||||
continue
|
||||
}
|
||||
|
||||
var usage float64
|
||||
var pending float64
|
||||
if stock.UsageQty != nil {
|
||||
usage = *stock.UsageQty
|
||||
}
|
||||
if stock.PendingQty != nil {
|
||||
pending = *stock.PendingQty
|
||||
}
|
||||
if s.Log != nil && s.Log.IsLevelEnabled(logrus.DebugLevel) {
|
||||
s.Log.WithFields(logrus.Fields{
|
||||
"recording_stock_id": stock.Id,
|
||||
"product_warehouse_id": stock.ProductWarehouseId,
|
||||
"current_usage_qty": usage,
|
||||
"current_pending_qty": pending,
|
||||
}).Debug("recording fifo release start")
|
||||
}
|
||||
|
||||
if err := s.FifoSvc.ReleaseUsage(ctx, commonSvc.StockReleaseRequest{
|
||||
UsableKey: recordingStockUsableKey,
|
||||
UsableID: stock.Id,
|
||||
@@ -771,6 +826,288 @@ func (s *recordingService) adjustProductWarehouseQuantities(ctx context.Context,
|
||||
return s.ProductWarehouseRepo.AdjustQuantities(ctx, deltas, func(*gorm.DB) *gorm.DB { return tx })
|
||||
}
|
||||
|
||||
type desiredStock struct {
|
||||
Usage float64
|
||||
Pending float64
|
||||
}
|
||||
|
||||
func resetStockQuantitiesForFIFO(stocks []entity.RecordingStock, enabled bool) []desiredStock {
|
||||
desired := make([]desiredStock, len(stocks))
|
||||
for i := range stocks {
|
||||
if stocks[i].UsageQty != nil {
|
||||
desired[i].Usage = *stocks[i].UsageQty
|
||||
}
|
||||
if stocks[i].PendingQty != nil {
|
||||
desired[i].Pending = *stocks[i].PendingQty
|
||||
}
|
||||
if !enabled {
|
||||
continue
|
||||
}
|
||||
zero := 0.0
|
||||
stocks[i].UsageQty = &zero
|
||||
stocks[i].PendingQty = &zero
|
||||
}
|
||||
return desired
|
||||
}
|
||||
|
||||
func applyStockDesiredQuantities(stocks []entity.RecordingStock, desired []desiredStock, enabled bool) {
|
||||
if !enabled {
|
||||
return
|
||||
}
|
||||
for i := range stocks {
|
||||
if i >= len(desired) {
|
||||
break
|
||||
}
|
||||
usage := desired[i].Usage
|
||||
pending := desired[i].Pending
|
||||
stocks[i].UsageQty = &usage
|
||||
stocks[i].PendingQty = &pending
|
||||
}
|
||||
}
|
||||
|
||||
func (s *recordingService) syncRecordingStocks(
|
||||
ctx context.Context,
|
||||
tx *gorm.DB,
|
||||
recordingID uint,
|
||||
existing []entity.RecordingStock,
|
||||
incoming []validation.Stock,
|
||||
) error {
|
||||
if s.FifoSvc == nil {
|
||||
if err := s.Repository.DeleteStocks(tx, recordingID); err != nil {
|
||||
return err
|
||||
}
|
||||
mapped := recordingutil.MapStocks(recordingID, incoming)
|
||||
return s.Repository.CreateStocks(tx, mapped)
|
||||
}
|
||||
|
||||
existingByWarehouse := make(map[uint][]entity.RecordingStock)
|
||||
for _, stock := range existing {
|
||||
existingByWarehouse[stock.ProductWarehouseId] = append(existingByWarehouse[stock.ProductWarehouseId], stock)
|
||||
}
|
||||
|
||||
stocksToConsume := make([]entity.RecordingStock, 0, len(incoming))
|
||||
for _, item := range incoming {
|
||||
list := existingByWarehouse[item.ProductWarehouseId]
|
||||
var stock entity.RecordingStock
|
||||
if len(list) > 0 {
|
||||
stock = list[0]
|
||||
existingByWarehouse[item.ProductWarehouseId] = list[1:]
|
||||
} else {
|
||||
zero := 0.0
|
||||
stock = entity.RecordingStock{
|
||||
RecordingId: recordingID,
|
||||
ProductWarehouseId: item.ProductWarehouseId,
|
||||
UsageQty: &zero,
|
||||
PendingQty: &zero,
|
||||
}
|
||||
if err := tx.Create(&stock).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
desired := item.Qty
|
||||
stock.UsageQty = &desired
|
||||
if item.PendingQty != nil {
|
||||
pending := *item.PendingQty
|
||||
stock.PendingQty = &pending
|
||||
}
|
||||
stocksToConsume = append(stocksToConsume, stock)
|
||||
}
|
||||
|
||||
var leftovers []entity.RecordingStock
|
||||
for _, list := range existingByWarehouse {
|
||||
leftovers = append(leftovers, list...)
|
||||
}
|
||||
if len(leftovers) > 0 {
|
||||
if err := s.releaseRecordingStocks(ctx, tx, leftovers); err != nil {
|
||||
return err
|
||||
}
|
||||
ids := make([]uint, 0, len(leftovers))
|
||||
for _, stock := range leftovers {
|
||||
if stock.Id != 0 {
|
||||
ids = append(ids, stock.Id)
|
||||
}
|
||||
}
|
||||
if len(ids) > 0 {
|
||||
if err := tx.Where("id IN ?", ids).Delete(&entity.RecordingStock{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(stocksToConsume) == 0 {
|
||||
return nil
|
||||
}
|
||||
return s.consumeRecordingStocks(ctx, tx, stocksToConsume)
|
||||
}
|
||||
|
||||
type eggTotals struct {
|
||||
Qty int
|
||||
Weight float64
|
||||
}
|
||||
|
||||
type stockTotals struct {
|
||||
Usage float64
|
||||
Pending float64
|
||||
Total float64
|
||||
}
|
||||
|
||||
func summarizeExistingStocks(stocks []entity.RecordingStock) map[uint]stockTotals {
|
||||
totals := make(map[uint]stockTotals)
|
||||
for _, stock := range stocks {
|
||||
var usage float64
|
||||
var pending float64
|
||||
if stock.UsageQty != nil {
|
||||
usage = *stock.UsageQty
|
||||
}
|
||||
if stock.PendingQty != nil {
|
||||
pending = *stock.PendingQty
|
||||
}
|
||||
current := totals[stock.ProductWarehouseId]
|
||||
current.Usage += usage
|
||||
current.Pending += pending
|
||||
current.Total += usage + pending
|
||||
totals[stock.ProductWarehouseId] = current
|
||||
}
|
||||
return totals
|
||||
}
|
||||
|
||||
func summarizeIncomingStocks(stocks []validation.Stock) map[uint]stockTotals {
|
||||
totals := make(map[uint]stockTotals)
|
||||
for _, stock := range stocks {
|
||||
var pending float64
|
||||
if stock.PendingQty != nil {
|
||||
pending = *stock.PendingQty
|
||||
}
|
||||
current := totals[stock.ProductWarehouseId]
|
||||
current.Usage += stock.Qty
|
||||
current.Pending += pending
|
||||
current.Total += stock.Qty + pending
|
||||
totals[stock.ProductWarehouseId] = current
|
||||
}
|
||||
return totals
|
||||
}
|
||||
|
||||
func stocksMatch(existing []entity.RecordingStock, incoming []validation.Stock) bool {
|
||||
hasPending := false
|
||||
for _, item := range incoming {
|
||||
if item.PendingQty != nil {
|
||||
hasPending = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
existingUsage := make(map[uint]float64)
|
||||
existingTotal := make(map[uint]float64)
|
||||
for _, stock := range existing {
|
||||
var usage float64
|
||||
var pending float64
|
||||
if stock.UsageQty != nil {
|
||||
usage = *stock.UsageQty
|
||||
}
|
||||
if stock.PendingQty != nil {
|
||||
pending = *stock.PendingQty
|
||||
}
|
||||
existingUsage[stock.ProductWarehouseId] += usage
|
||||
existingTotal[stock.ProductWarehouseId] += usage + pending
|
||||
}
|
||||
|
||||
incomingUsage := make(map[uint]float64)
|
||||
incomingTotal := make(map[uint]float64)
|
||||
for _, item := range incoming {
|
||||
var pending float64
|
||||
if item.PendingQty != nil {
|
||||
pending = *item.PendingQty
|
||||
}
|
||||
incomingUsage[item.ProductWarehouseId] += item.Qty
|
||||
incomingTotal[item.ProductWarehouseId] += item.Qty + pending
|
||||
}
|
||||
|
||||
if hasPending {
|
||||
return floatMapsMatch(existingTotal, incomingTotal)
|
||||
}
|
||||
return floatMapsMatch(existingUsage, incomingUsage)
|
||||
}
|
||||
|
||||
func depletionsMatch(existing []entity.RecordingDepletion, incoming []validation.Depletion) bool {
|
||||
existingTotals := make(map[uint]float64)
|
||||
for _, dep := range existing {
|
||||
existingTotals[dep.ProductWarehouseId] += dep.Qty
|
||||
}
|
||||
|
||||
incomingTotals := make(map[uint]float64)
|
||||
for _, dep := range incoming {
|
||||
incomingTotals[dep.ProductWarehouseId] += dep.Qty
|
||||
}
|
||||
|
||||
return floatMapsMatch(existingTotals, incomingTotals)
|
||||
}
|
||||
|
||||
func eggsMatch(existing []entity.RecordingEgg, incoming []validation.Egg) bool {
|
||||
existingTotals := make(map[uint]eggTotals)
|
||||
for _, egg := range existing {
|
||||
weight := 0.0
|
||||
if egg.Weight != nil {
|
||||
weight = *egg.Weight
|
||||
}
|
||||
current := existingTotals[egg.ProductWarehouseId]
|
||||
current.Qty += egg.Qty
|
||||
current.Weight += float64(egg.Qty) * weight
|
||||
existingTotals[egg.ProductWarehouseId] = current
|
||||
}
|
||||
|
||||
incomingTotals := make(map[uint]eggTotals)
|
||||
for _, egg := range incoming {
|
||||
weight := 0.0
|
||||
if egg.Weight != nil {
|
||||
weight = *egg.Weight
|
||||
}
|
||||
current := incomingTotals[egg.ProductWarehouseId]
|
||||
current.Qty += egg.Qty
|
||||
current.Weight += float64(egg.Qty) * weight
|
||||
incomingTotals[egg.ProductWarehouseId] = current
|
||||
}
|
||||
|
||||
if len(existingTotals) != len(incomingTotals) {
|
||||
return false
|
||||
}
|
||||
|
||||
for key, existingTotal := range existingTotals {
|
||||
incomingTotal, ok := incomingTotals[key]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if existingTotal.Qty != incomingTotal.Qty {
|
||||
return false
|
||||
}
|
||||
if !floatNearlyEqual(existingTotal.Weight, incomingTotal.Weight) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func floatMapsMatch(a, b map[uint]float64) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for key, value := range a {
|
||||
other, ok := b[key]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if !floatNearlyEqual(value, other) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func floatNearlyEqual(a, b float64) bool {
|
||||
return math.Abs(a-b) <= 0.000001
|
||||
}
|
||||
|
||||
func (s *recordingService) computeAndUpdateMetrics(ctx context.Context, tx *gorm.DB, recording *entity.Recording) error {
|
||||
day := 0
|
||||
if recording.Day != nil {
|
||||
|
||||
Reference in New Issue
Block a user