mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
Merge branch 'dev/teguh' into 'feat/BE/US-75/chick-in-doc'
[FIX/BE][US#75] Adjust "chickin delete one" code to match backend standard See merge request mbugroup/lti-api!49
This commit is contained in:
@@ -0,0 +1 @@
|
||||
DROP TABLE IF EXISTS laying_transfers;
|
||||
@@ -0,0 +1,46 @@
|
||||
CREATE TABLE IF NOT EXISTS laying_transfers (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
from_project_flock_id BIGINT NOT NULL,
|
||||
to_project_flock_id BIGINT NOT NULL,
|
||||
transfer_date DATE NOT NULL,
|
||||
total_qty INTEGER,
|
||||
notes TEXT,
|
||||
created_at TIMESTAMPTZ DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ DEFAULT now(),
|
||||
deleted_at TIMESTAMPTZ,
|
||||
created_by BIGINT
|
||||
);
|
||||
|
||||
-- FOREIGN KEYS (dijalankan setelah semua tabel parent ada)
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'project_flocks') THEN
|
||||
ALTER TABLE laying_transfers
|
||||
ADD CONSTRAINT fk_laying_from_project_flock
|
||||
FOREIGN KEY (from_project_flock_id)
|
||||
REFERENCES project_flocks(id)
|
||||
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
ALTER TABLE laying_transfers
|
||||
ADD CONSTRAINT fk_laying_to_project_flock
|
||||
FOREIGN KEY (to_project_flock_id)
|
||||
REFERENCES project_flocks(id)
|
||||
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'users') THEN
|
||||
ALTER TABLE laying_transfers
|
||||
ADD CONSTRAINT fk_laying_created_by
|
||||
FOREIGN KEY (created_by)
|
||||
REFERENCES users(id)
|
||||
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- INDEXES
|
||||
CREATE INDEX IF NOT EXISTS idx_laying_transfers_from_project_flock_id ON laying_transfers (from_project_flock_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_laying_transfers_to_project_flock_id ON laying_transfers (to_project_flock_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_laying_transfers_created_by ON laying_transfers (created_by);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_laying_transfers_deleted_at ON laying_transfers (deleted_at);
|
||||
+1
@@ -0,0 +1 @@
|
||||
DROP TABLE IF EXISTS laying_kandang_transfers;
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
CREATE TABLE IF NOT EXISTS laying_kandang_transfers (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
kandang_id BIGINT,
|
||||
product_warehouse_id BIGINT,
|
||||
qty NUMERIC(15,3),
|
||||
laying_transfer_id BIGINT NOT NULL
|
||||
);
|
||||
|
||||
-- FOREIGN KEYS (dijalankan setelah semua tabel parent ada)
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'kandangs') THEN
|
||||
ALTER TABLE laying_kandang_transfers
|
||||
ADD CONSTRAINT fk_laying_kandang_transfers_kandang
|
||||
FOREIGN KEY (kandang_id)
|
||||
REFERENCES kandangs(id)
|
||||
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'product_warehouses') THEN
|
||||
ALTER TABLE laying_kandang_transfers
|
||||
ADD CONSTRAINT fk_laying_kandang_transfers_product_warehouse
|
||||
FOREIGN KEY (product_warehouse_id)
|
||||
REFERENCES product_warehouses(id)
|
||||
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'laying_transfers') THEN
|
||||
ALTER TABLE laying_kandang_transfers
|
||||
ADD CONSTRAINT fk_laying_kandang_transfers_laying_transfer
|
||||
FOREIGN KEY (laying_transfer_id)
|
||||
REFERENCES laying_transfers(id)
|
||||
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- INDEXES
|
||||
CREATE INDEX IF NOT EXISTS idx_laying_kandang_transfers_kandang_id ON laying_kandang_transfers(kandang_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_laying_kandang_transfers_product_warehouse_id ON laying_kandang_transfers(product_warehouse_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_laying_kandang_transfers_laying_transfer_id ON laying_kandang_transfers(laying_transfer_id);
|
||||
@@ -0,0 +1,5 @@
|
||||
DROP TABLE IF EXISTS project_chickin_details;
|
||||
|
||||
DROP TABLE IF EXISTS project_chickins;
|
||||
|
||||
DROP TABLE IF EXISTS project_flock_populations;
|
||||
@@ -0,0 +1,58 @@
|
||||
-- ============================================
|
||||
-- MIGRATION: project_chickins
|
||||
-- ============================================
|
||||
|
||||
-- STEP 1: Hapus tabel jika sudah ada
|
||||
|
||||
-- STEP 2: Buat tabel project_chickins
|
||||
CREATE TABLE IF NOT EXISTS project_chickins (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
project_flock_kandang_id BIGINT NOT NULL,
|
||||
product_warehouse_id BIGINT NOT NULL,
|
||||
chick_in_date DATE NOT NULL,
|
||||
usage_qty NUMERIC(15, 3) NOT NULL,
|
||||
pending_usage_qty NUMERIC(15, 3) DEFAULT 0,
|
||||
notes TEXT,
|
||||
created_by BIGINT NOT NULL,
|
||||
created_at TIMESTAMPTZ DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ DEFAULT now(),
|
||||
deleted_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
-- STEP 3: FOREIGN KEYS
|
||||
DO $$
|
||||
BEGIN
|
||||
-- Relasi ke project_flock_kandangs
|
||||
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'project_flock_kandangs') THEN
|
||||
ALTER TABLE project_chickins
|
||||
ADD CONSTRAINT fk_project_chickins_kandang
|
||||
FOREIGN KEY (project_flock_kandang_id)
|
||||
REFERENCES project_flock_kandangs(id)
|
||||
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
END IF;
|
||||
|
||||
-- Relasi ke product_warehouses
|
||||
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'product_warehouses') THEN
|
||||
ALTER TABLE project_chickins
|
||||
ADD CONSTRAINT fk_project_chickins_warehouse
|
||||
FOREIGN KEY (product_warehouse_id)
|
||||
REFERENCES product_warehouses(id)
|
||||
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
END IF;
|
||||
|
||||
-- Relasi ke users
|
||||
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'users') THEN
|
||||
ALTER TABLE project_chickins
|
||||
ADD CONSTRAINT fk_project_chickins_created_by
|
||||
FOREIGN KEY (created_by)
|
||||
REFERENCES users(id)
|
||||
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- STEP 4: INDEXES
|
||||
CREATE INDEX IF NOT EXISTS idx_chickins_kandang_id ON project_chickins (project_flock_kandang_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_chickins_warehouse_id ON project_chickins (product_warehouse_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_chickins_created_by ON project_chickins (created_by);
|
||||
@@ -0,0 +1,57 @@
|
||||
-- ============================================
|
||||
-- MIGRATION: project_flock_populations
|
||||
-- ============================================
|
||||
|
||||
-- STEP 1: Hapus tabel jika sudah ada
|
||||
|
||||
-- STEP 2: Buat tabel project_flock_populations
|
||||
CREATE TABLE IF NOT EXISTS project_flock_populations (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
project_chickin_id BIGINT NOT NULL,
|
||||
product_warehouse_id BIGINT NOT NULL,
|
||||
total_qty NUMERIC(15, 3) NOT NULL,
|
||||
total_used_qty NUMERIC(15, 3) DEFAULT 0,
|
||||
notes TEXT,
|
||||
created_by BIGINT NOT NULL,
|
||||
created_at TIMESTAMPTZ DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ DEFAULT now(),
|
||||
deleted_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
-- STEP 3: FOREIGN KEYS
|
||||
DO $$
|
||||
BEGIN
|
||||
-- Relasi ke project_chickins
|
||||
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'project_chickins') THEN
|
||||
ALTER TABLE project_flock_populations
|
||||
ADD CONSTRAINT fk_project_flock_populations_chickin
|
||||
FOREIGN KEY (project_chickin_id)
|
||||
REFERENCES project_chickins(id)
|
||||
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
END IF;
|
||||
|
||||
-- Relasi ke product_warehouses
|
||||
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'product_warehouses') THEN
|
||||
ALTER TABLE project_flock_populations
|
||||
ADD CONSTRAINT fk_project_flock_populations_warehouse
|
||||
FOREIGN KEY (product_warehouse_id)
|
||||
REFERENCES product_warehouses(id)
|
||||
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
END IF;
|
||||
|
||||
-- Relasi ke users
|
||||
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'users') THEN
|
||||
ALTER TABLE project_flock_populations
|
||||
ADD CONSTRAINT fk_project_flock_populations_created_by
|
||||
FOREIGN KEY (created_by)
|
||||
REFERENCES users(id)
|
||||
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- STEP 4: INDEXES
|
||||
CREATE INDEX IF NOT EXISTS idx_populations_chickin_id ON project_flock_populations (project_chickin_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_populations_warehouse_id ON project_flock_populations (product_warehouse_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_populations_created_by ON project_flock_populations (created_by);
|
||||
+136
-136
@@ -93,9 +93,9 @@ func Run(db *gorm.DB) error {
|
||||
if err := seedTransferStock(tx, adminID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := seedChickin(tx, adminID); err != nil {
|
||||
return err
|
||||
}
|
||||
// if err := seedChickin(tx, adminID); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
fmt.Println("✅ Master data seeding completed")
|
||||
return nil
|
||||
@@ -1134,151 +1134,151 @@ func seedTransferStock(tx *gorm.DB, createdBy uint) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func seedChickin(tx *gorm.DB, createdBy uint) error {
|
||||
seeds := []struct {
|
||||
ProjectFlockKandangId uint
|
||||
ChickInDate string
|
||||
Quantity float64
|
||||
Note string
|
||||
}{
|
||||
{ProjectFlockKandangId: 1, ChickInDate: "2025-10-20", Quantity: 100, Note: "Seeder chickin 1"},
|
||||
{ProjectFlockKandangId: 2, ChickInDate: "2025-10-21", Quantity: 200, Note: "Seeder chickin 2"},
|
||||
}
|
||||
// func seedChickin(tx *gorm.DB, createdBy uint) error {
|
||||
// seeds := []struct {
|
||||
// ProjectFlockKandangId uint
|
||||
// ChickInDate string
|
||||
// Quantity float64
|
||||
// Note string
|
||||
// }{
|
||||
// {ProjectFlockKandangId: 1, ChickInDate: "2025-10-20", Quantity: 100, Note: "Seeder chickin 1"},
|
||||
// {ProjectFlockKandangId: 2, ChickInDate: "2025-10-21", Quantity: 200, Note: "Seeder chickin 2"},
|
||||
// }
|
||||
|
||||
for _, seed := range seeds {
|
||||
chickinDate, err := time.Parse("2006-01-02", seed.ChickInDate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// for _, seed := range seeds {
|
||||
// chickinDate, err := time.Parse("2006-01-02", seed.ChickInDate)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// Insert ProjectChickin jika belum ada
|
||||
var chickin entity.ProjectChickin
|
||||
err = tx.Where("project_flock_kandang_id = ? AND chick_in_date = ?", seed.ProjectFlockKandangId, chickinDate).
|
||||
First(&chickin).Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
chickin = entity.ProjectChickin{
|
||||
ProjectFlockKandangId: seed.ProjectFlockKandangId,
|
||||
ChickInDate: chickinDate,
|
||||
Quantity: seed.Quantity,
|
||||
Note: seed.Note,
|
||||
CreatedBy: createdBy,
|
||||
}
|
||||
if err := tx.Create(&chickin).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
// // Insert ProjectChickin jika belum ada
|
||||
// var chickin entity.ProjectChickin
|
||||
// err = tx.Where("project_flock_kandang_id = ? AND chick_in_date = ?", seed.ProjectFlockKandangId, chickinDate).
|
||||
// First(&chickin).Error
|
||||
// if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
// chickin = entity.ProjectChickin{
|
||||
// ProjectFlockKandangId: seed.ProjectFlockKandangId,
|
||||
// ChickInDate: chickinDate,
|
||||
// Quantity: seed.Quantity,
|
||||
// Note: seed.Note,
|
||||
// CreatedBy: createdBy,
|
||||
// }
|
||||
// if err := tx.Create(&chickin).Error; err != nil {
|
||||
// return err
|
||||
// }
|
||||
// } else if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
var population entity.ProjectFlockPopulation
|
||||
err = tx.Where("project_flock_kandang_id = ?", seed.ProjectFlockKandangId).First(&population).Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
population = entity.ProjectFlockPopulation{
|
||||
ProjectFlockKandangId: seed.ProjectFlockKandangId,
|
||||
InitialQuantity: seed.Quantity,
|
||||
CurrentQuantity: seed.Quantity,
|
||||
ReservedQuantity: 0,
|
||||
CreatedBy: createdBy,
|
||||
}
|
||||
if err := tx.Create(&population).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err != nil {
|
||||
return err
|
||||
} else {
|
||||
// Update population quantities
|
||||
if err := tx.Model(&entity.ProjectFlockPopulation{}).
|
||||
Where("id = ?", population.Id).
|
||||
Updates(map[string]any{
|
||||
"initial_quantity": population.InitialQuantity + seed.Quantity,
|
||||
"current_quantity": population.CurrentQuantity + seed.Quantity,
|
||||
"reserved_quantity": 0,
|
||||
}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// var population entity.ProjectFlockPopulation
|
||||
// err = tx.Where("project_flock_kandang_id = ?", seed.ProjectFlockKandangId).First(&population).Error
|
||||
// if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
// population = entity.ProjectFlockPopulation{
|
||||
// ProjectFlockKandangId: seed.ProjectFlockKandangId,
|
||||
// InitialQuantity: seed.Quantity,
|
||||
// CurrentQuantity: seed.Quantity,
|
||||
// ReservedQuantity: 0,
|
||||
// CreatedBy: createdBy,
|
||||
// }
|
||||
// if err := tx.Create(&population).Error; err != nil {
|
||||
// return err
|
||||
// }
|
||||
// } else if err != nil {
|
||||
// return err
|
||||
// } else {
|
||||
// // Update population quantities
|
||||
// if err := tx.Model(&entity.ProjectFlockPopulation{}).
|
||||
// Where("id = ?", population.Id).
|
||||
// Updates(map[string]any{
|
||||
// "initial_quantity": population.InitialQuantity + seed.Quantity,
|
||||
// "current_quantity": population.CurrentQuantity + seed.Quantity,
|
||||
// "reserved_quantity": 0,
|
||||
// }).Error; err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
|
||||
var pfk entity.ProjectFlockKandang
|
||||
if err := tx.Where("id = ?", seed.ProjectFlockKandangId).First(&pfk).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
// no pivot found; skip creating details
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
// var pfk entity.ProjectFlockKandang
|
||||
// if err := tx.Where("id = ?", seed.ProjectFlockKandangId).First(&pfk).Error; err != nil {
|
||||
// if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
// // no pivot found; skip creating details
|
||||
// continue
|
||||
// }
|
||||
// return err
|
||||
// }
|
||||
|
||||
var warehouse entity.Warehouse
|
||||
if err := tx.Where("kandang_id = ?", pfk.KandangId).First(&warehouse).Error; err != nil {
|
||||
// if warehouse not found, cannot create details
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
// var warehouse entity.Warehouse
|
||||
// if err := tx.Where("kandang_id = ?", pfk.KandangId).First(&warehouse).Error; err != nil {
|
||||
// // if warehouse not found, cannot create details
|
||||
// if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
// continue
|
||||
// }
|
||||
// return err
|
||||
// }
|
||||
|
||||
var productWarehouses []entity.ProductWarehouse
|
||||
err = tx.Table("product_warehouses").
|
||||
Select("product_warehouses.*").
|
||||
Joins("JOIN products ON products.id = product_warehouses.product_id").
|
||||
Joins("JOIN product_categories ON product_categories.id = products.product_category_id").
|
||||
Where("product_categories.code = ? AND product_warehouses.warehouse_id = ?", "DOC", warehouse.Id).
|
||||
Order("product_warehouses.created_at DESC").
|
||||
Find(&productWarehouses).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// var productWarehouses []entity.ProductWarehouse
|
||||
// err = tx.Table("product_warehouses").
|
||||
// Select("product_warehouses.*").
|
||||
// Joins("JOIN products ON products.id = product_warehouses.product_id").
|
||||
// Joins("JOIN product_categories ON product_categories.id = products.product_category_id").
|
||||
// Where("product_categories.code = ? AND product_warehouses.warehouse_id = ?", "DOC", warehouse.Id).
|
||||
// Order("product_warehouses.created_at DESC").
|
||||
// Find(&productWarehouses).Error
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// If no product warehouses found, keep existing chickin.Quantity and skip details
|
||||
if len(productWarehouses) == 0 {
|
||||
continue
|
||||
}
|
||||
// // If no product warehouses found, keep existing chickin.Quantity and skip details
|
||||
// if len(productWarehouses) == 0 {
|
||||
// continue
|
||||
// }
|
||||
|
||||
// sum all pw quantities and set chickin.Quantity to that total (mimic CreateOne)
|
||||
totalQty := 0.0
|
||||
for _, pw := range productWarehouses {
|
||||
totalQty += pw.Quantity
|
||||
}
|
||||
// // sum all pw quantities and set chickin.Quantity to that total (mimic CreateOne)
|
||||
// totalQty := 0.0
|
||||
// for _, pw := range productWarehouses {
|
||||
// totalQty += pw.Quantity
|
||||
// }
|
||||
|
||||
if chickin.Quantity != totalQty {
|
||||
if err := tx.Model(&entity.ProjectChickin{}).Where("id = ?", chickin.Id).Update("quantity", totalQty).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
chickin.Quantity = totalQty
|
||||
}
|
||||
// if chickin.Quantity != totalQty {
|
||||
// if err := tx.Model(&entity.ProjectChickin{}).Where("id = ?", chickin.Id).Update("quantity", totalQty).Error; err != nil {
|
||||
// return err
|
||||
// }
|
||||
// chickin.Quantity = totalQty
|
||||
// }
|
||||
|
||||
for _, pw := range productWarehouses {
|
||||
// ensure detail exists or create it with full pw.Quantity
|
||||
var detail entity.ProjectChickinDetail
|
||||
err = tx.Where("project_chickin_id = ? AND product_warehouse_id = ?", chickin.Id, pw.Id).First(&detail).Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
detail = entity.ProjectChickinDetail{
|
||||
ProjectChickinId: chickin.Id,
|
||||
ProductWarehouseId: pw.Id,
|
||||
Quantity: pw.Quantity,
|
||||
CreatedBy: createdBy,
|
||||
}
|
||||
if err := tx.Create(&detail).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err != nil {
|
||||
return err
|
||||
} else {
|
||||
if detail.Quantity != pw.Quantity {
|
||||
if err := tx.Model(&entity.ProjectChickinDetail{}).Where("id = ?", detail.Id).Update("quantity", pw.Quantity).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
// for _, pw := range productWarehouses {
|
||||
// // ensure detail exists or create it with full pw.Quantity
|
||||
// var detail entity.ProjectChickinDetail
|
||||
// err = tx.Where("project_chickin_id = ? AND product_warehouse_id = ?", chickin.Id, pw.Id).First(&detail).Error
|
||||
// if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
// detail = entity.ProjectChickinDetail{
|
||||
// ProjectChickinId: chickin.Id,
|
||||
// ProductWarehouseId: pw.Id,
|
||||
// Quantity: pw.Quantity,
|
||||
// CreatedBy: createdBy,
|
||||
// }
|
||||
// if err := tx.Create(&detail).Error; err != nil {
|
||||
// return err
|
||||
// }
|
||||
// } else if err != nil {
|
||||
// return err
|
||||
// } else {
|
||||
// if detail.Quantity != pw.Quantity {
|
||||
// if err := tx.Model(&entity.ProjectChickinDetail{}).Where("id = ?", detail.Id).Update("quantity", pw.Quantity).Error; err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// zero out pw quantity
|
||||
if err := tx.Model(&entity.ProductWarehouse{}).Where("id = ?", pw.Id).Update("quantity", 0).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
// // zero out pw quantity
|
||||
// if err := tx.Model(&entity.ProductWarehouse{}).Where("id = ?", pw.Id).Update("quantity", 0).Error; err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
return nil
|
||||
}
|
||||
// return nil
|
||||
// }
|
||||
|
||||
func ptr[T any](v T) *T {
|
||||
return &v
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type LayingKandangTransfer struct {
|
||||
Id uint `gorm:"primaryKey"`
|
||||
KandangId uint
|
||||
ProductWarehouseId uint
|
||||
Qty float64 `gorm:"type:numeric(15,3)"`
|
||||
LayingTransferId uint `gorm:"not null"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||
|
||||
Kandang *Kandang `gorm:"foreignKey:KandangId;references:Id"`
|
||||
ProductWarehouse *ProductWarehouse `gorm:"foreignKey:ProductWarehouseId;references:Id"`
|
||||
LayingTransfer *LayingTransfer `gorm:"foreignKey:LayingTransferId;references:Id"`
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type LayingTransfer struct {
|
||||
Id uint `gorm:"primaryKey"`
|
||||
FromProjectFlockId uint `gorm:"not null"`
|
||||
ToProjectFlockId uint `gorm:"not null"`
|
||||
TotalQty float64 `gorm:"type:numeric(15,3)"`
|
||||
TransferDate time.Time `gorm:"type:date;not null"`
|
||||
Notes string `gorm:"type:text"`
|
||||
CreatedBy uint `gorm:"not null"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||
|
||||
FromProjectFlock *ProjectFlock `gorm:"foreignKey:FromProjectFlockId;references:Id"`
|
||||
ToProjectFlock *ProjectFlock `gorm:"foreignKey:ToProjectFlockId;references:Id"`
|
||||
CreatedUser *User `gorm:"foreignKey:CreatedBy;references:Id"`
|
||||
}
|
||||
@@ -12,13 +12,16 @@ type ProjectChickin struct {
|
||||
Id uint `gorm:"primaryKey"`
|
||||
ProjectFlockKandangId uint `gorm:"not null"`
|
||||
ChickInDate time.Time `gorm:"not null"`
|
||||
Quantity float64 `gorm:"not null"`
|
||||
Note string `gorm:"type:text"`
|
||||
ProductWarehouseId uint `gorm:"not null"`
|
||||
UsageQty float64 `gorm:"type:numeric(15,3);not null"`
|
||||
PendingUsageQty float64 `gorm:"type:numeric(15,3);default:0"`
|
||||
Notes string `gorm:"type:text"`
|
||||
CreatedBy uint `gorm:"not null"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
|
||||
ProjectFlockKandang ProjectFlockKandang `gorm:"foreignKey:ProjectFlockKandangId;references:Id"`
|
||||
CreatedUser User `gorm:"foreignKey:CreatedBy;references:Id"`
|
||||
ProjectFlockKandang *ProjectFlockKandang `gorm:"foreignKey:ProjectFlockKandangId;references:Id"`
|
||||
ProductWarehouse *ProductWarehouse `gorm:"foreignKey:ProductWarehouseId;references:Id"`
|
||||
CreatedUser *User `gorm:"foreignKey:CreatedBy;references:Id"`
|
||||
}
|
||||
|
||||
@@ -7,16 +7,18 @@ import (
|
||||
)
|
||||
|
||||
type ProjectFlockPopulation struct {
|
||||
Id uint `gorm:"primaryKey"`
|
||||
ProjectFlockKandangId uint `gorm:"not null"`
|
||||
InitialQuantity float64 `gorm:"type:numeric(15,3);not null"`
|
||||
CurrentQuantity float64 `gorm:"type:numeric(15,3);not null"`
|
||||
ReservedQuantity float64 `gorm:"type:numeric(15,3)"`
|
||||
CreatedBy uint `gorm:"not null"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||
Id uint `gorm:"primaryKey"`
|
||||
ProjectChickinId uint `gorm:"not null"`
|
||||
ProductWarehouseId uint `gorm:"not null"`
|
||||
TotalQty float64 `gorm:"type:numeric(15,3);not null"`
|
||||
TotalUsedQty float64 `gorm:"type:numeric(15,3);not null"`
|
||||
Notes string `gorm:"type:text"`
|
||||
CreatedBy uint `gorm:"not null"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||
|
||||
ProjectFlockKandang *ProjectFlockKandang `gorm:"foreignKey:ProjectFlockKandangId;references:Id"`
|
||||
CreatedUser *User `gorm:"foreignKey:CreatedBy;references:Id"`
|
||||
ProjectChickin *ProjectChickin `gorm:"foreignKey:ProjectChickinId;references:Id"`
|
||||
ProductWarehouse *ProductWarehouse `gorm:"foreignKey:ProductWarehouseId;references:Id"`
|
||||
CreatedUser *User `gorm:"foreignKey:CreatedBy;references:Id"`
|
||||
}
|
||||
|
||||
@@ -28,3 +28,4 @@ type ProjectFlock struct {
|
||||
KandangHistory []ProjectFlockKandang `gorm:"foreignKey:ProjectFlockId;references:Id"`
|
||||
LatestApproval *Approval `gorm:"-" json:"-"`
|
||||
}
|
||||
|
||||
|
||||
@@ -3,10 +3,12 @@ package entities
|
||||
import "time"
|
||||
|
||||
type ProjectFlockKandang struct {
|
||||
Id uint `gorm:"primaryKey"`
|
||||
ProjectFlockId uint `gorm:"not null;index:idx_project_flock_kandangs_project;uniqueIndex:idx_project_flock_kandangs_unique"`
|
||||
KandangId uint `gorm:"not null;index:idx_project_flock_kandangs_kandang;uniqueIndex:idx_project_flock_kandangs_unique"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime"`
|
||||
ProjectFlock ProjectFlock `gorm:"foreignKey:ProjectFlockId;references:Id"`
|
||||
Kandang Kandang `gorm:"foreignKey:KandangId;references:Id"`
|
||||
Id uint `gorm:"primaryKey"`
|
||||
ProjectFlockId uint `gorm:"not null;index:idx_project_flock_kandangs_project;uniqueIndex:idx_project_flock_kandangs_unique"`
|
||||
KandangId uint `gorm:"not null;index:idx_project_flock_kandangs_kandang;uniqueIndex:idx_project_flock_kandangs_unique"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime"`
|
||||
|
||||
ProjectFlock ProjectFlock `gorm:"foreignKey:ProjectFlockId;references:Id"`
|
||||
Kandang Kandang `gorm:"foreignKey:KandangId;references:Id"`
|
||||
Chickins []ProjectChickin `gorm:"foreignKey:ProjectFlockKandangId;references:Id"`
|
||||
}
|
||||
|
||||
@@ -82,6 +82,10 @@ func (r *ConstantRepositoryImpl) GetConstants() map[string]interface{} {
|
||||
"LOKASI",
|
||||
"KANDANG",
|
||||
},
|
||||
"stock_log": map[string][]string{
|
||||
"log_types": []string{"TRANSFER", "ADJUSTMENT"},
|
||||
"transaction_types": []string{"INCREASE", "DECREASE"},
|
||||
},
|
||||
"supplier_categories": []string{
|
||||
"BOP",
|
||||
"SAPRONAK",
|
||||
|
||||
+29
@@ -17,6 +17,9 @@ type ProductWarehouseRepository interface {
|
||||
ExistsByID(ctx context.Context, id uint) (bool, error)
|
||||
GetProductWarehouseByProductAndWarehouseID(ctx context.Context, productId, warehouseId uint) (*entity.ProductWarehouse, error)
|
||||
GetByCategoryCodeAndWarehouseID(ctx context.Context, categoryCode string, warehouseId uint) ([]entity.ProductWarehouse, error)
|
||||
GetLatestByCategoryCodeAndWarehouseID(ctx context.Context, categoryCode string, warehouseId uint) (*entity.ProductWarehouse, error)
|
||||
WithTxRepo(tx *gorm.DB) ProductWarehouseRepository
|
||||
DB() *gorm.DB
|
||||
}
|
||||
|
||||
type ProductWarehouseRepositoryImpl struct {
|
||||
@@ -31,6 +34,15 @@ func NewProductWarehouseRepository(db *gorm.DB) ProductWarehouseRepository {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ProductWarehouseRepositoryImpl) WithTxRepo(tx *gorm.DB) ProductWarehouseRepository {
|
||||
return &ProductWarehouseRepositoryImpl{
|
||||
BaseRepositoryImpl: repository.NewBaseRepository[entity.ProductWarehouse](tx),
|
||||
db: tx,
|
||||
}
|
||||
}
|
||||
func (r *ProductWarehouseRepositoryImpl) DB() *gorm.DB {
|
||||
return r.db
|
||||
}
|
||||
func (r *ProductWarehouseRepositoryImpl) IsProductExist(ctx context.Context, productId uint) (bool, error) {
|
||||
return repository.Exists[entity.Product](ctx, r.db, productId)
|
||||
}
|
||||
@@ -89,3 +101,20 @@ func (r *ProductWarehouseRepositoryImpl) GetByCategoryCodeAndWarehouseID(ctx con
|
||||
}
|
||||
return productWarehouses, nil
|
||||
}
|
||||
|
||||
func (r *ProductWarehouseRepositoryImpl) GetLatestByCategoryCodeAndWarehouseID(ctx context.Context, categoryCode string, warehouseId uint) (*entity.ProductWarehouse, error) {
|
||||
var productWarehouse entity.ProductWarehouse
|
||||
err := r.db.WithContext(ctx).
|
||||
Table("product_warehouses").
|
||||
Select("product_warehouses.*").
|
||||
Joins("JOIN products ON products.id = product_warehouses.product_id").
|
||||
Joins("JOIN product_categories ON product_categories.id = products.product_category_id").
|
||||
Where("product_categories.code = ? AND product_warehouses.warehouse_id = ?", categoryCode, warehouseId).
|
||||
Order("product_warehouses.created_at DESC").
|
||||
Limit(1).
|
||||
First(&productWarehouse).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &productWarehouse, nil
|
||||
}
|
||||
|
||||
@@ -139,23 +139,33 @@ func (u *ChickinController) DeleteOne(c *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (u *ChickinController) Approve(c *fiber.Ctx) error {
|
||||
param := c.Params("id")
|
||||
|
||||
id, err := strconv.Atoi(param)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
|
||||
func (u *ChickinController) Approval(c *fiber.Ctx) error {
|
||||
req := new(validation.Approve)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
|
||||
}
|
||||
|
||||
if err := u.ChickinService.Approve(c, uint(id)); err != nil {
|
||||
results, err := u.ChickinService.Approval(c, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
data interface{}
|
||||
message = "Submit chickin approval successfully"
|
||||
)
|
||||
if len(results) == 1 {
|
||||
data = dto.ToChickinListDTO(results[0])
|
||||
} else {
|
||||
message = "Submit chickin approvals successfully"
|
||||
data = dto.ToChickinListDTOs(results)
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).
|
||||
JSON(response.Success{
|
||||
Code: fiber.StatusOK,
|
||||
Status: "success",
|
||||
Message: "Approve chickin successfully",
|
||||
Data: nil,
|
||||
Message: message,
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -18,8 +18,10 @@ type ChickinBaseDTO struct {
|
||||
Id uint `json:"id"`
|
||||
ProjectFlockKandang *ProjectFlockKandangDTO `json:"project_flock_kandang"`
|
||||
ChickInDate time.Time `json:"chick_in_date"`
|
||||
Quantity float64 `json:"quantity"`
|
||||
Note string `json:"note"`
|
||||
ProductWarehouseId uint `json:"product_warehouse_id"`
|
||||
UsageQty float64 `json:"usage_qty"`
|
||||
PendingUsageQty float64 `json:"pending_usage_qty"`
|
||||
Notes string `json:"notes"`
|
||||
}
|
||||
|
||||
type ProjectFlockDTO struct {
|
||||
@@ -44,8 +46,10 @@ type ChickinSimpleDTO struct {
|
||||
Id uint `json:"id"`
|
||||
ProjectFlockKandangId uint `json:"project_flock_kandang_id"`
|
||||
ChickInDate time.Time `json:"chick_in_date"`
|
||||
Quantity float64 `json:"quantity"`
|
||||
Note string `json:"note"`
|
||||
ProductWarehouseId uint `json:"product_warehouse_id"`
|
||||
UsageQty float64 `json:"usage_qty"`
|
||||
PendingUsageQty float64 `json:"pending_usage_qty"`
|
||||
Notes string `json:"notes"`
|
||||
CreatedBy uint `json:"created_by"`
|
||||
}
|
||||
|
||||
@@ -138,16 +142,18 @@ func ToProjectFlockKandangDTO(e entity.ProjectFlockKandang) ProjectFlockKandangD
|
||||
|
||||
func ToChickinBaseDTO(e entity.ProjectChickin) ChickinBaseDTO {
|
||||
var pfk *ProjectFlockKandangDTO
|
||||
if e.ProjectFlockKandang.Id != 0 {
|
||||
mapped := ToProjectFlockKandangDTO(e.ProjectFlockKandang)
|
||||
if e.ProjectFlockKandang != nil && e.ProjectFlockKandang.Id != 0 {
|
||||
mapped := ToProjectFlockKandangDTO(*e.ProjectFlockKandang)
|
||||
pfk = &mapped
|
||||
}
|
||||
return ChickinBaseDTO{
|
||||
Id: e.Id,
|
||||
ProjectFlockKandang: pfk,
|
||||
ChickInDate: e.ChickInDate,
|
||||
Quantity: e.Quantity,
|
||||
Note: e.Note,
|
||||
ProductWarehouseId: e.ProductWarehouseId,
|
||||
UsageQty: e.UsageQty,
|
||||
PendingUsageQty: e.PendingUsageQty,
|
||||
Notes: e.Notes,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,21 +162,23 @@ func ToChickinSimpleDTO(e entity.ProjectChickin) ChickinSimpleDTO {
|
||||
Id: e.Id,
|
||||
ProjectFlockKandangId: e.ProjectFlockKandangId,
|
||||
ChickInDate: e.ChickInDate,
|
||||
Quantity: e.Quantity,
|
||||
Note: e.Note,
|
||||
ProductWarehouseId: e.ProductWarehouseId,
|
||||
UsageQty: e.UsageQty,
|
||||
PendingUsageQty: e.PendingUsageQty,
|
||||
Notes: e.Notes,
|
||||
CreatedBy: e.CreatedBy,
|
||||
}
|
||||
}
|
||||
|
||||
func ToChickinListDTO(e entity.ProjectChickin) ChickinListDTO {
|
||||
var createdUser *userBaseDTO.UserBaseDTO
|
||||
if e.CreatedUser.Id != 0 {
|
||||
mapped := userBaseDTO.ToUserBaseDTO(e.CreatedUser)
|
||||
if e.CreatedUser != nil && e.CreatedUser.Id != 0 {
|
||||
mapped := userBaseDTO.ToUserBaseDTO(*e.CreatedUser)
|
||||
createdUser = &mapped
|
||||
}
|
||||
var pfk *ProjectFlockKandangDTO
|
||||
if e.ProjectFlockKandang.Id != 0 {
|
||||
mapped := ToProjectFlockKandangDTO(e.ProjectFlockKandang)
|
||||
if e.ProjectFlockKandang != nil && e.ProjectFlockKandang.Id != 0 {
|
||||
mapped := ToProjectFlockKandangDTO(*e.ProjectFlockKandang)
|
||||
pfk = &mapped
|
||||
}
|
||||
return ChickinListDTO{
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package chickins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"gorm.io/gorm"
|
||||
|
||||
commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
|
||||
|
||||
rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
|
||||
rKandang "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/repositories"
|
||||
rWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories"
|
||||
@@ -15,6 +20,8 @@ import (
|
||||
|
||||
rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories"
|
||||
sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||
|
||||
utils "gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||
)
|
||||
|
||||
type ChickinModule struct{}
|
||||
@@ -32,6 +39,12 @@ func (ChickinModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *
|
||||
|
||||
userRepo := rUser.NewUserRepository(db)
|
||||
|
||||
approvalRepo := commonRepo.NewApprovalRepository(db)
|
||||
approvalService := commonSvc.NewApprovalService(approvalRepo)
|
||||
if err := approvalService.RegisterWorkflowSteps(utils.ApprovalWorkflowProjectFlockKandang, utils.ProjectFlockKandangApprovalSteps); err != nil {
|
||||
panic(fmt.Sprintf("failed to register project flock kandang approval workflow: %v", err))
|
||||
}
|
||||
|
||||
chickinService := sChickin.NewChickinService(chickinRepo, kandangRepo, warehouseRepo, productWarehouseRepo, projectFlockRepo, projectflockkandangrepo, projectflockpopulationrepo, chickinDetailRepo, validate)
|
||||
userService := sUser.NewUserService(userRepo, validate)
|
||||
|
||||
|
||||
+25
@@ -1,6 +1,8 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
"gorm.io/gorm"
|
||||
@@ -8,6 +10,10 @@ import (
|
||||
|
||||
type ProjectChickinDetailRepository interface {
|
||||
repository.BaseRepository[entity.ProjectChickinDetail]
|
||||
CreateOne(ctx context.Context, entity *entity.ProjectChickinDetail, modifier func(*gorm.DB) *gorm.DB) error
|
||||
DeleteMany(ctx context.Context, modifier func(*gorm.DB) *gorm.DB) error
|
||||
GetByProjectChickinID(ctx context.Context, projectChickinID uint) ([]entity.ProjectChickinDetail, error)
|
||||
WithTxRepo(tx *gorm.DB) ProjectChickinDetailRepository
|
||||
}
|
||||
|
||||
type ChickinDetailRepositoryImpl struct {
|
||||
@@ -19,3 +25,22 @@ func NewChickinDetailRepository(db *gorm.DB) ProjectChickinDetailRepository {
|
||||
BaseRepositoryImpl: repository.NewBaseRepository[entity.ProjectChickinDetail](db),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ChickinDetailRepositoryImpl) WithTxRepo(tx *gorm.DB) ProjectChickinDetailRepository {
|
||||
return &ChickinDetailRepositoryImpl{BaseRepositoryImpl: repository.NewBaseRepository[entity.ProjectChickinDetail](tx)}
|
||||
}
|
||||
|
||||
func (r *ChickinDetailRepositoryImpl) DB() *gorm.DB {
|
||||
return r.BaseRepositoryImpl.DB()
|
||||
}
|
||||
|
||||
func (r *ChickinDetailRepositoryImpl) GetByProjectChickinID(ctx context.Context, projectChickinID uint) ([]entity.ProjectChickinDetail, error) {
|
||||
var records []entity.ProjectChickinDetail
|
||||
if err := r.DB().WithContext(ctx).Where("project_chickin_id = ?", projectChickinID).Find(&records).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(records) == 0 {
|
||||
return nil, gorm.ErrRecordNotFound
|
||||
}
|
||||
return records, nil
|
||||
}
|
||||
|
||||
@@ -25,5 +25,5 @@ func ChickinRoutes(v1 fiber.Router, u user.UserService, s chickin.ChickinService
|
||||
route.Get("/:id", ctrl.GetOne)
|
||||
route.Patch("/:id", ctrl.UpdateOne)
|
||||
route.Delete("/:id", ctrl.DeleteOne)
|
||||
route.Post("/:id/approve", ctrl.Approve)
|
||||
route.Post("/approvals", ctrl.Approval)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,11 @@ package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
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"
|
||||
rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
|
||||
KandangRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/repositories"
|
||||
@@ -24,7 +28,7 @@ type ChickinService interface {
|
||||
CreateOne(ctx *fiber.Ctx, req *validation.Create) (*entity.ProjectChickin, error)
|
||||
UpdateOne(ctx *fiber.Ctx, req *validation.Update, id uint) (*entity.ProjectChickin, error)
|
||||
DeleteOne(ctx *fiber.Ctx, id uint) error
|
||||
Approve(ctx *fiber.Ctx, id uint) error
|
||||
Approval(ctx *fiber.Ctx, req *validation.Approve) ([]entity.ProjectChickin, error)
|
||||
}
|
||||
|
||||
type chickinService struct {
|
||||
@@ -77,6 +81,7 @@ func (s chickinService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity
|
||||
}
|
||||
|
||||
offset := (params.Page - 1) * params.Limit
|
||||
|
||||
chickins, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
|
||||
db = s.withRelations(db)
|
||||
if params.ProjectFlockKandangId != 0 {
|
||||
@@ -109,107 +114,98 @@ func (s *chickinService) CreateOne(c *fiber.Ctx, req *validation.Create) (*entit
|
||||
return nil, err
|
||||
}
|
||||
|
||||
projectflockkandang, err := s.ProjectflockKandangRepo.GetByID(c.Context(), req.ProjectFlockKandangId)
|
||||
projectFlockKandang, err := s.ProjectflockKandangRepo.GetByID(c.Context(), req.ProjectFlockKandangId)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to get projectflock kandang: %+v", err)
|
||||
return nil, err
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Project Flock Kandang not found")
|
||||
}
|
||||
|
||||
warehouse, err := s.WarehouseRepo.GetByKandangID(c.Context(), projectflockkandang.KandangId)
|
||||
warehouse, err := s.WarehouseRepo.GetByKandangID(c.Context(), projectFlockKandang.KandangId)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to get warehouse: %+v", err)
|
||||
return nil, err
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Warehouse for Kandang not found")
|
||||
}
|
||||
|
||||
// move complex DB query into repository for cleaner service
|
||||
productWarehouses, err := s.ProductWarehouseRepo.GetByCategoryCodeAndWarehouseID(c.Context(), "DOC", warehouse.Id)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to get product warehouses: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
if len(productWarehouses) == 0 {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Product Warehouse not found for the given Project Flock and Warehouse")
|
||||
}
|
||||
totalQuantity := 0.0
|
||||
for _, pw := range productWarehouses {
|
||||
totalQuantity += pw.Quantity
|
||||
}
|
||||
var productWarehouses []entity.ProductWarehouse
|
||||
|
||||
if totalQuantity < 1 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Insufficient quantity in Product Warehouses")
|
||||
if strings.ToUpper(strings.TrimSpace(projectFlockKandang.ProjectFlock.Category)) == string(utils.ProjectFlockCategoryGrowing) {
|
||||
|
||||
productWarehouses, err = s.ProductWarehouseRepo.GetByCategoryCodeAndWarehouseID(c.Context(), "DOC", warehouse.Id)
|
||||
if err != nil || len(productWarehouses) == 0 {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Product for growing category in the Kandang's warehouse not found")
|
||||
}
|
||||
}
|
||||
|
||||
chickinDate, err := utils.ParseDateString(req.ChickInDate)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to parse chickin date: %+v", err)
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid ChickInDate format")
|
||||
}
|
||||
newChickin := &entity.ProjectChickin{
|
||||
ProjectFlockKandangId: projectflockkandang.Id,
|
||||
ChickInDate: chickinDate,
|
||||
Quantity: totalQuantity,
|
||||
Note: req.Note,
|
||||
CreatedBy: 1, //todo: ganti dengan user login
|
||||
|
||||
actorID := uint(1) // todo nanti ambil dari auth context
|
||||
newChikins := make([]*entity.ProjectChickin, 0)
|
||||
for _, productWarehouse := range productWarehouses {
|
||||
|
||||
if productWarehouse.Quantity > 0 {
|
||||
newChickin := &entity.ProjectChickin{
|
||||
ProjectFlockKandangId: req.ProjectFlockKandangId,
|
||||
ChickInDate: chickinDate,
|
||||
UsageQty: 0,
|
||||
PendingUsageQty: productWarehouse.Quantity,
|
||||
ProductWarehouseId: productWarehouse.Id,
|
||||
Notes: req.Note,
|
||||
CreatedBy: actorID,
|
||||
}
|
||||
|
||||
newChikins = append(newChikins, newChickin)
|
||||
}
|
||||
}
|
||||
err = s.Repository.CreateOne(c.Context(), newChickin, nil)
|
||||
|
||||
if len(newChikins) == 0 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "No chickins to create")
|
||||
}
|
||||
|
||||
err = s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
|
||||
|
||||
approvalSvcTx := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(dbTransaction))
|
||||
productWarehouseTx := s.ProductWarehouseRepo.WithTx(dbTransaction)
|
||||
|
||||
if err := s.Repository.WithTx(dbTransaction).CreateMany(c.Context(), newChikins, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
latest, err := approvalSvcTx.LatestByTarget(c.Context(), utils.ApprovalWorkflowProjectFlockKandang, projectFlockKandang.Id, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, chickin := range newChikins {
|
||||
|
||||
updates := map[string]any{"quantity": 0}
|
||||
|
||||
if err := productWarehouseTx.PatchOne(c.Context(), chickin.ProductWarehouseId, updates, nil); err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return fmt.Errorf("failed to update product warehouse quantity for id %d", chickin.ProductWarehouseId)
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if latest == nil {
|
||||
|
||||
action := entity.ApprovalActionCreated
|
||||
if _, err := approvalSvcTx.CreateApproval(c.Context(), utils.ApprovalWorkflowProjectFlockKandang, projectFlockKandang.Id, utils.ProjectFlockKandangStepPengajuan, &action, actorID, nil); err != nil {
|
||||
lower := strings.ToLower(err.Error())
|
||||
if !(strings.Contains(lower, "duplicate") || strings.Contains(lower, "unique constraint") || strings.Contains(lower, "23505")) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to create chickin: %+v", err)
|
||||
return nil, err
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
// Update semua product warehouse: set quantity jadi 0
|
||||
for _, pw := range productWarehouses {
|
||||
err = s.ProductWarehouseRepo.PatchOne(c.Context(), pw.Id, map[string]any{
|
||||
"quantity": 0,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to update product warehouse quantity: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newChickinDetail := &entity.ProjectChickinDetail{
|
||||
ProjectChickinId: newChickin.Id,
|
||||
ProductWarehouseId: pw.Id,
|
||||
Quantity: pw.Quantity,
|
||||
CreatedBy: 1, // todo: ganti dengan user login
|
||||
}
|
||||
err = s.ProjectChickinDetailRepo.CreateOne(c.Context(), newChickinDetail, nil)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to create chickin detail: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
existingPopulation, err := s.ProjectflockPopulationRepo.GetByProjectFlockKandangID(c.Context(), req.ProjectFlockKandangId)
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
s.Log.Errorf("Failed to get project flock population: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
if existingPopulation != nil {
|
||||
|
||||
err = s.ProjectflockPopulationRepo.PatchOne(c.Context(), existingPopulation.Id, map[string]any{
|
||||
"reserved_quantity": newChickin.Quantity + existingPopulation.ReservedQuantity,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to update project flock population: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
newPopulation := &entity.ProjectFlockPopulation{
|
||||
ProjectFlockKandangId: req.ProjectFlockKandangId,
|
||||
InitialQuantity: 0,
|
||||
CurrentQuantity: 0,
|
||||
ReservedQuantity: newChickin.Quantity,
|
||||
CreatedBy: 1, // todo: ganti dengan user login
|
||||
}
|
||||
err = s.ProjectflockPopulationRepo.CreateOne(c.Context(), newPopulation, nil)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to create project flock population: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return s.GetOne(c, newChickin.Id)
|
||||
return newChikins[0], nil
|
||||
}
|
||||
|
||||
func (s chickinService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint) (*entity.ProjectChickin, error) {
|
||||
@@ -223,7 +219,8 @@ func (s chickinService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint)
|
||||
updateBody["chick_in_date"] = req.ChickInDate
|
||||
}
|
||||
if req.Note != "" {
|
||||
updateBody["note"] = req.Note
|
||||
// entity uses `Notes` => column `notes`
|
||||
updateBody["notes"] = req.Note
|
||||
}
|
||||
if len(updateBody) == 0 {
|
||||
return s.GetOne(c, id)
|
||||
@@ -241,178 +238,141 @@ func (s chickinService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint)
|
||||
}
|
||||
|
||||
func (s chickinService) DeleteOne(c *fiber.Ctx, id uint) error {
|
||||
db := s.Repository.DB()
|
||||
|
||||
tx := db.WithContext(c.Context()).Begin()
|
||||
if tx.Error != nil {
|
||||
s.Log.Errorf("Failed to begin transaction: %+v", tx.Error)
|
||||
return tx.Error
|
||||
}
|
||||
rollback := func(err error) error {
|
||||
if rerr := tx.Rollback().Error; rerr != nil {
|
||||
s.Log.Errorf("Rollback failed: %+v", rerr)
|
||||
// Simplified delete: directly call repository delete. Complex restore logic removed for now.
|
||||
if err := s.Repository.DeleteOne(c.Context(), id); err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return fiber.NewError(fiber.StatusNotFound, "Chickin not found")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
chickinRepoTx := s.Repository.WithTx(tx)
|
||||
pfkRepoTx := s.ProjectflockKandangRepo.WithTx(tx)
|
||||
productWarehouseRepoTx := s.ProductWarehouseRepo.WithTx(tx)
|
||||
return nil
|
||||
}
|
||||
|
||||
chickin, err := chickinRepoTx.GetByID(c.Context(), id, nil)
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return rollback(fiber.NewError(fiber.StatusNotFound, "Chickin not found"))
|
||||
}
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed get chickin by id: %+v", err)
|
||||
return rollback(err)
|
||||
func (s chickinService) Approval(c *fiber.Ctx, req *validation.Approve) ([]entity.ProjectChickin, error) {
|
||||
if err := s.Validate.Struct(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var population entity.ProjectFlockPopulation
|
||||
if err := tx.WithContext(c.Context()).Where("project_flock_kandang_id = ?", chickin.ProjectFlockKandangId).First(&population).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return rollback(fiber.NewError(fiber.StatusNotFound, "Project flock population not found"))
|
||||
}
|
||||
s.Log.Errorf("Failed to get project flock population: %+v", err)
|
||||
return rollback(err)
|
||||
actorID := uint(1) // todo nanti ambil dari auth context
|
||||
|
||||
var action entity.ApprovalAction
|
||||
switch strings.ToUpper(strings.TrimSpace(req.Action)) {
|
||||
case string(entity.ApprovalActionRejected):
|
||||
action = entity.ApprovalActionRejected
|
||||
case string(entity.ApprovalActionApproved):
|
||||
action = entity.ApprovalActionApproved
|
||||
default:
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "action must be APPROVED or REJECTED")
|
||||
}
|
||||
|
||||
newReserved := population.ReservedQuantity - chickin.Quantity
|
||||
if newReserved < 0 {
|
||||
newReserved = 0
|
||||
}
|
||||
if err := tx.WithContext(c.Context()).Model(&entity.ProjectFlockPopulation{}).Where("id = ?", population.Id).Updates(map[string]any{"reserved_quantity": newReserved}).Error; err != nil {
|
||||
s.Log.Errorf("Failed to update project flock population: %+v", err)
|
||||
return rollback(err)
|
||||
approvableIDs := uniqueUintSlice(req.ApprovableIds)
|
||||
if len(approvableIDs) == 0 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "approvable_ids must contain at least one id")
|
||||
}
|
||||
|
||||
restoreFromDetails := func() (bool, error) {
|
||||
var details []entity.ProjectChickinDetail
|
||||
if err := tx.WithContext(c.Context()).Where("project_chickin_id = ?", chickin.Id).Find(&details).Error; err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(details) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
step := utils.ProjectFlockKandangStepPengajuan
|
||||
if action == entity.ApprovalActionApproved {
|
||||
step = utils.ProjectFlockKandangStepDisetujui
|
||||
}
|
||||
|
||||
for _, d := range details {
|
||||
var pw entity.ProductWarehouse
|
||||
if err := tx.WithContext(c.Context()).Where("id = ?", d.ProductWarehouseId).First(&pw).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
err := s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
|
||||
approvalSvc := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(dbTransaction))
|
||||
ProjectFlockPopulationRepotx := s.ProjectflockPopulationRepo.WithTx(dbTransaction)
|
||||
chickinRepoTx := s.Repository.WithTx(dbTransaction)
|
||||
|
||||
continue
|
||||
for _, approvableID := range approvableIDs {
|
||||
|
||||
exists, err := s.ProjectflockKandangRepo.WithTx(dbTransaction).IdExists(c.Context(), approvableID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
return fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("ProjectFlockKandang %d not found", approvableID))
|
||||
}
|
||||
|
||||
if _, err := approvalSvc.CreateApproval(
|
||||
c.Context(),
|
||||
utils.ApprovalWorkflowProjectFlockKandang,
|
||||
approvableID,
|
||||
step,
|
||||
&action,
|
||||
actorID,
|
||||
req.Notes,
|
||||
); err != nil {
|
||||
|
||||
lower := strings.ToLower(err.Error())
|
||||
if !(strings.Contains(lower, "duplicate") || strings.Contains(lower, "unique constraint") || strings.Contains(lower, "23505")) {
|
||||
return err
|
||||
}
|
||||
return false, err
|
||||
s.Log.Infof("ignored duplicate approval for kandang %d: %v", approvableID, err)
|
||||
}
|
||||
|
||||
updatedQuantity := pw.Quantity + d.Quantity
|
||||
if err := productWarehouseRepoTx.PatchOne(c.Context(), pw.Id, map[string]any{"quantity": updatedQuantity}, nil); err != nil {
|
||||
return false, err
|
||||
if action == entity.ApprovalActionApproved {
|
||||
|
||||
var chickins []entity.ProjectChickin
|
||||
if err := chickinRepoTx.DB().WithContext(c.Context()).Where("project_flock_kandang_id = ?", approvableID).Find(&chickins).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, chickin := range chickins {
|
||||
population := &entity.ProjectFlockPopulation{
|
||||
ProjectChickinId: chickin.Id,
|
||||
ProductWarehouseId: chickin.ProductWarehouseId,
|
||||
TotalQty: chickin.PendingUsageQty,
|
||||
TotalUsedQty: 0,
|
||||
Notes: chickin.Notes,
|
||||
CreatedBy: actorID,
|
||||
}
|
||||
if err := ProjectFlockPopulationRepotx.CreateOne(c.Context(), population, nil); err != nil {
|
||||
lower := strings.ToLower(err.Error())
|
||||
if !(strings.Contains(lower, "duplicate") || strings.Contains(lower, "unique constraint") || strings.Contains(lower, "23505")) {
|
||||
return err
|
||||
}
|
||||
s.Log.Infof("ignored duplicate population for chickin %d: %v", chickin.Id, err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if err := tx.WithContext(c.Context()).Where("project_chickin_id = ?", chickin.Id).Delete(&entity.ProjectChickinDetail{}).Error; err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
restored, err := restoreFromDetails()
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to restore from chickin details: %+v", err)
|
||||
return rollback(err)
|
||||
}
|
||||
|
||||
if !restored {
|
||||
|
||||
projectflockkandang, err := pfkRepoTx.GetByID(c.Context(), population.ProjectFlockKandangId)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to get projectflock kandang: %+v", err)
|
||||
return rollback(err)
|
||||
if fiberErr, ok := err.(*fiber.Error); ok {
|
||||
return nil, fiberErr
|
||||
}
|
||||
|
||||
var warehouse entity.Warehouse
|
||||
if err := tx.WithContext(c.Context()).Where("kandang_id = ?", projectflockkandang.KandangId).First(&warehouse).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return rollback(fiber.NewError(fiber.StatusNotFound, "Warehouse not found for kandang"))
|
||||
}
|
||||
s.Log.Errorf("Failed to get warehouse: %+v", err)
|
||||
return rollback(err)
|
||||
}
|
||||
|
||||
var productWarehouse entity.ProductWarehouse
|
||||
err = tx.WithContext(c.Context()).Table("product_warehouses").
|
||||
Select("product_warehouses.*").
|
||||
Joins("JOIN products ON products.id = product_warehouses.product_id").
|
||||
Joins("JOIN product_categories ON product_categories.id = products.product_category_id").
|
||||
Where("product_categories.code = ? AND product_warehouses.warehouse_id = ?", "DOC", warehouse.Id).
|
||||
Order("product_warehouses.created_at DESC").
|
||||
First(&productWarehouse).Error
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return rollback(fiber.NewError(fiber.StatusNotFound, "Product Warehouse not found for the given Project Flock and Warehouse"))
|
||||
}
|
||||
s.Log.Errorf("Failed to get product warehouse: %+v", err)
|
||||
return rollback(err)
|
||||
}
|
||||
|
||||
updatedQuantity := productWarehouse.Quantity + chickin.Quantity
|
||||
if err := productWarehouseRepoTx.PatchOne(c.Context(), productWarehouse.Id, map[string]any{"quantity": updatedQuantity}, nil); err != nil {
|
||||
s.Log.Errorf("Failed to update product warehouse quantity: %+v", err)
|
||||
return rollback(err)
|
||||
}
|
||||
}
|
||||
|
||||
// delete chickin (single place)
|
||||
if err := chickinRepoTx.DeleteOne(c.Context(), id); err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return rollback(fiber.NewError(fiber.StatusNotFound, "Chickin not found"))
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Chickin not found")
|
||||
}
|
||||
s.Log.Errorf("Failed to delete chickin: %+v", err)
|
||||
return rollback(err)
|
||||
s.Log.Errorf("Failed to record approval for chickins %+v: %+v", approvableIDs, err)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to record approval")
|
||||
}
|
||||
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
s.Log.Errorf("Failed to commit transaction: %+v", err)
|
||||
return rollback(err)
|
||||
updated := make([]entity.ProjectChickin, 0)
|
||||
for _, kandangID := range approvableIDs {
|
||||
var chickins []entity.ProjectChickin
|
||||
if err := s.Repository.DB().WithContext(c.Context()).Where("project_flock_kandang_id = ?", kandangID).Find(&chickins).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
updated = append(updated, chickins...)
|
||||
}
|
||||
|
||||
return nil
|
||||
return updated, nil
|
||||
}
|
||||
|
||||
func (s *chickinService) Approve(c *fiber.Ctx, id uint) error {
|
||||
|
||||
// todo: ini contoh akhir jika sudah approved
|
||||
|
||||
chickin, err := s.Repository.GetByID(
|
||||
c.Context(),
|
||||
id,
|
||||
nil,
|
||||
)
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return fiber.NewError(fiber.StatusNotFound, "Chickin not found")
|
||||
func uniqueUintSlice(values []uint) []uint {
|
||||
seen := make(map[uint]struct{}, len(values))
|
||||
result := make([]uint, 0, len(values))
|
||||
for _, v := range values {
|
||||
if _, ok := seen[v]; ok {
|
||||
continue
|
||||
}
|
||||
seen[v] = struct{}{}
|
||||
result = append(result, v)
|
||||
}
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed get chickin by id: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
population, err := s.ProjectflockPopulationRepo.GetByProjectFlockKandangID(c.Context(), chickin.ProjectFlockKandangId)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to get project flock population: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.ProjectflockPopulationRepo.PatchOne(c.Context(), population.Id, map[string]any{
|
||||
"reserved_quantity": population.ReservedQuantity - chickin.Quantity,
|
||||
"initial_quantity": population.InitialQuantity + chickin.Quantity,
|
||||
"current_quantity": population.CurrentQuantity + chickin.Quantity,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to update project flock population: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package validation
|
||||
type Create struct {
|
||||
ProjectFlockKandangId uint `json:"project_flock_kandang_id" validate:"required,number,min=1"`
|
||||
ChickInDate string `json:"chick_in_date" validate:"required,datetime=2006-01-02"`
|
||||
Note string `json:"note" validate:"omitempty`
|
||||
Note string `json:"note" validate:"omitempty"`
|
||||
}
|
||||
|
||||
type Update struct {
|
||||
@@ -16,3 +16,9 @@ type Query struct {
|
||||
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"`
|
||||
ProjectFlockKandangId uint `query:"project_flock_kandang_id" validate:"omitempty,number,min=1"`
|
||||
}
|
||||
|
||||
type Approve struct {
|
||||
Action string `json:"action" validate:"required_strict"`
|
||||
ApprovableIds []uint `json:"approvable_ids" validate:"required_strict,min=1,dive,gt=0"`
|
||||
Notes *string `json:"notes,omitempty" validate:"omitempty,max=500"`
|
||||
}
|
||||
|
||||
+19
-1
@@ -9,8 +9,16 @@ import (
|
||||
)
|
||||
|
||||
type ProjectFlockPopulationRepository interface {
|
||||
repository.BaseRepository[entity.ProjectFlockPopulation]
|
||||
// domain-specific
|
||||
GetByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (*entity.ProjectFlockPopulation, error)
|
||||
|
||||
// subset of base repository methods used by services
|
||||
CreateOne(ctx context.Context, entity *entity.ProjectFlockPopulation, modifier func(*gorm.DB) *gorm.DB) error
|
||||
PatchOne(ctx context.Context, id uint, updates map[string]any, modifier func(*gorm.DB) *gorm.DB) error
|
||||
|
||||
// transaction helpers
|
||||
WithTx(tx *gorm.DB) ProjectFlockPopulationRepository
|
||||
DB() *gorm.DB
|
||||
}
|
||||
|
||||
type projectFlockPopulationRepositoryImpl struct {
|
||||
@@ -23,6 +31,16 @@ func NewProjectFlockPopulationRepository(db *gorm.DB) ProjectFlockPopulationRepo
|
||||
}
|
||||
}
|
||||
|
||||
func (r *projectFlockPopulationRepositoryImpl) WithTx(tx *gorm.DB) ProjectFlockPopulationRepository {
|
||||
return &projectFlockPopulationRepositoryImpl{
|
||||
BaseRepositoryImpl: repository.NewBaseRepository[entity.ProjectFlockPopulation](tx),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *projectFlockPopulationRepositoryImpl) DB() *gorm.DB {
|
||||
return r.BaseRepositoryImpl.DB()
|
||||
}
|
||||
|
||||
func (r *projectFlockPopulationRepositoryImpl) GetByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (*entity.ProjectFlockPopulation, error) {
|
||||
var record entity.ProjectFlockPopulation
|
||||
err := r.DB().WithContext(ctx).
|
||||
|
||||
@@ -16,6 +16,7 @@ type ProjectflockRepository interface {
|
||||
GetActiveByFlock(ctx context.Context, flockID uint) (*entity.ProjectFlock, error)
|
||||
GetMaxPeriodByFlock(ctx context.Context, flockID uint) (int, error)
|
||||
GetNextPeriodForFlock(ctx context.Context, flockID uint) (int, error)
|
||||
IdExists(ctx context.Context, id uint) (bool, error)
|
||||
}
|
||||
|
||||
type ProjectflockRepositoryImpl struct {
|
||||
@@ -28,6 +29,10 @@ func NewProjectflockRepository(db *gorm.DB) ProjectflockRepository {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ProjectflockRepositoryImpl) IdExists(ctx context.Context, id uint) (bool, error) {
|
||||
return repository.Exists[entity.ProjectFlock](ctx, r.DB(), id)
|
||||
}
|
||||
|
||||
func (r *ProjectflockRepositoryImpl) GetAllByFlock(ctx context.Context, flockID uint) ([]entity.ProjectFlock, error) {
|
||||
var records []entity.ProjectFlock
|
||||
if err := r.DB().WithContext(ctx).
|
||||
|
||||
+5
@@ -3,6 +3,7 @@ package repository
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@@ -14,6 +15,7 @@ type ProjectFlockKandangRepository interface {
|
||||
DeleteMany(ctx context.Context, projectFlockID uint, kandangIDs []uint) error
|
||||
GetAll(ctx context.Context) ([]entity.ProjectFlockKandang, error)
|
||||
WithTx(tx *gorm.DB) ProjectFlockKandangRepository
|
||||
IdExists(ctx context.Context, id uint) (bool, error)
|
||||
DB() *gorm.DB
|
||||
}
|
||||
|
||||
@@ -67,6 +69,9 @@ func (r *projectFlockKandangRepositoryImpl) WithTx(tx *gorm.DB) ProjectFlockKand
|
||||
func (r *projectFlockKandangRepositoryImpl) DB() *gorm.DB {
|
||||
return r.db
|
||||
}
|
||||
func (r *projectFlockKandangRepositoryImpl) IdExists(ctx context.Context, id uint) (bool, error) {
|
||||
return repository.Exists[entity.ProjectFlockKandang](ctx, r.db, id)
|
||||
}
|
||||
|
||||
func (r *projectFlockKandangRepositoryImpl) GetByID(ctx context.Context, id uint) (*entity.ProjectFlockKandang, error) {
|
||||
record := new(entity.ProjectFlockKandang)
|
||||
|
||||
@@ -670,18 +670,19 @@ func (s *recordingService) getPreviousRecording(tx *gorm.DB, projectFlockKandang
|
||||
}
|
||||
|
||||
func (s *recordingService) getTotalChick(tx *gorm.DB, projectFlockKandangId uint) (int64, error) {
|
||||
var population entity.ProjectFlockPopulation
|
||||
err := tx.
|
||||
Where("project_flock_kandang_id = ?", projectFlockKandangId).
|
||||
Order("created_at DESC").
|
||||
First(&population).Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return 0, nil
|
||||
}
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int64(math.Round(population.InitialQuantity)), nil
|
||||
// var population entity.ProjectFlockPopulation
|
||||
// err := tx.
|
||||
// Where("project_flock_kandang_id = ?", projectFlockKandangId).
|
||||
// Order("created_at DESC").
|
||||
// First(&population).Error
|
||||
// if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
// return 0, nil
|
||||
// }
|
||||
// if err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
//todo : nanti ganti lagi mas saya hardcode dulu
|
||||
return int64(math.Round(1000)), nil
|
||||
}
|
||||
|
||||
func (s *recordingService) getAverageBodyWeight(tx *gorm.DB, recordingID uint) (float64, error) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
chickins "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins"
|
||||
projectflocks "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks"
|
||||
recordings "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings"
|
||||
transferLayings "gitlab.com/mbugroup/lti-api.git/internal/modules/production/transfer_layings"
|
||||
// MODULE IMPORTS
|
||||
)
|
||||
|
||||
@@ -20,8 +21,9 @@ func RegisterRoutes(router fiber.Router, db *gorm.DB, validate *validator.Valida
|
||||
projectflocks.ProjectflockModule{},
|
||||
recordings.RecordingModule{},
|
||||
chickins.ChickinModule{},
|
||||
transferLayings.TransferLayingModule{},
|
||||
// MODULE REGISTRY
|
||||
}
|
||||
}
|
||||
|
||||
for _, m := range allModules {
|
||||
m.RegisterRoutes(group, db, validate)
|
||||
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/modules/production/transfer_layings/dto"
|
||||
service "gitlab.com/mbugroup/lti-api.git/internal/modules/production/transfer_layings/services"
|
||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/transfer_layings/validations"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/response"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type TransferLayingController struct {
|
||||
TransferLayingService service.TransferLayingService
|
||||
}
|
||||
|
||||
func NewTransferLayingController(transferLayingService service.TransferLayingService) *TransferLayingController {
|
||||
return &TransferLayingController{
|
||||
TransferLayingService: transferLayingService,
|
||||
}
|
||||
}
|
||||
|
||||
func (u *TransferLayingController) GetAll(c *fiber.Ctx) error {
|
||||
query := &validation.Query{
|
||||
Page: c.QueryInt("page", 1),
|
||||
Limit: c.QueryInt("limit", 10),
|
||||
Search: c.Query("search", ""),
|
||||
}
|
||||
|
||||
if query.Page < 1 || query.Limit < 1 {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "page and limit must be greater than 0")
|
||||
}
|
||||
|
||||
result, totalResults, err := u.TransferLayingService.GetAll(c, query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).
|
||||
JSON(response.SuccessWithPaginate[dto.TransferLayingListDTO]{
|
||||
Code: fiber.StatusOK,
|
||||
Status: "success",
|
||||
Message: "Get all transferLayings successfully",
|
||||
Meta: response.Meta{
|
||||
Page: query.Page,
|
||||
Limit: query.Limit,
|
||||
TotalPages: int64(math.Ceil(float64(totalResults) / float64(query.Limit))),
|
||||
TotalResults: totalResults,
|
||||
},
|
||||
Data: dto.ToTransferLayingListDTOs(result),
|
||||
})
|
||||
}
|
||||
|
||||
func (u *TransferLayingController) GetOne(c *fiber.Ctx) error {
|
||||
param := c.Params("id")
|
||||
|
||||
id, err := strconv.Atoi(param)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
|
||||
}
|
||||
|
||||
result, err := u.TransferLayingService.GetOne(c, uint(id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).
|
||||
JSON(response.Success{
|
||||
Code: fiber.StatusOK,
|
||||
Status: "success",
|
||||
Message: "Get transferLaying successfully",
|
||||
Data: dto.ToTransferLayingListDTO(*result),
|
||||
})
|
||||
}
|
||||
|
||||
func (u *TransferLayingController) CreateOne(c *fiber.Ctx) error {
|
||||
req := new(validation.Create)
|
||||
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
|
||||
}
|
||||
|
||||
result, err := u.TransferLayingService.CreateOne(c, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusCreated).
|
||||
JSON(response.Success{
|
||||
Code: fiber.StatusCreated,
|
||||
Status: "success",
|
||||
Message: "Create transferLaying successfully",
|
||||
Data: dto.ToTransferLayingListDTO(*result),
|
||||
})
|
||||
}
|
||||
|
||||
func (u *TransferLayingController) UpdateOne(c *fiber.Ctx) error {
|
||||
req := new(validation.Update)
|
||||
param := c.Params("id")
|
||||
|
||||
id, err := strconv.Atoi(param)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
|
||||
}
|
||||
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
|
||||
}
|
||||
|
||||
result, err := u.TransferLayingService.UpdateOne(c, req, uint(id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).
|
||||
JSON(response.Success{
|
||||
Code: fiber.StatusOK,
|
||||
Status: "success",
|
||||
Message: "Update transferLaying successfully",
|
||||
Data: dto.ToTransferLayingListDTO(*result),
|
||||
})
|
||||
}
|
||||
|
||||
func (u *TransferLayingController) DeleteOne(c *fiber.Ctx) error {
|
||||
param := c.Params("id")
|
||||
|
||||
id, err := strconv.Atoi(param)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
|
||||
}
|
||||
|
||||
if err := u.TransferLayingService.DeleteOne(c, uint(id)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).
|
||||
JSON(response.Common{
|
||||
Code: fiber.StatusOK,
|
||||
Status: "success",
|
||||
Message: "Delete transferLaying successfully",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto"
|
||||
)
|
||||
|
||||
// === DTO Structs ===
|
||||
|
||||
type TransferLayingBaseDTO struct {
|
||||
Id uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type TransferLayingListDTO struct {
|
||||
TransferLayingBaseDTO
|
||||
CreatedUser *userDTO.UserBaseDTO `json:"created_user"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
type TransferLayingDetailDTO struct {
|
||||
TransferLayingListDTO
|
||||
}
|
||||
|
||||
// === Mapper Functions ===
|
||||
|
||||
func ToTransferLayingBaseDTO(e entity.LayingTransfer) TransferLayingBaseDTO {
|
||||
return TransferLayingBaseDTO{
|
||||
Id: e.Id,
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func ToTransferLayingListDTO(e entity.LayingTransfer) TransferLayingListDTO {
|
||||
var createdUser *userDTO.UserBaseDTO
|
||||
if e.CreatedUser.Id != 0 {
|
||||
mapped := userDTO.ToUserBaseDTO(*e.CreatedUser)
|
||||
createdUser = &mapped
|
||||
}
|
||||
|
||||
return TransferLayingListDTO{
|
||||
TransferLayingBaseDTO: ToTransferLayingBaseDTO(e),
|
||||
CreatedAt: e.CreatedAt,
|
||||
UpdatedAt: e.UpdatedAt,
|
||||
CreatedUser: createdUser,
|
||||
}
|
||||
}
|
||||
|
||||
func ToTransferLayingListDTOs(e []entity.LayingTransfer) []TransferLayingListDTO {
|
||||
result := make([]TransferLayingListDTO, len(e))
|
||||
for i, r := range e {
|
||||
result[i] = ToTransferLayingListDTO(r)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func ToTransferLayingDetailDTO(e entity.LayingTransfer) TransferLayingDetailDTO {
|
||||
return TransferLayingDetailDTO{
|
||||
TransferLayingListDTO: ToTransferLayingListDTO(e),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package transfer_layings
|
||||
|
||||
import (
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"gorm.io/gorm"
|
||||
|
||||
rProjectFlock "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
||||
rTransferLaying "gitlab.com/mbugroup/lti-api.git/internal/modules/production/transfer_layings/repositories"
|
||||
sTransferLaying "gitlab.com/mbugroup/lti-api.git/internal/modules/production/transfer_layings/services"
|
||||
|
||||
rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories"
|
||||
sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||
)
|
||||
|
||||
type TransferLayingModule struct{}
|
||||
|
||||
func (TransferLayingModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *validator.Validate) {
|
||||
transferLayingRepo := rTransferLaying.NewTransferLayingRepository(db)
|
||||
userRepo := rUser.NewUserRepository(db)
|
||||
projectFlockRepo := rProjectFlock.NewProjectflockRepository(db)
|
||||
projectFlockKandangRepo := rProjectFlock.NewProjectFlockKandangRepository(db)
|
||||
|
||||
transferLayingService := sTransferLaying.NewTransferLayingService(transferLayingRepo, projectFlockRepo, projectFlockKandangRepo, validate)
|
||||
userService := sUser.NewUserService(userRepo, validate)
|
||||
|
||||
TransferLayingRoutes(router, userService, transferLayingService)
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type TransferLayingRepository interface {
|
||||
repository.BaseRepository[entity.LayingTransfer]
|
||||
|
||||
}
|
||||
|
||||
type TransferLayingRepositoryImpl struct {
|
||||
*repository.BaseRepositoryImpl[entity.LayingTransfer]
|
||||
}
|
||||
|
||||
func NewTransferLayingRepository(db *gorm.DB) TransferLayingRepository {
|
||||
return &TransferLayingRepositoryImpl{
|
||||
BaseRepositoryImpl: repository.NewBaseRepository[entity.LayingTransfer](db),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package transfer_layings
|
||||
|
||||
import (
|
||||
// m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||
controller "gitlab.com/mbugroup/lti-api.git/internal/modules/production/transfer_layings/controllers"
|
||||
transferLaying "gitlab.com/mbugroup/lti-api.git/internal/modules/production/transfer_layings/services"
|
||||
user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func TransferLayingRoutes(v1 fiber.Router, u user.UserService, s transferLaying.TransferLayingService) {
|
||||
ctrl := controller.NewTransferLayingController(s)
|
||||
|
||||
route := v1.Group("/transfer_layings")
|
||||
|
||||
// route.Get("/", m.Auth(u), ctrl.GetAll)
|
||||
// route.Post("/", m.Auth(u), ctrl.CreateOne)
|
||||
// route.Get("/:id", m.Auth(u), ctrl.GetOne)
|
||||
// route.Patch("/:id", m.Auth(u), ctrl.UpdateOne)
|
||||
// route.Delete("/:id", m.Auth(u), ctrl.DeleteOne)
|
||||
|
||||
route.Get("/", ctrl.GetAll)
|
||||
route.Post("/", ctrl.CreateOne)
|
||||
route.Get("/:id", ctrl.GetOne)
|
||||
route.Patch("/:id", ctrl.UpdateOne)
|
||||
route.Delete("/:id", ctrl.DeleteOne)
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
common "gitlab.com/mbugroup/lti-api.git/internal/common/service"
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
ProjectFlockRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
||||
repository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/transfer_layings/repositories"
|
||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/transfer_layings/validations"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type TransferLayingService interface {
|
||||
GetAll(ctx *fiber.Ctx, params *validation.Query) ([]entity.LayingTransfer, int64, error)
|
||||
GetOne(ctx *fiber.Ctx, id uint) (*entity.LayingTransfer, error)
|
||||
CreateOne(ctx *fiber.Ctx, req *validation.Create) (*entity.LayingTransfer, error)
|
||||
UpdateOne(ctx *fiber.Ctx, req *validation.Update, id uint) (*entity.LayingTransfer, error)
|
||||
DeleteOne(ctx *fiber.Ctx, id uint) error
|
||||
}
|
||||
|
||||
type transferLayingService struct {
|
||||
Log *logrus.Logger
|
||||
Validate *validator.Validate
|
||||
Repository repository.TransferLayingRepository
|
||||
ProjectFlockRepo ProjectFlockRepository.ProjectflockRepository
|
||||
ProjectFlockKandangRepo ProjectFlockRepository.ProjectFlockKandangRepository
|
||||
}
|
||||
|
||||
func NewTransferLayingService(repo repository.TransferLayingRepository, projectFlockRepo ProjectFlockRepository.ProjectflockRepository, projectFlockKandangRepo ProjectFlockRepository.ProjectFlockKandangRepository, validate *validator.Validate) TransferLayingService {
|
||||
return &transferLayingService{
|
||||
Log: utils.Log,
|
||||
Validate: validate,
|
||||
Repository: repo,
|
||||
ProjectFlockRepo: projectFlockRepo,
|
||||
ProjectFlockKandangRepo: projectFlockKandangRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (s transferLayingService) withRelations(db *gorm.DB) *gorm.DB {
|
||||
return db.Preload("CreatedUser")
|
||||
}
|
||||
|
||||
func (s transferLayingService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.LayingTransfer, int64, error) {
|
||||
if err := s.Validate.Struct(params); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
offset := (params.Page - 1) * params.Limit
|
||||
|
||||
transferLayings, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
|
||||
db = s.withRelations(db)
|
||||
if params.Search != "" {
|
||||
return db.Where("name LIKE ?", "%"+params.Search+"%")
|
||||
}
|
||||
return db.Order("created_at DESC").Order("updated_at DESC")
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to get transferLayings: %+v", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
return transferLayings, total, nil
|
||||
}
|
||||
|
||||
func (s transferLayingService) GetOne(c *fiber.Ctx, id uint) (*entity.LayingTransfer, error) {
|
||||
transferLaying, err := s.Repository.GetByID(c.Context(), id, s.withRelations)
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "TransferLaying not found")
|
||||
}
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed get transferLaying by id: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
return transferLaying, nil
|
||||
}
|
||||
|
||||
func (s *transferLayingService) CreateOne(c *fiber.Ctx, req *validation.Create) (*entity.LayingTransfer, error) {
|
||||
if err := s.Validate.Struct(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := common.EnsureRelations(c.Context(),
|
||||
common.RelationCheck{Name: "Source Project Flock", ID: &req.SourceProjectFlockId, Exists: s.ProjectFlockRepo.IdExists},
|
||||
common.RelationCheck{Name: "Target Project Flock", ID: &req.TargetProjectFlockId, Exists: s.ProjectFlockRepo.IdExists},
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, detail := range req.Details {
|
||||
if err := common.EnsureRelations(c.Context(),
|
||||
common.RelationCheck{Name: "Project Flock Kandang", ID: &detail.SourceProjectFlockKandangId, Exists: s.ProjectFlockKandangRepo.IdExists},
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
transferDate, err := utils.ParseDateString(req.TransferDate)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid transfer date format")
|
||||
}
|
||||
|
||||
var totalQty float64
|
||||
for _, item := range req.Details {
|
||||
totalQty += item.Quantity
|
||||
}
|
||||
|
||||
err = s.Repository.DB().WithContext(c.Context()).Transaction(func(dbTransaction *gorm.DB) error {
|
||||
|
||||
createBody := &entity.LayingTransfer{
|
||||
Notes: req.Reason,
|
||||
FromProjectFlockId: req.SourceProjectFlockId,
|
||||
ToProjectFlockId: req.TargetProjectFlockId,
|
||||
TransferDate: transferDate,
|
||||
TotalQty: totalQty,
|
||||
CreatedBy: 1, //todo : harus diambil dari auth
|
||||
}
|
||||
|
||||
if err := s.Repository.WithTx(dbTransaction).CreateOne(c.Context(), createBody, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (s transferLayingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint) (*entity.LayingTransfer, error) {
|
||||
if err := s.Validate.Struct(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updateBody := make(map[string]any)
|
||||
|
||||
if req.Name != nil {
|
||||
updateBody["name"] = *req.Name
|
||||
}
|
||||
|
||||
if len(updateBody) == 0 {
|
||||
return s.GetOne(c, id)
|
||||
}
|
||||
|
||||
if err := s.Repository.PatchOne(c.Context(), id, updateBody, nil); err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "TransferLaying not found")
|
||||
}
|
||||
s.Log.Errorf("Failed to update transferLaying: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.GetOne(c, id)
|
||||
}
|
||||
|
||||
func (s transferLayingService) DeleteOne(c *fiber.Ctx, id uint) error {
|
||||
if err := s.Repository.DeleteOne(c.Context(), id); err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return fiber.NewError(fiber.StatusNotFound, "TransferLaying not found")
|
||||
}
|
||||
s.Log.Errorf("Failed to delete transferLaying: %+v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package validation
|
||||
|
||||
type CreateDetail struct {
|
||||
SourceProjectFlockKandangId uint `json:"source_project_flock_kandang_id" validate:"required"`
|
||||
Quantity float64 `json:"quantity" validate:"required,gt=0"`
|
||||
}
|
||||
|
||||
type Create struct {
|
||||
TransferDate string `json:"transfer_date" validate:"required,datetime=2006-01-02"`
|
||||
SourceProjectFlockId uint `json:"source_project_flock_id" validate:"required"`
|
||||
TargetProjectFlockId uint `json:"target_project_flock_id" validate:"required"`
|
||||
Details []CreateDetail `json:"details" validate:"required,min=1,dive,required"`
|
||||
Reason string `json:"reason" validate:"omitempty,max=1000"`
|
||||
}
|
||||
|
||||
type Update struct {
|
||||
Name *string `json:"name,omitempty" validate:"omitempty"`
|
||||
}
|
||||
|
||||
type Query struct {
|
||||
Page int `query:"page" validate:"omitempty,number,min=1,gt=0"`
|
||||
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100,gt=0"`
|
||||
Search string `query:"search" validate:"omitempty,max=50"`
|
||||
}
|
||||
@@ -79,6 +79,24 @@ const (
|
||||
WarehouseTypeKandang WarehouseType = "KANDANG"
|
||||
)
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Stock log
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
type StockLogTransactionType string
|
||||
|
||||
const (
|
||||
StockLogTransactionTypeIncrease StockLogTransactionType = "INCREASE"
|
||||
StockLogTransactionTypeDecrease StockLogTransactionType = "DECREASE"
|
||||
)
|
||||
|
||||
type StockLogType string
|
||||
|
||||
const (
|
||||
StockLogTypeAdjustment StockLogType = "ADJUSTMENT"
|
||||
StockLogTypeTransfer StockLogType = "TRANSFER"
|
||||
)
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// WarehouseType
|
||||
// -------------------------------------------------------------------
|
||||
@@ -140,6 +158,20 @@ var ProjectFlockApprovalSteps = map[approvalutils.ApprovalStep]string{
|
||||
ProjectFlockStepAktif: "Aktif",
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Project Flock Kandang Approval
|
||||
// -------------------------------------------------------------------
|
||||
const (
|
||||
ApprovalWorkflowProjectFlockKandang approvalutils.ApprovalWorkflowKey = approvalutils.ApprovalWorkflowKey("PROJECT_FLOCK_KANDANGS")
|
||||
ProjectFlockKandangStepPengajuan approvalutils.ApprovalStep = 1
|
||||
ProjectFlockKandangStepDisetujui approvalutils.ApprovalStep = 2
|
||||
)
|
||||
|
||||
var ProjectFlockKandangApprovalSteps = map[approvalutils.ApprovalStep]string{
|
||||
ProjectFlockKandangStepPengajuan: "Pengajuan",
|
||||
ProjectFlockKandangStepDisetujui: "Disetujui",
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Validators
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user