feat(BE): integrate FIFO service for stock adjustments and transfers

- Added FIFO service integration in the adjustments module to manage stockable and usable items for adjustments.
- Created a new repository for adjustment stocks to handle database operations.
- Enhanced the adjustment service to track stock adjustments using FIFO logic for both increase and decrease operations.
- Updated product warehouse DTOs and repositories to include project flock information.
- Implemented FIFO logic in the transfer module to manage stock transfers between warehouses.
- Added integration tests for FIFO operations in stock transfers, ensuring correct stock consumption and replenishment.
This commit is contained in:
aguhh18
2025-12-28 19:15:41 +07:00
parent a0d2c1c7dd
commit 812db3f79e
17 changed files with 950 additions and 104 deletions
@@ -24,11 +24,12 @@ type ProductWarehousNestedDTO struct {
type ProductWarehouseListDTO struct {
ProductWarehouseRelationDTO
Product *productDTO.ProductRelationDTO `json:"product,omitempty"`
Warehouse *WarehouseRelationDTO `json:"warehouse,omitempty"`
CreatedUser *UserRelationDTO `json:"created_user,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Product *productDTO.ProductRelationDTO `json:"product,omitempty"`
Warehouse *WarehouseRelationDTO `json:"warehouse,omitempty"`
ProjectFlockKandang *ProjectFlockKandangRelationDTO `json:"project_flock_kandang,omitempty"`
CreatedUser *UserRelationDTO `json:"created_user,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type UserRelationDTO struct {
@@ -71,6 +72,19 @@ type AreaRelationDTO struct {
Name string `json:"name"`
}
type ProjectFlockKandangRelationDTO struct {
Id uint `json:"id"`
ProjectFlockId uint `json:"project_flock_id"`
KandangId uint `json:"kandang_id"`
Period int `json:"period"`
ProjectFlock *ProjectFlockRelationDTO `json:"project_flock,omitempty"`
}
type ProjectFlockRelationDTO struct {
Id uint `json:"id"`
FlockName string `json:"flock_name"`
}
// === Mapper Functions ===
func ToProductWarehouseRelationDTO(e entity.ProductWarehouse) ProductWarehouseRelationDTO {
@@ -105,6 +119,12 @@ func ToProductWarehouseListDTO(e entity.ProductWarehouse) ProductWarehouseListDT
// Map Product relation jika ada
if e.Product.Id != 0 {
product := productDTO.ToProductRelationDTO(e.Product)
// Tambahkan flock name ke product name jika ada project flock
if e.ProjectFlockKandang != nil && e.ProjectFlockKandang.ProjectFlock.Id != 0 {
product.Name = product.Name + " (" + e.ProjectFlockKandang.ProjectFlock.FlockName + ")"
}
dto.Product = &product
}
@@ -139,6 +159,26 @@ func ToProductWarehouseListDTO(e entity.ProductWarehouse) ProductWarehouseListDT
dto.Warehouse = &warehouse
}
// Map ProjectFlockKandang relation jika ada
if e.ProjectFlockKandang != nil && e.ProjectFlockKandang.Id != 0 {
pfkDTO := &ProjectFlockKandangRelationDTO{
Id: e.ProjectFlockKandang.Id,
ProjectFlockId: e.ProjectFlockKandang.ProjectFlockId,
KandangId: e.ProjectFlockKandang.KandangId,
Period: e.ProjectFlockKandang.Period,
}
// Map ProjectFlock jika ada
if e.ProjectFlockKandang.ProjectFlock.Id != 0 {
pfkDTO.ProjectFlock = &ProjectFlockRelationDTO{
Id: e.ProjectFlockKandang.ProjectFlock.Id,
FlockName: e.ProjectFlockKandang.ProjectFlock.FlockName,
}
}
dto.ProjectFlockKandang = pfkDTO
}
// Map CreatedUser relation jika ada
// if e.CreatedUser.Id != 0 {
// user := UserRelationDTO{
@@ -81,9 +81,29 @@ func (r *ProductWarehouseRepositoryImpl) ProductWarehouseExistByProductAndWareho
func (r *ProductWarehouseRepositoryImpl) GetProductWarehouseByProductAndWarehouseID(ctx context.Context, productId, warehouseId uint) (*entity.ProductWarehouse, error) {
var productWarehouse entity.ProductWarehouse
if err := r.DB().WithContext(ctx).Where("product_id = ? AND warehouse_id = ?", productId, warehouseId).First(&productWarehouse).Error; err != nil {
err := r.DB().WithContext(ctx).
Where("product_id = ? AND warehouse_id = ? AND project_flock_kandang_id IS NOT NULL", productId, warehouseId).
Order("id DESC").
Preload("ProjectFlockKandang").
First(&productWarehouse).Error
if err == nil {
if productWarehouse.ProjectFlockKandang.ClosedAt == nil {
return &productWarehouse, nil
}
}
err = r.DB().WithContext(ctx).
Where("product_id = ? AND warehouse_id = ? AND project_flock_kandang_id IS NULL", productId, warehouseId).
First(&productWarehouse).Error
if err != nil {
return nil, err
}
return &productWarehouse, nil
}
@@ -244,6 +264,8 @@ func (r *ProductWarehouseRepositoryImpl) GetDetailByID(ctx context.Context, id u
Preload("Warehouse").
Preload("Warehouse.Area").
Preload("Warehouse.Location").
Preload("ProjectFlockKandang").
Preload("ProjectFlockKandang.ProjectFlock").
First(&productWarehouse, id).Error
if err != nil {
return nil, err
@@ -44,7 +44,8 @@ func (s productWarehouseService) withRelations(db *gorm.DB) *gorm.DB {
Preload("Warehouse.Location").
Preload("Warehouse.Area").
Preload("Warehouse.Kandang").
Preload("ProjectFlockKandang")
Preload("ProjectFlockKandang").
Preload("ProjectFlockKandang.ProjectFlock")
}
func (s productWarehouseService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.ProductWarehouse, int64, error) {