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:
ragilap
2025-10-31 16:03:05 +07:00
parent 614da067f7
commit f869943573
38 changed files with 2808 additions and 1259 deletions
+64 -274
View File
@@ -8,7 +8,6 @@ import (
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
"gitlab.com/mbugroup/lti-api.git/internal/utils"
approvalutils "gitlab.com/mbugroup/lti-api.git/internal/utils/approvals"
"gorm.io/gorm"
)
@@ -41,21 +40,14 @@ func Run(db *gorm.DB) error {
return err
}
flocks, err := seedFlocks(tx, adminID)
if err != nil {
if _, err := seedFlocks(tx, adminID); err != nil {
return err
}
fcrs, err := seedFcr(tx, adminID)
if err != nil {
if _, err := seedFcr(tx, adminID); err != nil {
return err
}
if err := seedProjectFlocks(tx, adminID, flocks, areas, fcrs, locations); err != nil {
return err
}
kandangs, err := seedKandangs(tx, adminID, locations, users)
if err != nil {
return err
@@ -93,10 +85,6 @@ func Run(db *gorm.DB) error {
if err := seedTransferStock(tx, adminID); err != nil {
return err
}
if err := seedChickin(tx, adminID); err != nil {
return err
}
fmt.Println("✅ Master data seeding completed")
return nil
})
@@ -243,158 +231,12 @@ func seedFlocks(tx *gorm.DB, createdBy uint) (map[string]uint, error) {
return result, nil
}
func seedProjectFlocks(
tx *gorm.DB,
createdBy uint,
flocks, areas, fcrs, locations map[string]uint,
) error {
seeds := []struct {
Key string
Flock string
Area string
Category utils.ProjectFlockCategory
Fcr string
Location string
Period int
}{
{
Key: "Singaparna Period 1",
Flock: "Flock Priangan",
Area: "Priangan",
Category: utils.ProjectFlockCategoryGrowing,
Fcr: "FCR Layer",
Location: "Singaparna",
Period: 1,
},
{
Key: "Cikaum Period 1",
Flock: "Flock Banten",
Area: "Banten",
Category: utils.ProjectFlockCategoryGrowing,
Fcr: "FCR Layer",
Location: "Cikaum",
Period: 1,
},
}
for _, seed := range seeds {
flockID, ok := flocks[seed.Flock]
if !ok {
return fmt.Errorf("floc %s not seeded", seed.Flock)
}
areaID, ok := areas[seed.Area]
if !ok {
return fmt.Errorf("area %s not seeded", seed.Area)
}
fcrID, ok := fcrs[seed.Fcr]
if !ok {
return fmt.Errorf("fcr %s not seeded", seed.Fcr)
}
locationID, ok := locations[seed.Location]
if !ok {
return fmt.Errorf("location %s not seeded", seed.Location)
}
var projectFlock entity.ProjectFlock
err := tx.Where(
"flock_id = ? AND area_id = ? AND category = ? AND fcr_id = ? AND location_id = ? AND period = ?",
flockID, areaID, seed.Category, fcrID, locationID, seed.Period,
).First(&projectFlock).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
projectFlock = entity.ProjectFlock{
FlockId: flockID,
AreaId: areaID,
Category: string(seed.Category),
FcrId: fcrID,
LocationId: locationID,
Period: seed.Period,
CreatedBy: createdBy,
}
if err := tx.Create(&projectFlock).Error; err != nil {
return err
}
} else if err != nil {
return err
} else {
if err := tx.Model(&entity.ProjectFlock{}).Where("id = ?", projectFlock.Id).Updates(map[string]any{
"flock_id": flockID,
"area_id": areaID,
"category": string(seed.Category),
"fcr_id": fcrID,
"location_id": locationID,
"period": seed.Period,
}).Error; err != nil {
return err
}
}
if err := ensureProjectFlockApprovals(tx, projectFlock.Id, createdBy); err != nil {
return err
}
}
return nil
}
func ensureProjectFlockApprovals(tx *gorm.DB, projectFlockID uint, actorID uint) error {
if projectFlockID == 0 || actorID == 0 {
return nil
}
workflow := utils.ApprovalWorkflowProjectFlock.String()
steps := []struct {
step approvalutils.ApprovalStep
action entity.ApprovalAction
}{
{step: utils.ProjectFlockStepPengajuan, action: entity.ApprovalActionCreated},
{step: utils.ProjectFlockStepAktif, action: entity.ApprovalActionApproved},
}
for _, cfg := range steps {
var count int64
if err := tx.Model(&entity.Approval{}).
Where("approvable_type = ? AND approvable_id = ? AND step_number = ?", workflow, projectFlockID, uint16(cfg.step)).
Count(&count).Error; err != nil {
return err
}
if count > 0 {
continue
}
stepName, ok := utils.ProjectFlockApprovalSteps[cfg.step]
if !ok || strings.TrimSpace(stepName) == "" {
stepName = fmt.Sprintf("Step %d", cfg.step)
}
var actionPtr *entity.ApprovalAction
action := cfg.action
actionPtr = &action
record := entity.Approval{
ApprovableType: workflow,
ApprovableId: projectFlockID,
StepNumber: uint16(cfg.step),
StepName: stepName,
Action: actionPtr,
ActionBy: uintPtr(actorID),
}
if err := tx.Create(&record).Error; err != nil {
return err
}
}
return nil
}
func seedKandangs(tx *gorm.DB, createdBy uint, locations map[string]uint, users map[string]uint) (map[string]uint, error) {
seeds := []struct {
Name string
Status utils.KandangStatus
Location string
PicKey string
Name string
Status utils.KandangStatus
Location string
PicKey string
}{
{Name: "Singaparna 1", Status: utils.KandangStatusNonActive, Location: "Singaparna", PicKey: "admin"},
{Name: "Singaparna 2", Status: utils.KandangStatusNonActive, Location: "Singaparna", PicKey: "admin"},
@@ -414,16 +256,15 @@ func seedKandangs(tx *gorm.DB, createdBy uint, locations map[string]uint, users
return nil, fmt.Errorf("user %s not seeded", seed.PicKey)
}
var kandang entity.Kandang
err := tx.Where("name = ?", seed.Name).First(&kandang).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
kandang = entity.Kandang{
Name: seed.Name,
Status: string(seed.Status),
LocationId: locID,
PicId: picID,
CreatedBy: createdBy,
Name: seed.Name,
Status: string(seed.Status),
LocationId: locID,
PicId: picID,
CreatedBy: createdBy,
}
if err := tx.Create(&kandang).Error; err != nil {
return nil, err
@@ -446,7 +287,6 @@ func seedKandangs(tx *gorm.DB, createdBy uint, locations map[string]uint, users
return result, nil
}
func seedWarehouses(tx *gorm.DB, createdBy uint, areas map[string]uint, locations map[string]uint, kandangs map[string]uint) error {
seeds := []struct {
Name string
@@ -525,6 +365,7 @@ func seedProductCategories(tx *gorm.DB, createdBy uint) (map[string]uint, error)
}{
{"Bahan Baku", "RAW"},
{"Day Old Chick", "DOC"},
{"Telur", "EGG"},
}
result := make(map[string]uint, len(seeds))
@@ -739,6 +580,22 @@ func seedProducts(tx *gorm.DB, createdBy uint, uoms map[string]uint, categories
Suppliers: []string{"PT CHAROEN POKPHAND INDONESIA Tbk"},
Flags: []utils.FlagType{utils.FlagPakan, utils.FlagStarter},
},
{
Name: "Telur Konsumsi Baik",
Brand: "Layer Farm",
Sku: "EGG-GOOD",
Uom: "Unit",
Category: "Telur",
Price: 1800,
},
{
Name: "Telur Pecah",
Brand: "Layer Farm",
Sku: "EGG-CRACK",
Uom: "Unit",
Category: "Telur",
Price: 900,
},
}
for _, seed := range seeds {
@@ -978,25 +835,44 @@ func seedBanks(tx *gorm.DB, createdBy uint) error {
}
func seedProductWarehouse(tx *gorm.DB, createdBy uint) error {
seeds := []struct {
ProductID uint
WarehouseID uint
Quantity float64
ProductName string
WarehouseName string
Quantity float64
}{
{ProductID: 1, WarehouseID: 1, Quantity: 100},
{ProductID: 2, WarehouseID: 2, Quantity: 200},
{ProductID: 2, WarehouseID: 1, Quantity: 300},
{ProductID: 1, WarehouseID: 3, Quantity: 5000},
{ProductName: "DOC Broiler", WarehouseName: "Gudang Priangan", Quantity: 100},
{ProductName: "281 SPECIAL STARTER", WarehouseName: "Gudang Singaparna", Quantity: 200},
{ProductName: "281 SPECIAL STARTER", WarehouseName: "Gudang Banten", Quantity: 300},
{ProductName: "DOC Broiler", WarehouseName: "Gudang Singaparna 1", Quantity: 5000},
{ProductName: "Telur Konsumsi Baik", WarehouseName: "Gudang Singaparna 1", Quantity: 600},
{ProductName: "Telur Pecah", WarehouseName: "Gudang Singaparna 1", Quantity: 80},
{ProductName: "Telur Konsumsi Baik", WarehouseName: "Gudang Cikaum 1", Quantity: 450},
{ProductName: "Telur Pecah", WarehouseName: "Gudang Cikaum 1", Quantity: 60},
}
for _, seed := range seeds {
var product entity.Product
if err := tx.Where("name = ?", seed.ProductName).First(&product).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fmt.Errorf("product %q not found for product warehouse seeding", seed.ProductName)
}
return err
}
var warehouse entity.Warehouse
if err := tx.Where("name = ?", seed.WarehouseName).First(&warehouse).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fmt.Errorf("warehouse %q not found for product warehouse seeding", seed.WarehouseName)
}
return err
}
var productWarehouse entity.ProductWarehouse
err := tx.Where("product_id = ? AND warehouse_id = ?", seed.ProductID, seed.WarehouseID).First(&productWarehouse).Error
err := tx.Where("product_id = ? AND warehouse_id = ?", product.Id, warehouse.Id).First(&productWarehouse).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
productWarehouse = entity.ProductWarehouse{
ProductId: seed.ProductID,
WarehouseId: seed.WarehouseID,
ProductId: product.Id,
WarehouseId: warehouse.Id,
Quantity: seed.Quantity,
CreatedBy: createdBy,
}
@@ -1005,6 +881,12 @@ func seedProductWarehouse(tx *gorm.DB, createdBy uint) error {
}
} else if err != nil {
return err
} else {
if err := tx.Model(&productWarehouse).Updates(map[string]any{
"quantity": seed.Quantity,
}).Error; err != nil {
return err
}
}
}
@@ -1085,71 +967,6 @@ func seedTransferStock(tx *gorm.DB, createdBy uint) error {
return nil
}
func seedChickin(tx *gorm.DB, createdBy uint) error {
// gunakan identitas yang stabil, bukan ID pivot
seeds := []struct {
KandangName string
LocationName string
Period int
ChickInDate string
Quantity float64
Note string
}{
{"Singaparna 1", "Singaparna", 1, "2025-10-20", 100, "Seeder chickin 1"},
{"Cikaum 1", "Cikaum", 1, "2025-10-21", 200, "Seeder chickin 2"},
}
for _, s := range seeds {
pfkID, err := ensurePFK(tx, s.KandangName, s.LocationName, s.Period)
if err != nil { return err }
date, err := time.Parse("2006-01-02", s.ChickInDate)
if err != nil { return err }
// upsert project_chickin (idempotent)
var chickin entity.ProjectChickin
err = tx.Where("project_flock_kandang_id = ? AND chick_in_date = ?", pfkID, date).First(&chickin).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
chickin = entity.ProjectChickin{
ProjectFlockKandangId: pfkID,
ChickInDate: date,
Quantity: s.Quantity,
Note: s.Note,
CreatedBy: createdBy,
}
if err := tx.Create(&chickin).Error; err != nil { return err }
} else if err != nil {
return err
}
// upsert population
var pop entity.ProjectFlockPopulation
err = tx.Where("project_flock_kandang_id = ?", pfkID).First(&pop).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
pop = entity.ProjectFlockPopulation{
ProjectFlockKandangId: pfkID,
InitialQuantity: s.Quantity,
CurrentQuantity: s.Quantity,
ReservedQuantity: 0,
CreatedBy: createdBy,
}
if err := tx.Create(&pop).Error; err != nil { return err }
} else if err != nil {
return err
} else {
if err := tx.Model(&entity.ProjectFlockPopulation{}).
Where("id = ?", pop.Id).
Updates(map[string]any{
"initial_quantity": pop.InitialQuantity + s.Quantity,
"current_quantity": pop.CurrentQuantity + s.Quantity,
"reserved_quantity": 0,
}).Error; err != nil { return err }
}
}
return nil
}
func ptr[T any](v T) *T {
return &v
}
@@ -1165,30 +982,3 @@ func intPtr(v int) *int {
func uintPtr(v uint) *uint {
return &v
}
func ensurePFK(tx *gorm.DB, kandangName, locationName string, period int) (uint, error) {
var kandang entity.Kandang
if err := tx.Where("name = ?", kandangName).First(&kandang).Error; err != nil {
return 0, fmt.Errorf("kandang %q not found: %w", kandangName, err)
}
var loc entity.Location
if err := tx.Where("name = ?", locationName).First(&loc).Error; err != nil {
return 0, fmt.Errorf("location %q not found: %w", locationName, err)
}
var pf entity.ProjectFlock
if err := tx.Where("location_id = ? AND period = ?", loc.Id, period).First(&pf).Error; err != nil {
return 0, fmt.Errorf("project_flock for %s period %d not found: %w", locationName, period, err)
}
var pfk entity.ProjectFlockKandang
if err := tx.Where("project_flock_id = ? AND kandang_id = ?", pf.Id, kandang.Id).First(&pfk).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
pfk = entity.ProjectFlockKandang{ ProjectFlockId: pf.Id, KandangId: kandang.Id }
if err := tx.Create(&pfk).Error; err != nil {
return 0, fmt.Errorf("create pivot pfk(%d,%d) failed: %w", pf.Id, kandang.Id, err)
}
} else {
return 0, err
}
}
return pfk.Id, nil
}