mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
fix: chickin include stock allocation, fix calculation hpp
This commit is contained in:
@@ -366,6 +366,7 @@ func countActiveUsableAllocations(ctx context.Context, db *gorm.DB, usableType s
|
||||
Table("stock_allocations").
|
||||
Where("usable_type = ? AND usable_id = ?", usableType, usableID).
|
||||
Where("status = ?", entity.StockAllocationStatusActive).
|
||||
Where("allocation_purpose = ?", entity.StockAllocationPurposeConsume).
|
||||
Count(&count).Error
|
||||
return count, err
|
||||
}
|
||||
@@ -376,19 +377,20 @@ func countActiveStockableAllocations(ctx context.Context, db *gorm.DB, stockable
|
||||
Table("stock_allocations").
|
||||
Where("stockable_type = ? AND stockable_id = ?", stockableType, stockableID).
|
||||
Where("status = ?", entity.StockAllocationStatusActive).
|
||||
Where("allocation_purpose = ?", entity.StockAllocationPurposeConsume).
|
||||
Count(&count).Error
|
||||
return count, err
|
||||
}
|
||||
|
||||
func hardDeleteUsableAllocations(ctx context.Context, tx *gorm.DB, usableType string, usableID uint) error {
|
||||
return tx.WithContext(ctx).
|
||||
Exec("DELETE FROM stock_allocations WHERE usable_type = ? AND usable_id = ?", usableType, usableID).
|
||||
Exec("DELETE FROM stock_allocations WHERE usable_type = ? AND usable_id = ? AND allocation_purpose = ?", usableType, usableID, entity.StockAllocationPurposeConsume).
|
||||
Error
|
||||
}
|
||||
|
||||
func hardDeleteStockableAllocations(ctx context.Context, tx *gorm.DB, stockableType string, stockableID uint) error {
|
||||
return tx.WithContext(ctx).
|
||||
Exec("DELETE FROM stock_allocations WHERE stockable_type = ? AND stockable_id = ?", stockableType, stockableID).
|
||||
Exec("DELETE FROM stock_allocations WHERE stockable_type = ? AND stockable_id = ? AND allocation_purpose = ?", stockableType, stockableID, entity.StockAllocationPurposeConsume).
|
||||
Error
|
||||
}
|
||||
|
||||
|
||||
@@ -324,6 +324,7 @@ func countActiveAllocations(ctx context.Context, db *gorm.DB, usableType string,
|
||||
Table("stock_allocations").
|
||||
Where("usable_type = ? AND usable_id = ?", usableType, usableID).
|
||||
Where("status = ?", entity.StockAllocationStatusActive).
|
||||
Where("allocation_purpose = ?", entity.StockAllocationPurposeConsume).
|
||||
Count(&count).Error
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -14,6 +15,7 @@ import (
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/config"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/database"
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils/fifo"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -181,6 +183,8 @@ func main() {
|
||||
orphanPopulationRows := int64(0)
|
||||
syncedPopulationQtyRows := int64(0)
|
||||
syncedPopulationUsedRows := int64(0)
|
||||
traceReleasedRows := int64(0)
|
||||
traceInsertedRows := int64(0)
|
||||
if rowsOrphan, rowsQty, rowsUsed, err := resyncProjectFlockPopulation(ctx, db, projectFlockKandangID); err != nil {
|
||||
fmt.Printf("FAIL population_resync project_flock_kandang_id=%d error=%v\n", projectFlockKandangID, err)
|
||||
failedApply++
|
||||
@@ -196,9 +200,22 @@ func main() {
|
||||
)
|
||||
}
|
||||
|
||||
if released, inserted, err := resyncChickinTraceByProjectFlockKandang(ctx, db, fifoStockV2Svc, projectFlockKandangID); err != nil {
|
||||
fmt.Printf("FAIL chickin_trace_resync project_flock_kandang_id=%d error=%v\n", projectFlockKandangID, err)
|
||||
failedApply++
|
||||
} else {
|
||||
traceReleasedRows = released
|
||||
traceInsertedRows = inserted
|
||||
fmt.Printf(
|
||||
"SYNC chickin_trace released=%d inserted=%d\n",
|
||||
traceReleasedRows,
|
||||
traceInsertedRows,
|
||||
)
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf(
|
||||
"Summary: planned=%d skipped_pw=%d failed_resolve=%d applied=%d failed_apply=%d population_orphan=%d population_qty_synced=%d population_used_synced=%d\n",
|
||||
"Summary: planned=%d skipped_pw=%d failed_resolve=%d applied=%d failed_apply=%d population_orphan=%d population_qty_synced=%d population_used_synced=%d trace_released=%d trace_inserted=%d\n",
|
||||
len(targets),
|
||||
skippedPW,
|
||||
failedResolve,
|
||||
@@ -207,6 +224,8 @@ func main() {
|
||||
orphanPopulationRows,
|
||||
syncedPopulationQtyRows,
|
||||
syncedPopulationUsedRows,
|
||||
traceReleasedRows,
|
||||
traceInsertedRows,
|
||||
)
|
||||
if failedResolve > 0 || failedApply > 0 {
|
||||
os.Exit(1)
|
||||
@@ -448,6 +467,7 @@ func resyncProjectFlockPopulation(ctx context.Context, db *gorm.DB, projectFlock
|
||||
FROM stock_allocations sa
|
||||
WHERE sa.stockable_type = 'PROJECT_FLOCK_POPULATION'
|
||||
AND sa.status = 'ACTIVE'
|
||||
AND sa.allocation_purpose = 'CONSUME'
|
||||
GROUP BY sa.stockable_id
|
||||
)
|
||||
UPDATE project_flock_populations p
|
||||
@@ -463,3 +483,167 @@ func resyncProjectFlockPopulation(ctx context.Context, db *gorm.DB, projectFlock
|
||||
|
||||
return orphanResult.RowsAffected, qtyResult.RowsAffected, usedResult.RowsAffected, nil
|
||||
}
|
||||
|
||||
func resyncChickinTraceByProjectFlockKandang(
|
||||
ctx context.Context,
|
||||
db *gorm.DB,
|
||||
fifoStockV2Svc commonSvc.FifoStockV2Service,
|
||||
projectFlockKandangID uint,
|
||||
) (int64, int64, error) {
|
||||
if projectFlockKandangID == 0 {
|
||||
return 0, 0, nil
|
||||
}
|
||||
|
||||
var productWarehouseIDs []uint
|
||||
if err := db.WithContext(ctx).
|
||||
Table("project_chickins").
|
||||
Distinct("product_warehouse_id").
|
||||
Where("project_flock_kandang_id = ?", projectFlockKandangID).
|
||||
Where("deleted_at IS NULL").
|
||||
Order("product_warehouse_id ASC").
|
||||
Pluck("product_warehouse_id", &productWarehouseIDs).Error; err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
if len(productWarehouseIDs) == 0 {
|
||||
return 0, 0, nil
|
||||
}
|
||||
|
||||
totalReleased := int64(0)
|
||||
totalInserted := int64(0)
|
||||
|
||||
for _, productWarehouseID := range productWarehouseIDs {
|
||||
var releasedRows int64
|
||||
var insertedRows int64
|
||||
|
||||
err := db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
flagGroups, err := resolveFlagGroupsByProductWarehouse(ctx, tx, productWarehouseID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(flagGroups) == 0 {
|
||||
return nil
|
||||
}
|
||||
flagGroupCode := strings.TrimSpace(flagGroups[0])
|
||||
if flagGroupCode == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
released := tx.WithContext(ctx).
|
||||
Table("stock_allocations").
|
||||
Where("product_warehouse_id = ?", productWarehouseID).
|
||||
Where("usable_type = ?", fifo.UsableKeyProjectChickin.String()).
|
||||
Where("allocation_purpose = ?", entity.StockAllocationPurposeTraceChickin).
|
||||
Where("status = ?", entity.StockAllocationStatusActive).
|
||||
Updates(map[string]any{
|
||||
"status": entity.StockAllocationStatusReleased,
|
||||
"released_at": time.Now(),
|
||||
"updated_at": time.Now(),
|
||||
"note": "chickin_trace_reflow_reset",
|
||||
})
|
||||
if released.Error != nil {
|
||||
return released.Error
|
||||
}
|
||||
releasedRows = released.RowsAffected
|
||||
|
||||
type chickinRow struct {
|
||||
ID uint `gorm:"column:id"`
|
||||
UsageQty float64 `gorm:"column:usage_qty"`
|
||||
ChickIn time.Time `gorm:"column:chick_in_date"`
|
||||
}
|
||||
chickins := make([]chickinRow, 0)
|
||||
if err := tx.WithContext(ctx).
|
||||
Table("project_chickins").
|
||||
Select("id, usage_qty, chick_in_date").
|
||||
Where("product_warehouse_id = ?", productWarehouseID).
|
||||
Where("deleted_at IS NULL").
|
||||
Where("usage_qty > 0").
|
||||
Order("chick_in_date ASC, id ASC").
|
||||
Scan(&chickins).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if len(chickins) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
gatherRows, err := fifoStockV2Svc.Gather(ctx, commonSvc.FifoStockV2GatherRequest{
|
||||
FlagGroupCode: flagGroupCode,
|
||||
Lane: "STOCKABLE",
|
||||
AllocationPurpose: entity.StockAllocationPurposeTraceChickin,
|
||||
IgnoreSourceUsed: true,
|
||||
ProductWarehouseID: productWarehouseID,
|
||||
Limit: 50000,
|
||||
Tx: tx,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(gatherRows) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
type lotKey struct {
|
||||
StockableType string
|
||||
StockableID uint
|
||||
}
|
||||
remainingByLot := make(map[lotKey]float64, len(gatherRows))
|
||||
for _, row := range gatherRows {
|
||||
key := lotKey{StockableType: row.Ref.LegacyTypeKey, StockableID: row.Ref.ID}
|
||||
remainingByLot[key] = row.AvailableQuantity
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
lotIndex := 0
|
||||
for _, chickinRow := range chickins {
|
||||
remaining := chickinRow.UsageQty
|
||||
for remaining > 1e-6 && lotIndex < len(gatherRows) {
|
||||
lot := gatherRows[lotIndex]
|
||||
key := lotKey{StockableType: lot.Ref.LegacyTypeKey, StockableID: lot.Ref.ID}
|
||||
available := remainingByLot[key]
|
||||
if available <= 1e-6 {
|
||||
lotIndex++
|
||||
continue
|
||||
}
|
||||
|
||||
portion := math.Min(remaining, available)
|
||||
if portion <= 1e-6 {
|
||||
lotIndex++
|
||||
continue
|
||||
}
|
||||
|
||||
insert := map[string]any{
|
||||
"product_warehouse_id": productWarehouseID,
|
||||
"stockable_type": lot.Ref.LegacyTypeKey,
|
||||
"stockable_id": lot.Ref.ID,
|
||||
"usable_type": fifo.UsableKeyProjectChickin.String(),
|
||||
"usable_id": chickinRow.ID,
|
||||
"qty": portion,
|
||||
"status": entity.StockAllocationStatusActive,
|
||||
"allocation_purpose": entity.StockAllocationPurposeTraceChickin,
|
||||
"engine_version": "v2",
|
||||
"flag_group_code": flagGroupCode,
|
||||
"function_code": "CHICKIN_TRACE",
|
||||
"created_at": now,
|
||||
"updated_at": now,
|
||||
}
|
||||
if err := tx.WithContext(ctx).Table("stock_allocations").Create(insert).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
insertedRows++
|
||||
remaining -= portion
|
||||
remainingByLot[key] = available - portion
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return totalReleased, totalInserted, err
|
||||
}
|
||||
|
||||
totalReleased += releasedRows
|
||||
totalInserted += insertedRows
|
||||
}
|
||||
|
||||
return totalReleased, totalInserted, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"os"
|
||||
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/config"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/database"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils/fifo"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type mismatchRow struct {
|
||||
ChickinID uint `gorm:"column:chickin_id"`
|
||||
ProjectFlockKandang uint `gorm:"column:project_flock_kandang_id"`
|
||||
ProductWarehouseID uint `gorm:"column:product_warehouse_id"`
|
||||
UsageQty float64 `gorm:"column:usage_qty"`
|
||||
TraceQty float64 `gorm:"column:trace_qty"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
var projectFlockKandangID uint
|
||||
flag.UintVar(&projectFlockKandangID, "project-flock-kandang-id", 0, "Optional project flock kandang scope")
|
||||
flag.Parse()
|
||||
|
||||
ctx := context.Background()
|
||||
db := database.Connect(config.DBHost, config.DBName)
|
||||
|
||||
rows, err := loadTraceMismatches(ctx, db, projectFlockKandangID)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to load trace mismatches: %v", err)
|
||||
}
|
||||
|
||||
activeConsumeRows, err := countActiveConsumeProjectChickin(ctx, db, projectFlockKandangID)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to count active consume rows: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Scope project_flock_kandang_id=%d\n", projectFlockKandangID)
|
||||
fmt.Printf("Mismatched chickin trace rows: %d\n", len(rows))
|
||||
fmt.Printf("Active PROJECT_CHICKIN consume rows: %d\n", activeConsumeRows)
|
||||
|
||||
if len(rows) > 0 {
|
||||
for _, row := range rows {
|
||||
fmt.Printf(
|
||||
"MISMATCH chickin_id=%d pfk=%d pw=%d usage=%.3f trace=%.3f diff=%.3f\n",
|
||||
row.ChickinID,
|
||||
row.ProjectFlockKandang,
|
||||
row.ProductWarehouseID,
|
||||
row.UsageQty,
|
||||
row.TraceQty,
|
||||
row.TraceQty-row.UsageQty,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if len(rows) > 0 || activeConsumeRows > 0 {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func loadTraceMismatches(ctx context.Context, db *gorm.DB, projectFlockKandangID uint) ([]mismatchRow, error) {
|
||||
query := db.WithContext(ctx).
|
||||
Table("project_chickins pc").
|
||||
Select(`
|
||||
pc.id AS chickin_id,
|
||||
pc.project_flock_kandang_id,
|
||||
pc.product_warehouse_id,
|
||||
COALESCE(pc.usage_qty, 0) AS usage_qty,
|
||||
COALESCE(SUM(sa.qty), 0) AS trace_qty
|
||||
`).
|
||||
Joins(`
|
||||
LEFT JOIN stock_allocations sa
|
||||
ON sa.usable_type = ?
|
||||
AND sa.usable_id = pc.id
|
||||
AND sa.status = 'ACTIVE'
|
||||
AND sa.allocation_purpose = 'TRACE_CHICKIN'
|
||||
`, fifo.UsableKeyProjectChickin.String()).
|
||||
Where("pc.deleted_at IS NULL").
|
||||
Where("COALESCE(pc.usage_qty,0) > 0").
|
||||
Group("pc.id, pc.project_flock_kandang_id, pc.product_warehouse_id, pc.usage_qty")
|
||||
|
||||
if projectFlockKandangID > 0 {
|
||||
query = query.Where("pc.project_flock_kandang_id = ?", projectFlockKandangID)
|
||||
}
|
||||
|
||||
rows := make([]mismatchRow, 0)
|
||||
if err := query.Scan(&rows).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := make([]mismatchRow, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
if math.Abs(row.TraceQty-row.UsageQty) > 1e-3 {
|
||||
out = append(out, row)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func countActiveConsumeProjectChickin(ctx context.Context, db *gorm.DB, projectFlockKandangID uint) (int64, error) {
|
||||
q := db.WithContext(ctx).
|
||||
Table("stock_allocations sa").
|
||||
Joins("JOIN project_chickins pc ON pc.id = sa.usable_id").
|
||||
Where("sa.usable_type = ?", fifo.UsableKeyProjectChickin.String()).
|
||||
Where("sa.status = 'ACTIVE'").
|
||||
Where("sa.allocation_purpose = 'CONSUME'")
|
||||
|
||||
if projectFlockKandangID > 0 {
|
||||
q = q.Where("pc.project_flock_kandang_id = ?", projectFlockKandangID)
|
||||
}
|
||||
|
||||
var count int64
|
||||
if err := q.Count(&count).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
Reference in New Issue
Block a user