package service import ( "errors" entity "gitlab.com/mbugroup/lti-api.git/internal/entities" rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories" KandangRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/repositories" rWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories" repository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/repositories" rProjectFlockKandang "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories" validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/validations" rProjectFlock "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories" AuditLogRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/shared/repositories" "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 ChickinService interface { GetAll(ctx *fiber.Ctx, params *validation.Query) ([]entity.ProjectChickin, int64, error) GetOne(ctx *fiber.Ctx, id uint) (*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) DeleteOne(ctx *fiber.Ctx, id uint) error Approve(ctx *fiber.Ctx, id uint) error } type chickinService struct { Log *logrus.Logger Validate *validator.Validate Repository repository.ProjectChickinRepository KandangRepo KandangRepo.KandangRepository WarehouseRepo rWarehouse.WarehouseRepository ProductWarehouseRepo rProductWarehouse.ProductWarehouseRepository ProjectFlockRepo rProjectFlock.ProjectflockRepository AuditLogRepo AuditLogRepo.AuditLogRepository ProjectflockKandangRepo rProjectFlockKandang.ProjectFlockKandangRepository } func NewChickinService(repo repository.ProjectChickinRepository, kandangRepo KandangRepo.KandangRepository, warehouseRepo rWarehouse.WarehouseRepository, productWarehouseRepo rProductWarehouse.ProductWarehouseRepository, projectFlockRepo rProjectFlock.ProjectflockRepository, auditLogRepo AuditLogRepo.AuditLogRepository, projectflockkandangRepo rProjectFlockKandang.ProjectFlockKandangRepository, validate *validator.Validate) ChickinService { return &chickinService{ Log: utils.Log, Validate: validate, Repository: repo, KandangRepo: kandangRepo, WarehouseRepo: warehouseRepo, ProductWarehouseRepo: productWarehouseRepo, ProjectFlockRepo: projectFlockRepo, AuditLogRepo: auditLogRepo, ProjectflockKandangRepo: projectflockkandangRepo, } } func (s chickinService) withRelations(db *gorm.DB) *gorm.DB { return db. Preload("CreatedUser"). Preload("ProjectFlockKandang.Kandang"). Preload("ProjectFlockKandang.ProjectFlock"). Preload("ProjectFlockKandang.ProjectFlock.Flock"). Preload("ProjectFlockKandang.ProjectFlock.ProductCategory"). Preload("ProjectFlockKandang.ProjectFlock.Area"). Preload("ProjectFlockKandang.ProjectFlock.Fcr"). Preload("ProjectFlockKandang.ProjectFlock.Location") } func (s chickinService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.ProjectChickin, int64, error) { if err := s.Validate.Struct(params); err != nil { return nil, 0, err } offset := (params.Page - 1) * params.Limit chickins, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB { db = s.withRelations(db) if params.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 chickins: %+v", err) return nil, 0, err } return chickins, total, nil } func (s chickinService) GetOne(c *fiber.Ctx, id uint) (*entity.ProjectChickin, error) { chickin, err := s.Repository.GetByID(c.Context(), id, s.withRelations) if errors.Is(err, gorm.ErrRecordNotFound) { return nil, fiber.NewError(fiber.StatusNotFound, "Chickin not found") } if err != nil { s.Log.Errorf("Failed get chickin by id: %+v", err) return nil, err } return chickin, nil } func (s *chickinService) CreateOne(c *fiber.Ctx, req *validation.Create) (*entity.ProjectChickin, error) { if err := s.Validate.Struct(req); err != nil { return nil, err } // ambil salah satu kandang dari project_floc_id dari kandang repository projectflockkandang, err := s.ProjectflockKandangRepo.GetByID(c.Context(), 1) if err != nil { s.Log.Errorf("Failed to get projectflock kandang: %+v", err) return nil, err } // ambil warehouse dari kandangid warehouse, err := s.WarehouseRepo.GetByKandangID(c.Context(), projectflockkandang.KandangId) if err != nil { s.Log.Errorf("Failed to get warehouse: %+v", err) return nil, err } // getprojectflock id with relation projectFlock, err := s.ProjectFlockRepo.GetByID( c.Context(), projectflockkandang.ProjectFlockId, func(db *gorm.DB) *gorm.DB { return db.Preload("ProductCategory") }, ) if err != nil { s.Log.Errorf("Failed to get project flock: %+v", err) return nil, fiber.NewError(fiber.StatusNotFound, "Project Flock not found") } // ambil quantity var productWarehouse entity.ProductWarehouse err = s.ProductWarehouseRepo.DB().WithContext(c.Context()). 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 = ?", projectFlock.ProductCategory.Code, warehouse.Id). Order("created_at DESC"). First(&productWarehouse).Error if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, 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 nil, err } if productWarehouse.Quantity < 1 { return nil, fiber.NewError(fiber.StatusBadRequest, "Insufficient product quantity in warehouse") } // masukan ke chic in chickinDate, err := utils.ParseDateString(req.ChickInDate) if err != nil { s.Log.Errorf("Failed to parse chickin date: %+v", err) return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid ChickInDate format") } newChickin := &entity.ProjectChickin{ ProjectFlocKandangId: projectflockkandang.ProjectFlockId, ChickInDate: chickinDate, Quantity: productWarehouse.Quantity, Note: "", CreatedBy: 1, //todo: ganti dengan } err = s.Repository.CreateOne(c.Context(), newChickin, nil) if err != nil { s.Log.Errorf("Failed to create chickin: %+v", err) return nil, err } // Kurangi quantity di product warehouse updatedQuantity := productWarehouse.Quantity - newChickin.Quantity if updatedQuantity < 0 { updatedQuantity = 0 } err = s.ProductWarehouseRepo.PatchOne(c.Context(), productWarehouse.Id, map[string]any{ "quantity": updatedQuantity, }, nil) if err != nil { s.Log.Errorf("Failed to update product warehouse quantity: %+v", err) return nil, err } // masukan check apakah stock availability ada, jika ada update, jika tidak buat baru stockAvailability := &entity.StockAvailability{ EntityType: entity.EntityTypeProjectFlockKandang, ReservedQuantity: productWarehouse.Quantity, EntityId: projectflockkandang.Id, ProductId: productWarehouse.ProductId, } var existingStockAvailability entity.StockAvailability err = s.ProductWarehouseRepo.DB().WithContext(c.Context()). Where("entity_type = ? AND entity_id = ? AND product_id = ?", stockAvailability.EntityType, stockAvailability.EntityId, stockAvailability.ProductId). First(&existingStockAvailability).Error if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { // buat baru stockAvailability.ReservedQuantity = newChickin.Quantity stockAvailability.Quantity = 0 err = s.ProductWarehouseRepo.DB().WithContext(c.Context()).Create(stockAvailability).Error if err != nil { s.Log.Errorf("Failed to create stock availability: %+v", err) return nil, err } } else { s.Log.Errorf("Failed to get stock availability: %+v", err) return nil, err } } else { // update existing newQuantity := existingStockAvailability.ReservedQuantity + newChickin.Quantity err = s.ProductWarehouseRepo.DB().WithContext(c.Context()). Model(&existingStockAvailability). Update("reserved_quantity", newQuantity).Error if err != nil { s.Log.Errorf("Failed to update stock availability: %+v", err) return nil, err } } return s.GetOne(c, newChickin.Id) } func (s chickinService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uint) (*entity.ProjectChickin, error) { if err := s.Validate.Struct(req); err != nil { return nil, err } updateBody := make(map[string]any) if req.ChickInDate != "" { updateBody["chick_in_date"] = req.ChickInDate } 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, "Chickin not found") } s.Log.Errorf("Failed to update chickin: %+v", err) return nil, err } return s.GetOne(c, id) } func (s chickinService) 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, "Chickin not found") } s.Log.Errorf("Failed to delete chickin: %+v", err) return err } return nil } func (s *chickinService) Approve(c *fiber.Ctx, id uint) error { chickin, err := s.Repository.GetByID( c.Context(), id, nil, ) if errors.Is(err, gorm.ErrRecordNotFound) { return fiber.NewError(fiber.StatusNotFound, "Chickin not found") } if err != nil { s.Log.Errorf("Failed get chickin by id: %+v", err) return err } //pindahkan stock dari reserved ke actual stock // get stock avaibility untuk di update var stockAvailability entity.StockAvailability err = s.ProductWarehouseRepo.DB().WithContext(c.Context()). Where("entity_type = ? AND entity_id = ? ", entity.EntityTypeProjectFlockKandang, chickin.ProjectFlocKandangId). First(&stockAvailability).Error if err != nil { s.Log.Errorf("Failed to get stock availability: %+v", err) return err } newReservedQuantity := stockAvailability.ReservedQuantity - chickin.Quantity if newReservedQuantity < 0 { newReservedQuantity = 0 } return nil }