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 {
|
if err := seedTransferStock(tx, adminID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := seedChickin(tx, adminID); err != nil {
|
// if err := seedChickin(tx, adminID); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
fmt.Println("✅ Master data seeding completed")
|
fmt.Println("✅ Master data seeding completed")
|
||||||
return nil
|
return nil
|
||||||
@@ -1134,151 +1134,151 @@ func seedTransferStock(tx *gorm.DB, createdBy uint) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func seedChickin(tx *gorm.DB, createdBy uint) error {
|
// func seedChickin(tx *gorm.DB, createdBy uint) error {
|
||||||
seeds := []struct {
|
// seeds := []struct {
|
||||||
ProjectFlockKandangId uint
|
// ProjectFlockKandangId uint
|
||||||
ChickInDate string
|
// ChickInDate string
|
||||||
Quantity float64
|
// Quantity float64
|
||||||
Note string
|
// Note string
|
||||||
}{
|
// }{
|
||||||
{ProjectFlockKandangId: 1, ChickInDate: "2025-10-20", Quantity: 100, Note: "Seeder chickin 1"},
|
// {ProjectFlockKandangId: 1, ChickInDate: "2025-10-20", Quantity: 100, Note: "Seeder chickin 1"},
|
||||||
{ProjectFlockKandangId: 2, ChickInDate: "2025-10-21", Quantity: 200, Note: "Seeder chickin 2"},
|
// {ProjectFlockKandangId: 2, ChickInDate: "2025-10-21", Quantity: 200, Note: "Seeder chickin 2"},
|
||||||
}
|
// }
|
||||||
|
|
||||||
for _, seed := range seeds {
|
// for _, seed := range seeds {
|
||||||
chickinDate, err := time.Parse("2006-01-02", seed.ChickInDate)
|
// chickinDate, err := time.Parse("2006-01-02", seed.ChickInDate)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Insert ProjectChickin jika belum ada
|
// // Insert ProjectChickin jika belum ada
|
||||||
var chickin entity.ProjectChickin
|
// var chickin entity.ProjectChickin
|
||||||
err = tx.Where("project_flock_kandang_id = ? AND chick_in_date = ?", seed.ProjectFlockKandangId, chickinDate).
|
// err = tx.Where("project_flock_kandang_id = ? AND chick_in_date = ?", seed.ProjectFlockKandangId, chickinDate).
|
||||||
First(&chickin).Error
|
// First(&chickin).Error
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
// if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
chickin = entity.ProjectChickin{
|
// chickin = entity.ProjectChickin{
|
||||||
ProjectFlockKandangId: seed.ProjectFlockKandangId,
|
// ProjectFlockKandangId: seed.ProjectFlockKandangId,
|
||||||
ChickInDate: chickinDate,
|
// ChickInDate: chickinDate,
|
||||||
Quantity: seed.Quantity,
|
// Quantity: seed.Quantity,
|
||||||
Note: seed.Note,
|
// Note: seed.Note,
|
||||||
CreatedBy: createdBy,
|
// CreatedBy: createdBy,
|
||||||
}
|
// }
|
||||||
if err := tx.Create(&chickin).Error; err != nil {
|
// if err := tx.Create(&chickin).Error; err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
} else if err != nil {
|
// } else if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
var population entity.ProjectFlockPopulation
|
// var population entity.ProjectFlockPopulation
|
||||||
err = tx.Where("project_flock_kandang_id = ?", seed.ProjectFlockKandangId).First(&population).Error
|
// err = tx.Where("project_flock_kandang_id = ?", seed.ProjectFlockKandangId).First(&population).Error
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
// if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
population = entity.ProjectFlockPopulation{
|
// population = entity.ProjectFlockPopulation{
|
||||||
ProjectFlockKandangId: seed.ProjectFlockKandangId,
|
// ProjectFlockKandangId: seed.ProjectFlockKandangId,
|
||||||
InitialQuantity: seed.Quantity,
|
// InitialQuantity: seed.Quantity,
|
||||||
CurrentQuantity: seed.Quantity,
|
// CurrentQuantity: seed.Quantity,
|
||||||
ReservedQuantity: 0,
|
// ReservedQuantity: 0,
|
||||||
CreatedBy: createdBy,
|
// CreatedBy: createdBy,
|
||||||
}
|
// }
|
||||||
if err := tx.Create(&population).Error; err != nil {
|
// if err := tx.Create(&population).Error; err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
} else if err != nil {
|
// } else if err != nil {
|
||||||
return err
|
// return err
|
||||||
} else {
|
// } else {
|
||||||
// Update population quantities
|
// // Update population quantities
|
||||||
if err := tx.Model(&entity.ProjectFlockPopulation{}).
|
// if err := tx.Model(&entity.ProjectFlockPopulation{}).
|
||||||
Where("id = ?", population.Id).
|
// Where("id = ?", population.Id).
|
||||||
Updates(map[string]any{
|
// Updates(map[string]any{
|
||||||
"initial_quantity": population.InitialQuantity + seed.Quantity,
|
// "initial_quantity": population.InitialQuantity + seed.Quantity,
|
||||||
"current_quantity": population.CurrentQuantity + seed.Quantity,
|
// "current_quantity": population.CurrentQuantity + seed.Quantity,
|
||||||
"reserved_quantity": 0,
|
// "reserved_quantity": 0,
|
||||||
}).Error; err != nil {
|
// }).Error; err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
var pfk entity.ProjectFlockKandang
|
// var pfk entity.ProjectFlockKandang
|
||||||
if err := tx.Where("id = ?", seed.ProjectFlockKandangId).First(&pfk).Error; err != nil {
|
// if err := tx.Where("id = ?", seed.ProjectFlockKandangId).First(&pfk).Error; err != nil {
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
// if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
// no pivot found; skip creating details
|
// // no pivot found; skip creating details
|
||||||
continue
|
// continue
|
||||||
}
|
// }
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
var warehouse entity.Warehouse
|
// var warehouse entity.Warehouse
|
||||||
if err := tx.Where("kandang_id = ?", pfk.KandangId).First(&warehouse).Error; err != nil {
|
// if err := tx.Where("kandang_id = ?", pfk.KandangId).First(&warehouse).Error; err != nil {
|
||||||
// if warehouse not found, cannot create details
|
// // if warehouse not found, cannot create details
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
// if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
continue
|
// continue
|
||||||
}
|
// }
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
var productWarehouses []entity.ProductWarehouse
|
// var productWarehouses []entity.ProductWarehouse
|
||||||
err = tx.Table("product_warehouses").
|
// err = tx.Table("product_warehouses").
|
||||||
Select("product_warehouses.*").
|
// Select("product_warehouses.*").
|
||||||
Joins("JOIN products ON products.id = product_warehouses.product_id").
|
// Joins("JOIN products ON products.id = product_warehouses.product_id").
|
||||||
Joins("JOIN product_categories ON product_categories.id = products.product_category_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).
|
// Where("product_categories.code = ? AND product_warehouses.warehouse_id = ?", "DOC", warehouse.Id).
|
||||||
Order("product_warehouses.created_at DESC").
|
// Order("product_warehouses.created_at DESC").
|
||||||
Find(&productWarehouses).Error
|
// Find(&productWarehouses).Error
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
// If no product warehouses found, keep existing chickin.Quantity and skip details
|
// // If no product warehouses found, keep existing chickin.Quantity and skip details
|
||||||
if len(productWarehouses) == 0 {
|
// if len(productWarehouses) == 0 {
|
||||||
continue
|
// continue
|
||||||
}
|
// }
|
||||||
|
|
||||||
// sum all pw quantities and set chickin.Quantity to that total (mimic CreateOne)
|
// // sum all pw quantities and set chickin.Quantity to that total (mimic CreateOne)
|
||||||
totalQty := 0.0
|
// totalQty := 0.0
|
||||||
for _, pw := range productWarehouses {
|
// for _, pw := range productWarehouses {
|
||||||
totalQty += pw.Quantity
|
// totalQty += pw.Quantity
|
||||||
}
|
// }
|
||||||
|
|
||||||
if chickin.Quantity != totalQty {
|
// if chickin.Quantity != totalQty {
|
||||||
if err := tx.Model(&entity.ProjectChickin{}).Where("id = ?", chickin.Id).Update("quantity", totalQty).Error; err != nil {
|
// if err := tx.Model(&entity.ProjectChickin{}).Where("id = ?", chickin.Id).Update("quantity", totalQty).Error; err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
chickin.Quantity = totalQty
|
// chickin.Quantity = totalQty
|
||||||
}
|
// }
|
||||||
|
|
||||||
for _, pw := range productWarehouses {
|
// for _, pw := range productWarehouses {
|
||||||
// ensure detail exists or create it with full pw.Quantity
|
// // ensure detail exists or create it with full pw.Quantity
|
||||||
var detail entity.ProjectChickinDetail
|
// var detail entity.ProjectChickinDetail
|
||||||
err = tx.Where("project_chickin_id = ? AND product_warehouse_id = ?", chickin.Id, pw.Id).First(&detail).Error
|
// err = tx.Where("project_chickin_id = ? AND product_warehouse_id = ?", chickin.Id, pw.Id).First(&detail).Error
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
// if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
detail = entity.ProjectChickinDetail{
|
// detail = entity.ProjectChickinDetail{
|
||||||
ProjectChickinId: chickin.Id,
|
// ProjectChickinId: chickin.Id,
|
||||||
ProductWarehouseId: pw.Id,
|
// ProductWarehouseId: pw.Id,
|
||||||
Quantity: pw.Quantity,
|
// Quantity: pw.Quantity,
|
||||||
CreatedBy: createdBy,
|
// CreatedBy: createdBy,
|
||||||
}
|
// }
|
||||||
if err := tx.Create(&detail).Error; err != nil {
|
// if err := tx.Create(&detail).Error; err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
} else if err != nil {
|
// } else if err != nil {
|
||||||
return err
|
// return err
|
||||||
} else {
|
// } else {
|
||||||
if detail.Quantity != pw.Quantity {
|
// if detail.Quantity != pw.Quantity {
|
||||||
if err := tx.Model(&entity.ProjectChickinDetail{}).Where("id = ?", detail.Id).Update("quantity", pw.Quantity).Error; err != nil {
|
// if err := tx.Model(&entity.ProjectChickinDetail{}).Where("id = ?", detail.Id).Update("quantity", pw.Quantity).Error; err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// zero out pw quantity
|
// // zero out pw quantity
|
||||||
if err := tx.Model(&entity.ProductWarehouse{}).Where("id = ?", pw.Id).Update("quantity", 0).Error; err != nil {
|
// if err := tx.Model(&entity.ProductWarehouse{}).Where("id = ?", pw.Id).Update("quantity", 0).Error; err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
func ptr[T any](v T) *T {
|
func ptr[T any](v T) *T {
|
||||||
return &v
|
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"`
|
Id uint `gorm:"primaryKey"`
|
||||||
ProjectFlockKandangId uint `gorm:"not null"`
|
ProjectFlockKandangId uint `gorm:"not null"`
|
||||||
ChickInDate time.Time `gorm:"not null"`
|
ChickInDate time.Time `gorm:"not null"`
|
||||||
Quantity float64 `gorm:"not null"`
|
ProductWarehouseId uint `gorm:"not null"`
|
||||||
Note string `gorm:"type:text"`
|
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"`
|
CreatedBy uint `gorm:"not null"`
|
||||||
CreatedAt time.Time `gorm:"autoCreateTime"`
|
CreatedAt time.Time `gorm:"autoCreateTime"`
|
||||||
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
||||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||||
|
|
||||||
ProjectFlockKandang ProjectFlockKandang `gorm:"foreignKey:ProjectFlockKandangId;references:Id"`
|
ProjectFlockKandang *ProjectFlockKandang `gorm:"foreignKey:ProjectFlockKandangId;references:Id"`
|
||||||
CreatedUser User `gorm:"foreignKey:CreatedBy;references:Id"`
|
ProductWarehouse *ProductWarehouse `gorm:"foreignKey:ProductWarehouseId;references:Id"`
|
||||||
|
CreatedUser *User `gorm:"foreignKey:CreatedBy;references:Id"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,15 +8,17 @@ import (
|
|||||||
|
|
||||||
type ProjectFlockPopulation struct {
|
type ProjectFlockPopulation struct {
|
||||||
Id uint `gorm:"primaryKey"`
|
Id uint `gorm:"primaryKey"`
|
||||||
ProjectFlockKandangId uint `gorm:"not null"`
|
ProjectChickinId uint `gorm:"not null"`
|
||||||
InitialQuantity float64 `gorm:"type:numeric(15,3);not null"`
|
ProductWarehouseId uint `gorm:"not null"`
|
||||||
CurrentQuantity float64 `gorm:"type:numeric(15,3);not null"`
|
TotalQty float64 `gorm:"type:numeric(15,3);not null"`
|
||||||
ReservedQuantity float64 `gorm:"type:numeric(15,3)"`
|
TotalUsedQty float64 `gorm:"type:numeric(15,3);not null"`
|
||||||
|
Notes string `gorm:"type:text"`
|
||||||
CreatedBy uint `gorm:"not null"`
|
CreatedBy uint `gorm:"not null"`
|
||||||
CreatedAt time.Time `gorm:"autoCreateTime"`
|
CreatedAt time.Time `gorm:"autoCreateTime"`
|
||||||
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
||||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||||
|
|
||||||
ProjectFlockKandang *ProjectFlockKandang `gorm:"foreignKey:ProjectFlockKandangId;references:Id"`
|
ProjectChickin *ProjectChickin `gorm:"foreignKey:ProjectChickinId;references:Id"`
|
||||||
|
ProductWarehouse *ProductWarehouse `gorm:"foreignKey:ProductWarehouseId;references:Id"`
|
||||||
CreatedUser *User `gorm:"foreignKey:CreatedBy;references:Id"`
|
CreatedUser *User `gorm:"foreignKey:CreatedBy;references:Id"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,3 +28,4 @@ type ProjectFlock struct {
|
|||||||
KandangHistory []ProjectFlockKandang `gorm:"foreignKey:ProjectFlockId;references:Id"`
|
KandangHistory []ProjectFlockKandang `gorm:"foreignKey:ProjectFlockId;references:Id"`
|
||||||
LatestApproval *Approval `gorm:"-" json:"-"`
|
LatestApproval *Approval `gorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ type ProjectFlockKandang struct {
|
|||||||
ProjectFlockId uint `gorm:"not null;index:idx_project_flock_kandangs_project;uniqueIndex:idx_project_flock_kandangs_unique"`
|
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"`
|
KandangId uint `gorm:"not null;index:idx_project_flock_kandangs_kandang;uniqueIndex:idx_project_flock_kandangs_unique"`
|
||||||
CreatedAt time.Time `gorm:"autoCreateTime"`
|
CreatedAt time.Time `gorm:"autoCreateTime"`
|
||||||
|
|
||||||
ProjectFlock ProjectFlock `gorm:"foreignKey:ProjectFlockId;references:Id"`
|
ProjectFlock ProjectFlock `gorm:"foreignKey:ProjectFlockId;references:Id"`
|
||||||
Kandang Kandang `gorm:"foreignKey:KandangId;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",
|
"LOKASI",
|
||||||
"KANDANG",
|
"KANDANG",
|
||||||
},
|
},
|
||||||
|
"stock_log": map[string][]string{
|
||||||
|
"log_types": []string{"TRANSFER", "ADJUSTMENT"},
|
||||||
|
"transaction_types": []string{"INCREASE", "DECREASE"},
|
||||||
|
},
|
||||||
"supplier_categories": []string{
|
"supplier_categories": []string{
|
||||||
"BOP",
|
"BOP",
|
||||||
"SAPRONAK",
|
"SAPRONAK",
|
||||||
|
|||||||
+29
@@ -17,6 +17,9 @@ type ProductWarehouseRepository interface {
|
|||||||
ExistsByID(ctx context.Context, id uint) (bool, error)
|
ExistsByID(ctx context.Context, id uint) (bool, error)
|
||||||
GetProductWarehouseByProductAndWarehouseID(ctx context.Context, productId, warehouseId uint) (*entity.ProductWarehouse, error)
|
GetProductWarehouseByProductAndWarehouseID(ctx context.Context, productId, warehouseId uint) (*entity.ProductWarehouse, error)
|
||||||
GetByCategoryCodeAndWarehouseID(ctx context.Context, categoryCode string, 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 {
|
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) {
|
func (r *ProductWarehouseRepositoryImpl) IsProductExist(ctx context.Context, productId uint) (bool, error) {
|
||||||
return repository.Exists[entity.Product](ctx, r.db, productId)
|
return repository.Exists[entity.Product](ctx, r.db, productId)
|
||||||
}
|
}
|
||||||
@@ -89,3 +101,20 @@ func (r *ProductWarehouseRepositoryImpl) GetByCategoryCodeAndWarehouseID(ctx con
|
|||||||
}
|
}
|
||||||
return productWarehouses, nil
|
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 {
|
func (u *ChickinController) Approval(c *fiber.Ctx) error {
|
||||||
param := c.Params("id")
|
req := new(validation.Approve)
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
id, err := strconv.Atoi(param)
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := u.ChickinService.Approve(c, uint(id)); err != nil {
|
results, err := u.ChickinService.Approval(c, req)
|
||||||
|
if err != nil {
|
||||||
return err
|
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).
|
return c.Status(fiber.StatusOK).
|
||||||
JSON(response.Success{
|
JSON(response.Success{
|
||||||
Code: fiber.StatusOK,
|
Code: fiber.StatusOK,
|
||||||
Status: "success",
|
Status: "success",
|
||||||
Message: "Approve chickin successfully",
|
Message: message,
|
||||||
Data: nil,
|
Data: data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ type ChickinBaseDTO struct {
|
|||||||
Id uint `json:"id"`
|
Id uint `json:"id"`
|
||||||
ProjectFlockKandang *ProjectFlockKandangDTO `json:"project_flock_kandang"`
|
ProjectFlockKandang *ProjectFlockKandangDTO `json:"project_flock_kandang"`
|
||||||
ChickInDate time.Time `json:"chick_in_date"`
|
ChickInDate time.Time `json:"chick_in_date"`
|
||||||
Quantity float64 `json:"quantity"`
|
ProductWarehouseId uint `json:"product_warehouse_id"`
|
||||||
Note string `json:"note"`
|
UsageQty float64 `json:"usage_qty"`
|
||||||
|
PendingUsageQty float64 `json:"pending_usage_qty"`
|
||||||
|
Notes string `json:"notes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProjectFlockDTO struct {
|
type ProjectFlockDTO struct {
|
||||||
@@ -44,8 +46,10 @@ type ChickinSimpleDTO struct {
|
|||||||
Id uint `json:"id"`
|
Id uint `json:"id"`
|
||||||
ProjectFlockKandangId uint `json:"project_flock_kandang_id"`
|
ProjectFlockKandangId uint `json:"project_flock_kandang_id"`
|
||||||
ChickInDate time.Time `json:"chick_in_date"`
|
ChickInDate time.Time `json:"chick_in_date"`
|
||||||
Quantity float64 `json:"quantity"`
|
ProductWarehouseId uint `json:"product_warehouse_id"`
|
||||||
Note string `json:"note"`
|
UsageQty float64 `json:"usage_qty"`
|
||||||
|
PendingUsageQty float64 `json:"pending_usage_qty"`
|
||||||
|
Notes string `json:"notes"`
|
||||||
CreatedBy uint `json:"created_by"`
|
CreatedBy uint `json:"created_by"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,16 +142,18 @@ func ToProjectFlockKandangDTO(e entity.ProjectFlockKandang) ProjectFlockKandangD
|
|||||||
|
|
||||||
func ToChickinBaseDTO(e entity.ProjectChickin) ChickinBaseDTO {
|
func ToChickinBaseDTO(e entity.ProjectChickin) ChickinBaseDTO {
|
||||||
var pfk *ProjectFlockKandangDTO
|
var pfk *ProjectFlockKandangDTO
|
||||||
if e.ProjectFlockKandang.Id != 0 {
|
if e.ProjectFlockKandang != nil && e.ProjectFlockKandang.Id != 0 {
|
||||||
mapped := ToProjectFlockKandangDTO(e.ProjectFlockKandang)
|
mapped := ToProjectFlockKandangDTO(*e.ProjectFlockKandang)
|
||||||
pfk = &mapped
|
pfk = &mapped
|
||||||
}
|
}
|
||||||
return ChickinBaseDTO{
|
return ChickinBaseDTO{
|
||||||
Id: e.Id,
|
Id: e.Id,
|
||||||
ProjectFlockKandang: pfk,
|
ProjectFlockKandang: pfk,
|
||||||
ChickInDate: e.ChickInDate,
|
ChickInDate: e.ChickInDate,
|
||||||
Quantity: e.Quantity,
|
ProductWarehouseId: e.ProductWarehouseId,
|
||||||
Note: e.Note,
|
UsageQty: e.UsageQty,
|
||||||
|
PendingUsageQty: e.PendingUsageQty,
|
||||||
|
Notes: e.Notes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,21 +162,23 @@ func ToChickinSimpleDTO(e entity.ProjectChickin) ChickinSimpleDTO {
|
|||||||
Id: e.Id,
|
Id: e.Id,
|
||||||
ProjectFlockKandangId: e.ProjectFlockKandangId,
|
ProjectFlockKandangId: e.ProjectFlockKandangId,
|
||||||
ChickInDate: e.ChickInDate,
|
ChickInDate: e.ChickInDate,
|
||||||
Quantity: e.Quantity,
|
ProductWarehouseId: e.ProductWarehouseId,
|
||||||
Note: e.Note,
|
UsageQty: e.UsageQty,
|
||||||
|
PendingUsageQty: e.PendingUsageQty,
|
||||||
|
Notes: e.Notes,
|
||||||
CreatedBy: e.CreatedBy,
|
CreatedBy: e.CreatedBy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ToChickinListDTO(e entity.ProjectChickin) ChickinListDTO {
|
func ToChickinListDTO(e entity.ProjectChickin) ChickinListDTO {
|
||||||
var createdUser *userBaseDTO.UserBaseDTO
|
var createdUser *userBaseDTO.UserBaseDTO
|
||||||
if e.CreatedUser.Id != 0 {
|
if e.CreatedUser != nil && e.CreatedUser.Id != 0 {
|
||||||
mapped := userBaseDTO.ToUserBaseDTO(e.CreatedUser)
|
mapped := userBaseDTO.ToUserBaseDTO(*e.CreatedUser)
|
||||||
createdUser = &mapped
|
createdUser = &mapped
|
||||||
}
|
}
|
||||||
var pfk *ProjectFlockKandangDTO
|
var pfk *ProjectFlockKandangDTO
|
||||||
if e.ProjectFlockKandang.Id != 0 {
|
if e.ProjectFlockKandang != nil && e.ProjectFlockKandang.Id != 0 {
|
||||||
mapped := ToProjectFlockKandangDTO(e.ProjectFlockKandang)
|
mapped := ToProjectFlockKandangDTO(*e.ProjectFlockKandang)
|
||||||
pfk = &mapped
|
pfk = &mapped
|
||||||
}
|
}
|
||||||
return ChickinListDTO{
|
return ChickinListDTO{
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
package chickins
|
package chickins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/go-playground/validator/v10"
|
"github.com/go-playground/validator/v10"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"gorm.io/gorm"
|
"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"
|
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"
|
rKandang "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/repositories"
|
||||||
rWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/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"
|
rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories"
|
||||||
sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||||
|
|
||||||
|
utils "gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ChickinModule struct{}
|
type ChickinModule struct{}
|
||||||
@@ -32,6 +39,12 @@ func (ChickinModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *
|
|||||||
|
|
||||||
userRepo := rUser.NewUserRepository(db)
|
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)
|
chickinService := sChickin.NewChickinService(chickinRepo, kandangRepo, warehouseRepo, productWarehouseRepo, projectFlockRepo, projectflockkandangrepo, projectflockpopulationrepo, chickinDetailRepo, validate)
|
||||||
userService := sUser.NewUserService(userRepo, validate)
|
userService := sUser.NewUserService(userRepo, validate)
|
||||||
|
|
||||||
|
|||||||
+25
@@ -1,6 +1,8 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
@@ -8,6 +10,10 @@ import (
|
|||||||
|
|
||||||
type ProjectChickinDetailRepository interface {
|
type ProjectChickinDetailRepository interface {
|
||||||
repository.BaseRepository[entity.ProjectChickinDetail]
|
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 {
|
type ChickinDetailRepositoryImpl struct {
|
||||||
@@ -19,3 +25,22 @@ func NewChickinDetailRepository(db *gorm.DB) ProjectChickinDetailRepository {
|
|||||||
BaseRepositoryImpl: repository.NewBaseRepository[entity.ProjectChickinDetail](db),
|
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.Get("/:id", ctrl.GetOne)
|
||||||
route.Patch("/:id", ctrl.UpdateOne)
|
route.Patch("/:id", ctrl.UpdateOne)
|
||||||
route.Delete("/:id", ctrl.DeleteOne)
|
route.Delete("/:id", ctrl.DeleteOne)
|
||||||
route.Post("/:id/approve", ctrl.Approve)
|
route.Post("/approvals", ctrl.Approval)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,11 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"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"
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
|
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"
|
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)
|
CreateOne(ctx *fiber.Ctx, req *validation.Create) (*entity.ProjectChickin, error)
|
||||||
UpdateOne(ctx *fiber.Ctx, req *validation.Update, id uint) (*entity.ProjectChickin, error)
|
UpdateOne(ctx *fiber.Ctx, req *validation.Update, id uint) (*entity.ProjectChickin, error)
|
||||||
DeleteOne(ctx *fiber.Ctx, id uint) 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 {
|
type chickinService struct {
|
||||||
@@ -77,6 +81,7 @@ func (s chickinService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity
|
|||||||
}
|
}
|
||||||
|
|
||||||
offset := (params.Page - 1) * params.Limit
|
offset := (params.Page - 1) * params.Limit
|
||||||
|
|
||||||
chickins, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
|
chickins, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
|
||||||
db = s.withRelations(db)
|
db = s.withRelations(db)
|
||||||
if params.ProjectFlockKandangId != 0 {
|
if params.ProjectFlockKandangId != 0 {
|
||||||
@@ -109,107 +114,98 @@ func (s *chickinService) CreateOne(c *fiber.Ctx, req *validation.Create) (*entit
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
projectflockkandang, err := s.ProjectflockKandangRepo.GetByID(c.Context(), req.ProjectFlockKandangId)
|
projectFlockKandang, err := s.ProjectflockKandangRepo.GetByID(c.Context(), req.ProjectFlockKandangId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.Errorf("Failed to get projectflock kandang: %+v", err)
|
return nil, fiber.NewError(fiber.StatusNotFound, "Project Flock Kandang not found")
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
warehouse, err := s.WarehouseRepo.GetByKandangID(c.Context(), projectflockkandang.KandangId)
|
warehouse, err := s.WarehouseRepo.GetByKandangID(c.Context(), projectFlockKandang.KandangId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.Errorf("Failed to get warehouse: %+v", err)
|
return nil, fiber.NewError(fiber.StatusNotFound, "Warehouse for Kandang not found")
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// move complex DB query into repository for cleaner service
|
var productWarehouses []entity.ProductWarehouse
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
if totalQuantity < 1 {
|
if strings.ToUpper(strings.TrimSpace(projectFlockKandang.ProjectFlock.Category)) == string(utils.ProjectFlockCategoryGrowing) {
|
||||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Insufficient quantity in Product Warehouses")
|
|
||||||
|
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)
|
chickinDate, err := utils.ParseDateString(req.ChickInDate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.Errorf("Failed to parse chickin date: %+v", err)
|
|
||||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid ChickInDate format")
|
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid ChickInDate format")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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{
|
newChickin := &entity.ProjectChickin{
|
||||||
ProjectFlockKandangId: projectflockkandang.Id,
|
|
||||||
ChickInDate: chickinDate,
|
|
||||||
Quantity: totalQuantity,
|
|
||||||
Note: req.Note,
|
|
||||||
CreatedBy: 1, //todo: ganti dengan user login
|
|
||||||
}
|
|
||||||
err = s.Repository.CreateOne(c.Context(), newChickin, nil)
|
|
||||||
if err != nil {
|
|
||||||
s.Log.Errorf("Failed to create chickin: %+v", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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,
|
ProjectFlockKandangId: req.ProjectFlockKandangId,
|
||||||
InitialQuantity: 0,
|
ChickInDate: chickinDate,
|
||||||
CurrentQuantity: 0,
|
UsageQty: 0,
|
||||||
ReservedQuantity: newChickin.Quantity,
|
PendingUsageQty: productWarehouse.Quantity,
|
||||||
CreatedBy: 1, // todo: ganti dengan user login
|
ProductWarehouseId: productWarehouse.Id,
|
||||||
|
Notes: req.Note,
|
||||||
|
CreatedBy: actorID,
|
||||||
}
|
}
|
||||||
err = s.ProjectflockPopulationRepo.CreateOne(c.Context(), newPopulation, nil)
|
|
||||||
if err != nil {
|
newChikins = append(newChikins, newChickin)
|
||||||
s.Log.Errorf("Failed to create project flock population: %+v", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.GetOne(c, newChickin.Id)
|
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 {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return newChikins[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s chickinService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint) (*entity.ProjectChickin, error) {
|
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
|
updateBody["chick_in_date"] = req.ChickInDate
|
||||||
}
|
}
|
||||||
if req.Note != "" {
|
if req.Note != "" {
|
||||||
updateBody["note"] = req.Note
|
// entity uses `Notes` => column `notes`
|
||||||
|
updateBody["notes"] = req.Note
|
||||||
}
|
}
|
||||||
if len(updateBody) == 0 {
|
if len(updateBody) == 0 {
|
||||||
return s.GetOne(c, id)
|
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 {
|
func (s chickinService) DeleteOne(c *fiber.Ctx, id uint) error {
|
||||||
db := s.Repository.DB()
|
|
||||||
|
|
||||||
tx := db.WithContext(c.Context()).Begin()
|
// Simplified delete: directly call repository delete. Complex restore logic removed for now.
|
||||||
if tx.Error != nil {
|
if err := s.Repository.DeleteOne(c.Context(), id); err != 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)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
chickinRepoTx := s.Repository.WithTx(tx)
|
|
||||||
pfkRepoTx := s.ProjectflockKandangRepo.WithTx(tx)
|
|
||||||
productWarehouseRepoTx := s.ProductWarehouseRepo.WithTx(tx)
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return false, 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 err := tx.WithContext(c.Context()).Where("project_chickin_id = ?", chickin.Id).Delete(&entity.ProjectChickinDetail{}).Error; err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return true, 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
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"))
|
|
||||||
}
|
|
||||||
s.Log.Errorf("Failed to delete chickin: %+v", err)
|
|
||||||
return rollback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := tx.Commit().Error; err != nil {
|
|
||||||
s.Log.Errorf("Failed to commit transaction: %+v", err)
|
|
||||||
return rollback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return 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) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return fiber.NewError(fiber.StatusNotFound, "Chickin not found")
|
return fiber.NewError(fiber.StatusNotFound, "Chickin not found")
|
||||||
}
|
}
|
||||||
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 err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s chickinService) Approval(c *fiber.Ctx, req *validation.Approve) ([]entity.ProjectChickin, error) {
|
||||||
|
if err := s.Validate.Struct(req); err != nil {
|
||||||
|
return nil, 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")
|
||||||
|
}
|
||||||
|
|
||||||
|
approvableIDs := uniqueUintSlice(req.ApprovableIds)
|
||||||
|
if len(approvableIDs) == 0 {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, "approvable_ids must contain at least one id")
|
||||||
|
}
|
||||||
|
|
||||||
|
step := utils.ProjectFlockKandangStepPengajuan
|
||||||
|
if action == entity.ApprovalActionApproved {
|
||||||
|
step = utils.ProjectFlockKandangStepDisetujui
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
s.Log.Infof("ignored duplicate approval for kandang %d: %v", approvableID, 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if fiberErr, ok := err.(*fiber.Error); ok {
|
||||||
|
return nil, fiberErr
|
||||||
|
}
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, fiber.NewError(fiber.StatusNotFound, "Chickin not found")
|
||||||
|
}
|
||||||
|
s.Log.Errorf("Failed to record approval for chickins %+v: %+v", approvableIDs, err)
|
||||||
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to record approval")
|
||||||
|
}
|
||||||
|
|
||||||
|
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 updated, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package validation
|
|||||||
type Create struct {
|
type Create struct {
|
||||||
ProjectFlockKandangId uint `json:"project_flock_kandang_id" validate:"required,number,min=1"`
|
ProjectFlockKandangId uint `json:"project_flock_kandang_id" validate:"required,number,min=1"`
|
||||||
ChickInDate string `json:"chick_in_date" validate:"required,datetime=2006-01-02"`
|
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 {
|
type Update struct {
|
||||||
@@ -16,3 +16,9 @@ type Query struct {
|
|||||||
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"`
|
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100"`
|
||||||
ProjectFlockKandangId uint `query:"project_flock_kandang_id" validate:"omitempty,number,min=1"`
|
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 {
|
type ProjectFlockPopulationRepository interface {
|
||||||
repository.BaseRepository[entity.ProjectFlockPopulation]
|
// domain-specific
|
||||||
GetByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (*entity.ProjectFlockPopulation, error)
|
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 {
|
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) {
|
func (r *projectFlockPopulationRepositoryImpl) GetByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (*entity.ProjectFlockPopulation, error) {
|
||||||
var record entity.ProjectFlockPopulation
|
var record entity.ProjectFlockPopulation
|
||||||
err := r.DB().WithContext(ctx).
|
err := r.DB().WithContext(ctx).
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ type ProjectflockRepository interface {
|
|||||||
GetActiveByFlock(ctx context.Context, flockID uint) (*entity.ProjectFlock, error)
|
GetActiveByFlock(ctx context.Context, flockID uint) (*entity.ProjectFlock, error)
|
||||||
GetMaxPeriodByFlock(ctx context.Context, flockID uint) (int, error)
|
GetMaxPeriodByFlock(ctx context.Context, flockID uint) (int, error)
|
||||||
GetNextPeriodForFlock(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 {
|
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) {
|
func (r *ProjectflockRepositoryImpl) GetAllByFlock(ctx context.Context, flockID uint) ([]entity.ProjectFlock, error) {
|
||||||
var records []entity.ProjectFlock
|
var records []entity.ProjectFlock
|
||||||
if err := r.DB().WithContext(ctx).
|
if err := r.DB().WithContext(ctx).
|
||||||
|
|||||||
+5
@@ -3,6 +3,7 @@ package repository
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
@@ -14,6 +15,7 @@ type ProjectFlockKandangRepository interface {
|
|||||||
DeleteMany(ctx context.Context, projectFlockID uint, kandangIDs []uint) error
|
DeleteMany(ctx context.Context, projectFlockID uint, kandangIDs []uint) error
|
||||||
GetAll(ctx context.Context) ([]entity.ProjectFlockKandang, error)
|
GetAll(ctx context.Context) ([]entity.ProjectFlockKandang, error)
|
||||||
WithTx(tx *gorm.DB) ProjectFlockKandangRepository
|
WithTx(tx *gorm.DB) ProjectFlockKandangRepository
|
||||||
|
IdExists(ctx context.Context, id uint) (bool, error)
|
||||||
DB() *gorm.DB
|
DB() *gorm.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,6 +69,9 @@ func (r *projectFlockKandangRepositoryImpl) WithTx(tx *gorm.DB) ProjectFlockKand
|
|||||||
func (r *projectFlockKandangRepositoryImpl) DB() *gorm.DB {
|
func (r *projectFlockKandangRepositoryImpl) DB() *gorm.DB {
|
||||||
return r.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) {
|
func (r *projectFlockKandangRepositoryImpl) GetByID(ctx context.Context, id uint) (*entity.ProjectFlockKandang, error) {
|
||||||
record := new(entity.ProjectFlockKandang)
|
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) {
|
func (s *recordingService) getTotalChick(tx *gorm.DB, projectFlockKandangId uint) (int64, error) {
|
||||||
var population entity.ProjectFlockPopulation
|
// var population entity.ProjectFlockPopulation
|
||||||
err := tx.
|
// err := tx.
|
||||||
Where("project_flock_kandang_id = ?", projectFlockKandangId).
|
// Where("project_flock_kandang_id = ?", projectFlockKandangId).
|
||||||
Order("created_at DESC").
|
// Order("created_at DESC").
|
||||||
First(&population).Error
|
// First(&population).Error
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
// if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return 0, nil
|
// return 0, nil
|
||||||
}
|
// }
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return 0, err
|
// return 0, err
|
||||||
}
|
// }
|
||||||
return int64(math.Round(population.InitialQuantity)), nil
|
//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) {
|
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"
|
chickins "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins"
|
||||||
projectflocks "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks"
|
projectflocks "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks"
|
||||||
recordings "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings"
|
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
|
// MODULE IMPORTS
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -20,8 +21,9 @@ func RegisterRoutes(router fiber.Router, db *gorm.DB, validate *validator.Valida
|
|||||||
projectflocks.ProjectflockModule{},
|
projectflocks.ProjectflockModule{},
|
||||||
recordings.RecordingModule{},
|
recordings.RecordingModule{},
|
||||||
chickins.ChickinModule{},
|
chickins.ChickinModule{},
|
||||||
|
transferLayings.TransferLayingModule{},
|
||||||
// MODULE REGISTRY
|
// MODULE REGISTRY
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, m := range allModules {
|
for _, m := range allModules {
|
||||||
m.RegisterRoutes(group, db, validate)
|
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"
|
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
|
// WarehouseType
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
@@ -140,6 +158,20 @@ var ProjectFlockApprovalSteps = map[approvalutils.ApprovalStep]string{
|
|||||||
ProjectFlockStepAktif: "Aktif",
|
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
|
// Validators
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user