mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
Merge branch 'feat/BE/Sprint-5' of https://gitlab.com/mbugroup/lti-api into dev/teguh
This commit is contained in:
@@ -158,7 +158,6 @@ func (s *expenseService) CreateOne(c *fiber.Ctx, req *validation.Create) (*expen
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Periksa apakah nonstock terkait dengan supplier
|
|
||||||
supplierFound, err := s.NonstockRepo.IsNonstockAssociatedWithSupplier(c.Context(), nonstockId, req.SupplierID)
|
supplierFound, err := s.NonstockRepo.IsNonstockAssociatedWithSupplier(c.Context(), nonstockId, req.SupplierID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
|||||||
@@ -261,7 +261,7 @@ func (u *ProjectflockController) Approval(c *fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ProjectflockController) GetFlockPeriodSummary(c *fiber.Ctx) error {
|
func (u *ProjectflockController) GetPeriodSummary(c *fiber.Ctx) error {
|
||||||
param := c.Params("location_id")
|
param := c.Params("location_id")
|
||||||
|
|
||||||
id, err := strconv.Atoi(param)
|
id, err := strconv.Atoi(param)
|
||||||
@@ -269,7 +269,7 @@ func (u *ProjectflockController) GetFlockPeriodSummary(c *fiber.Ctx) error {
|
|||||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid location_id")
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid location_id")
|
||||||
}
|
}
|
||||||
|
|
||||||
summaries, err := u.ProjectflockService.GetFlockPeriodSummary(c, uint(id))
|
summaries, err := u.ProjectflockService.GetPeriodSummary(c, uint(id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,17 +11,24 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
const baseNameExpression = "LOWER(TRIM(regexp_replace(flock_name, '\\\\s+\\\\d+(\\\\s+\\\\d+)*$', '', 'g')))"
|
|
||||||
|
|
||||||
type ProjectflockRepository interface {
|
type ProjectflockRepository interface {
|
||||||
repository.BaseRepository[entity.ProjectFlock]
|
repository.BaseRepository[entity.ProjectFlock]
|
||||||
GetAllWithFilters(ctx context.Context, offset, limit int, params *validation.Query) ([]entity.ProjectFlock, int64, error)
|
GetAllWithFilters(ctx context.Context, offset, limit int, params *validation.Query) ([]entity.ProjectFlock, int64, error)
|
||||||
WithDefaultRelations() func(*gorm.DB) *gorm.DB
|
WithDefaultRelations() func(*gorm.DB) *gorm.DB
|
||||||
ExistsByFlockName(ctx context.Context, flockName string, excludeID *uint) (bool, error)
|
ExistsByFlockName(ctx context.Context, flockName string, excludeID *uint) (bool, error)
|
||||||
|
GetNextPeriodsForKandangs(ctx context.Context, kandangIDs []uint) (map[uint]int, error)
|
||||||
|
GetCurrentProjectPeriod(ctx context.Context, projectFlockID uint) (int, error)
|
||||||
|
GetKandangPeriodSummaryRows(ctx context.Context, locationID uint) ([]KandangPeriodRow, error)
|
||||||
AreaExists(ctx context.Context, id uint) (bool, error)
|
AreaExists(ctx context.Context, id uint) (bool, error)
|
||||||
FcrExists(ctx context.Context, id uint) (bool, error)
|
FcrExists(ctx context.Context, id uint) (bool, error)
|
||||||
LocationExists(ctx context.Context, id uint) (bool, error)
|
LocationExists(ctx context.Context, id uint) (bool, error)
|
||||||
}
|
}
|
||||||
|
type KandangPeriodRow struct {
|
||||||
|
Id uint
|
||||||
|
Name string
|
||||||
|
LatestPeriod int
|
||||||
|
}
|
||||||
|
|
||||||
type ProjectflockRepositoryImpl struct {
|
type ProjectflockRepositoryImpl struct {
|
||||||
*repository.BaseRepositoryImpl[entity.ProjectFlock]
|
*repository.BaseRepositoryImpl[entity.ProjectFlock]
|
||||||
@@ -35,19 +42,13 @@ func NewProjectflockRepository(db *gorm.DB) ProjectflockRepository {
|
|||||||
|
|
||||||
func (r *ProjectflockRepositoryImpl) GetAllWithFilters(ctx context.Context, offset, limit int, params *validation.Query) ([]entity.ProjectFlock, int64, error) {
|
func (r *ProjectflockRepositoryImpl) GetAllWithFilters(ctx context.Context, offset, limit int, params *validation.Query) ([]entity.ProjectFlock, int64, error) {
|
||||||
return r.GetAll(ctx, offset, limit, func(db *gorm.DB) *gorm.DB {
|
return r.GetAll(ctx, offset, limit, func(db *gorm.DB) *gorm.DB {
|
||||||
db = r.withDefaultRelations(db)
|
|
||||||
return r.applyQueryFilters(db, params)
|
return r.applyQueryFilters(db, params)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ProjectflockRepositoryImpl) WithDefaultRelations() func(*gorm.DB) *gorm.DB {
|
func (r *ProjectflockRepositoryImpl) WithDefaultRelations() func(*gorm.DB) *gorm.DB {
|
||||||
return func(db *gorm.DB) *gorm.DB {
|
return func(db *gorm.DB) *gorm.DB {
|
||||||
return r.withDefaultRelations(db)
|
return db.
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ProjectflockRepositoryImpl) withDefaultRelations(db *gorm.DB) *gorm.DB {
|
|
||||||
return db.
|
|
||||||
Preload("CreatedUser").
|
Preload("CreatedUser").
|
||||||
Preload("Area").
|
Preload("Area").
|
||||||
Preload("Fcr").
|
Preload("Fcr").
|
||||||
@@ -55,8 +56,10 @@ func (r *ProjectflockRepositoryImpl) withDefaultRelations(db *gorm.DB) *gorm.DB
|
|||||||
Preload("Kandangs").
|
Preload("Kandangs").
|
||||||
Preload("KandangHistory").
|
Preload("KandangHistory").
|
||||||
Preload("KandangHistory.Kandang")
|
Preload("KandangHistory.Kandang")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (r *ProjectflockRepositoryImpl) applyQueryFilters(db *gorm.DB, params *validation.Query) *gorm.DB {
|
func (r *ProjectflockRepositoryImpl) applyQueryFilters(db *gorm.DB, params *validation.Query) *gorm.DB {
|
||||||
if params == nil {
|
if params == nil {
|
||||||
return db
|
return db
|
||||||
@@ -163,6 +166,88 @@ func (r *ProjectflockRepositoryImpl) LocationExists(ctx context.Context, id uint
|
|||||||
return repository.Exists[entity.Location](ctx, r.DB(), id)
|
return repository.Exists[entity.Location](ctx, r.DB(), id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *ProjectflockRepositoryImpl) GetNextPeriodsForKandangs(ctx context.Context, kandangIDs []uint) (map[uint]int, error) {
|
||||||
|
result := make(map[uint]int)
|
||||||
|
if len(kandangIDs) == 0 {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
unique := uniqueUintSlice(kandangIDs)
|
||||||
|
for _, id := range unique {
|
||||||
|
result[id] = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
type periodRow struct {
|
||||||
|
KandangID uint
|
||||||
|
MaxPeriod int
|
||||||
|
}
|
||||||
|
|
||||||
|
var rows []periodRow
|
||||||
|
if err := r.DB().WithContext(ctx).
|
||||||
|
Table("project_flock_kandangs").
|
||||||
|
Select("kandang_id, COALESCE(MAX(period), 0) AS max_period").
|
||||||
|
Where("kandang_id IN ?", unique).
|
||||||
|
Group("kandang_id").
|
||||||
|
Scan(&rows).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, row := range rows {
|
||||||
|
if row.MaxPeriod > 0 {
|
||||||
|
result[row.KandangID] = row.MaxPeriod + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ProjectflockRepositoryImpl) GetCurrentProjectPeriod(ctx context.Context, projectFlockID uint) (int, error) {
|
||||||
|
if projectFlockID == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentPeriod int
|
||||||
|
if err := r.DB().WithContext(ctx).
|
||||||
|
Table("project_flock_kandangs").
|
||||||
|
Where("project_flock_id = ?", projectFlockID).
|
||||||
|
Select("COALESCE(MAX(period), 0)").
|
||||||
|
Scan(¤tPeriod).Error; err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentPeriod, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ProjectflockRepositoryImpl) GetKandangPeriodSummaryRows(ctx context.Context, locationID uint) ([]KandangPeriodRow, error) {
|
||||||
|
rows := make([]KandangPeriodRow, 0)
|
||||||
|
if err := r.DB().WithContext(ctx).
|
||||||
|
Table("kandangs AS k").
|
||||||
|
Select("k.id, k.name, COALESCE(MAX(pfk.period), 0) AS latest_period").
|
||||||
|
Joins("LEFT JOIN project_flock_kandangs AS pfk ON pfk.kandang_id = k.id").
|
||||||
|
Where("k.location_id = ?", locationID).
|
||||||
|
Where("k.deleted_at IS NULL").
|
||||||
|
Group("k.id, k.name").
|
||||||
|
Order("k.id ASC").
|
||||||
|
Scan(&rows).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows, 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
|
||||||
|
}
|
||||||
|
|
||||||
func (r *ProjectflockRepositoryImpl) buildOrderExpressions(sortBy, sortOrder string) []string {
|
func (r *ProjectflockRepositoryImpl) buildOrderExpressions(sortBy, sortOrder string) []string {
|
||||||
direction := "ASC"
|
direction := "ASC"
|
||||||
if strings.ToLower(sortOrder) == "desc" {
|
if strings.ToLower(sortOrder) == "desc" {
|
||||||
|
|||||||
@@ -20,9 +20,8 @@ func ProjectflockRoutes(v1 fiber.Router, u user.UserService, s projectflock.Proj
|
|||||||
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.Get("/kandangs/:project_flock_kandang_id/periods", ctrl.GetFlockPeriodSummary)
|
|
||||||
route.Get("/kandangs/lookup", ctrl.LookupProjectFlockKandang)
|
route.Get("/kandangs/lookup", ctrl.LookupProjectFlockKandang)
|
||||||
route.Post("/approvals", ctrl.Approval)
|
route.Post("/approvals", ctrl.Approval)
|
||||||
route.Get("/kandangs/:location_id/periods", ctrl.GetFlockPeriodSummary)
|
route.Get("/locations/:location_id/periods", ctrl.GetPeriodSummary)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ type ProjectflockService interface {
|
|||||||
GetAvailableDocQuantity(ctx *fiber.Ctx, kandangID uint) (float64, error)
|
GetAvailableDocQuantity(ctx *fiber.Ctx, kandangID uint) (float64, error)
|
||||||
DeleteOne(ctx *fiber.Ctx, id uint) error
|
DeleteOne(ctx *fiber.Ctx, id uint) error
|
||||||
GetProjectFlockKandangByProjectAndKandang(ctx *fiber.Ctx, projectFlockID uint, kandangID uint) (*entity.ProjectFlockKandang, float64, error)
|
GetProjectFlockKandangByProjectAndKandang(ctx *fiber.Ctx, projectFlockID uint, kandangID uint) (*entity.ProjectFlockKandang, float64, error)
|
||||||
GetFlockPeriodSummary(ctx *fiber.Ctx, locationID uint) ([]KandangPeriodSummary, error)
|
GetPeriodSummary(ctx *fiber.Ctx, locationID uint) ([]KandangPeriodSummary, error)
|
||||||
GetProjectPeriods(ctx *fiber.Ctx, projectIDs []uint) (map[uint]int, error)
|
GetProjectPeriods(ctx *fiber.Ctx, projectIDs []uint) (map[uint]int, error)
|
||||||
Approval(ctx *fiber.Ctx, req *validation.Approve) ([]entity.ProjectFlock, error)
|
Approval(ctx *fiber.Ctx, req *validation.Approve) ([]entity.ProjectFlock, error)
|
||||||
}
|
}
|
||||||
@@ -85,18 +85,6 @@ func NewProjectflockService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s projectflockService) withRelations(db *gorm.DB) *gorm.DB {
|
|
||||||
return db.
|
|
||||||
Preload("CreatedUser").
|
|
||||||
Preload("Flock").
|
|
||||||
Preload("Area").
|
|
||||||
Preload("Fcr").
|
|
||||||
Preload("Location").
|
|
||||||
Preload("Kandangs").
|
|
||||||
Preload("KandangHistory").
|
|
||||||
Preload("KandangHistory.Kandang")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s projectflockService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.ProjectFlock, int64, map[uint]*flockDTO.FlockRelationDTO, error) {
|
func (s projectflockService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.ProjectFlock, int64, map[uint]*flockDTO.FlockRelationDTO, error) {
|
||||||
if err := s.Validate.Struct(params); err != nil {
|
if err := s.Validate.Struct(params); err != nil {
|
||||||
return nil, 0, nil, err
|
return nil, 0, nil, err
|
||||||
@@ -124,9 +112,7 @@ func (s projectflockService) GetAll(c *fiber.Ctx, params *validation.Query) ([]e
|
|||||||
ids[i] = item.Id
|
ids[i] = item.Id
|
||||||
}
|
}
|
||||||
|
|
||||||
latestMap, err := s.ApprovalSvc.LatestByTargets(c.Context(), s.approvalWorkflow, ids, func(db *gorm.DB) *gorm.DB {
|
latestMap, err := s.ApprovalSvc.LatestByTargets(c.Context(), s.approvalWorkflow, ids, s.Repository.WithDefaultRelations())
|
||||||
return s.withRelations(db)
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.Warnf("Unable to load latest approvals for projectflocks: %+v", err)
|
s.Log.Warnf("Unable to load latest approvals for projectflocks: %+v", err)
|
||||||
} else if len(latestMap) > 0 {
|
} else if len(latestMap) > 0 {
|
||||||
@@ -170,9 +156,7 @@ func (s projectflockService) getOneEntityOnly(c *fiber.Ctx, id uint) (*entity.Pr
|
|||||||
}
|
}
|
||||||
|
|
||||||
if s.ApprovalSvc != nil {
|
if s.ApprovalSvc != nil {
|
||||||
approvals, err := s.ApprovalSvc.ListByTarget(c.Context(), s.approvalWorkflow, id, func(db *gorm.DB) *gorm.DB {
|
approvals, err := s.ApprovalSvc.ListByTarget(c.Context(), s.approvalWorkflow, id, s.Repository.WithDefaultRelations())
|
||||||
return s.withRelations(db)
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.Warnf("Unable to load approvals for projectflock %d: %+v", id, err)
|
s.Log.Warnf("Unable to load approvals for projectflock %d: %+v", id, err)
|
||||||
} else if len(approvals) > 0 {
|
} else if len(approvals) > 0 {
|
||||||
@@ -199,9 +183,7 @@ func (s projectflockService) GetOne(c *fiber.Ctx, id uint) (*entity.ProjectFlock
|
|||||||
}
|
}
|
||||||
|
|
||||||
if s.ApprovalSvc != nil {
|
if s.ApprovalSvc != nil {
|
||||||
approvals, err := s.ApprovalSvc.ListByTarget(c.Context(), s.approvalWorkflow, id, func(db *gorm.DB) *gorm.DB {
|
approvals, err := s.ApprovalSvc.ListByTarget(c.Context(), s.approvalWorkflow, id, s.Repository.WithDefaultRelations())
|
||||||
return s.withRelations(db)
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.Warnf("Unable to load approvals for projectflock %d: %+v", id, err)
|
s.Log.Warnf("Unable to load approvals for projectflock %d: %+v", id, err)
|
||||||
} else if len(approvals) > 0 {
|
} else if len(approvals) > 0 {
|
||||||
@@ -320,13 +302,12 @@ func (s *projectflockService) CreateOne(c *fiber.Ctx, req *validation.Create) (*
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute period based on location history (max period in that location + 1),
|
// Compute period per kandang so every kandang maintains its own cycle history.
|
||||||
// and store it on project_flock_kandangs only.
|
periods, err := projectRepo.GetNextPeriodsForKandangs(c.Context(), kandangIDs)
|
||||||
nextPeriod, err := s.nextLocationPeriod(c.Context(), dbTransaction, req.LocationId)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := s.attachKandangs(c.Context(), dbTransaction, createBody.Id, kandangIDs, nextPeriod); err != nil {
|
if err := s.attachKandangs(c.Context(), dbTransaction, createBody.Id, kandangIDs, periods); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -544,19 +525,24 @@ func (s projectflockService) UpdateOne(c *fiber.Ctx, req *validation.Update, id
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(toAttach) > 0 {
|
if len(toAttach) > 0 {
|
||||||
var currentPeriod int
|
currentPeriod, err := projectRepo.GetCurrentProjectPeriod(c.Context(), id)
|
||||||
if err := dbTransaction.WithContext(c.Context()).
|
if err != nil {
|
||||||
Table("project_flock_kandangs").
|
|
||||||
Where("project_flock_id = ?", id).
|
|
||||||
Select("COALESCE(MAX(period), 0)").
|
|
||||||
Scan(¤tPeriod).Error; err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if currentPeriod <= 0 {
|
|
||||||
currentPeriod = 1
|
periods := make(map[uint]int, len(toAttach))
|
||||||
|
if currentPeriod > 0 {
|
||||||
|
for _, kid := range toAttach {
|
||||||
|
periods[kid] = currentPeriod
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
periods, err = projectRepo.GetNextPeriodsForKandangs(c.Context(), toAttach)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.attachKandangs(c.Context(), dbTransaction, id, toAttach, currentPeriod); err != nil {
|
if err := s.attachKandangs(c.Context(), dbTransaction, id, toAttach, periods); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -832,32 +818,6 @@ func (s projectflockService) GetAvailableDocQuantity(ctx *fiber.Ctx, kandangID u
|
|||||||
return total, nil
|
return total, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// nextLocationPeriod computes the next period number for a given location
|
|
||||||
// based on the maximum period that has ever been used by any kandang in that location.
|
|
||||||
func (s projectflockService) nextLocationPeriod(ctx context.Context, tx *gorm.DB, locationID uint) (int, error) {
|
|
||||||
if locationID == 0 {
|
|
||||||
return 0, fiber.NewError(fiber.StatusBadRequest, "location_id is required to compute period")
|
|
||||||
}
|
|
||||||
|
|
||||||
db := s.Repository.DB()
|
|
||||||
if tx != nil {
|
|
||||||
db = tx
|
|
||||||
}
|
|
||||||
|
|
||||||
var maxPeriod int
|
|
||||||
if err := db.WithContext(ctx).
|
|
||||||
Table("project_flock_kandangs pfk").
|
|
||||||
Joins("JOIN kandangs k ON k.id = pfk.kandang_id").
|
|
||||||
Where("k.location_id = ?", locationID).
|
|
||||||
Select("COALESCE(MAX(pfk.period), 0)").
|
|
||||||
Scan(&maxPeriod).Error; err != nil {
|
|
||||||
s.Log.Errorf("Failed to compute max period for location %d: %+v", locationID, err)
|
|
||||||
return 0, fiber.NewError(fiber.StatusInternalServerError, "Failed to compute period for location")
|
|
||||||
}
|
|
||||||
|
|
||||||
return maxPeriod + 1, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s projectflockService) GetProjectPeriods(c *fiber.Ctx, projectIDs []uint) (map[uint]int, error) {
|
func (s projectflockService) GetProjectPeriods(c *fiber.Ctx, projectIDs []uint) (map[uint]int, error) {
|
||||||
if len(projectIDs) == 0 {
|
if len(projectIDs) == 0 {
|
||||||
return map[uint]int{}, nil
|
return map[uint]int{}, nil
|
||||||
@@ -865,7 +825,7 @@ func (s projectflockService) GetProjectPeriods(c *fiber.Ctx, projectIDs []uint)
|
|||||||
return s.pivotRepo().ProjectPeriodsByProjectIDs(c.Context(), projectIDs)
|
return s.pivotRepo().ProjectPeriodsByProjectIDs(c.Context(), projectIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s projectflockService) GetFlockPeriodSummary(c *fiber.Ctx, locationID uint) ([]KandangPeriodSummary, error) {
|
func (s projectflockService) GetPeriodSummary(c *fiber.Ctx, locationID uint) ([]KandangPeriodSummary, error) {
|
||||||
if locationID == 0 {
|
if locationID == 0 {
|
||||||
return nil, fiber.NewError(fiber.StatusBadRequest, "location_id is required")
|
return nil, fiber.NewError(fiber.StatusBadRequest, "location_id is required")
|
||||||
}
|
}
|
||||||
@@ -879,24 +839,8 @@ func (s projectflockService) GetFlockPeriodSummary(c *fiber.Ctx, locationID uint
|
|||||||
return nil, fiber.NewError(fiber.StatusNotFound, "Location not found")
|
return nil, fiber.NewError(fiber.StatusNotFound, "Location not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
type kandangPeriodRow struct {
|
rows, err := s.Repository.GetKandangPeriodSummaryRows(c.Context(), locationID)
|
||||||
Id uint
|
if err != nil {
|
||||||
Name string
|
|
||||||
LatestPeriod int
|
|
||||||
}
|
|
||||||
|
|
||||||
var rows []kandangPeriodRow
|
|
||||||
|
|
||||||
db := s.Repository.DB().WithContext(c.Context())
|
|
||||||
if err := db.
|
|
||||||
Table("kandangs AS k").
|
|
||||||
Select("k.id, k.name, COALESCE(MAX(pfk.period), 0) AS latest_period").
|
|
||||||
Joins("LEFT JOIN project_flock_kandangs AS pfk ON pfk.kandang_id = k.id").
|
|
||||||
Where("k.location_id = ?", locationID).
|
|
||||||
Where("k.deleted_at IS NULL").
|
|
||||||
Group("k.id, k.name").
|
|
||||||
Order("k.id ASC").
|
|
||||||
Scan(&rows).Error; err != nil {
|
|
||||||
s.Log.Errorf("Failed to fetch kandang period summary for location %d: %+v", locationID, err)
|
s.Log.Errorf("Failed to fetch kandang period summary for location %d: %+v", locationID, err)
|
||||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch kandang period summary")
|
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch kandang period summary")
|
||||||
}
|
}
|
||||||
@@ -991,7 +935,7 @@ func (s projectflockService) ensureFlockByName(ctx context.Context, actorID uint
|
|||||||
return newFlock, nil
|
return newFlock, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s projectflockService) attachKandangs(ctx context.Context, dbTransaction *gorm.DB, projectFlockID uint, kandangIDs []uint, period int) error {
|
func (s projectflockService) attachKandangs(ctx context.Context, dbTransaction *gorm.DB, projectFlockID uint, kandangIDs []uint, periods map[uint]int) error {
|
||||||
if len(kandangIDs) == 0 {
|
if len(kandangIDs) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -1026,6 +970,10 @@ func (s projectflockService) attachKandangs(ctx context.Context, dbTransaction *
|
|||||||
|
|
||||||
records := make([]*entity.ProjectFlockKandang, 0, len(toAttach))
|
records := make([]*entity.ProjectFlockKandang, 0, len(toAttach))
|
||||||
for _, id := range toAttach {
|
for _, id := range toAttach {
|
||||||
|
period := periods[id]
|
||||||
|
if period <= 0 {
|
||||||
|
period = 1
|
||||||
|
}
|
||||||
records = append(records, &entity.ProjectFlockKandang{
|
records = append(records, &entity.ProjectFlockKandang{
|
||||||
ProjectFlockId: projectFlockID,
|
ProjectFlockId: projectFlockID,
|
||||||
KandangId: id,
|
KandangId: id,
|
||||||
|
|||||||
@@ -168,9 +168,9 @@ func (s *purchaseService) CreateOne(c *fiber.Ctx, req *validation.CreatePurchase
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
user, ok := authmiddleware.AuthenticatedUser(c)
|
actorID, err := actorIDFromContext(c)
|
||||||
if !ok || user == nil || user.Id == 0 {
|
if err != nil {
|
||||||
return nil, fiber.NewError(fiber.StatusUnauthorized, "Please authenticate")
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := c.Context()
|
ctx := c.Context()
|
||||||
@@ -263,7 +263,7 @@ func (s *purchaseService) CreateOne(c *fiber.Ctx, req *validation.CreatePurchase
|
|||||||
DueDate: dueDate,
|
DueDate: dueDate,
|
||||||
GrandTotal: 0,
|
GrandTotal: 0,
|
||||||
Notes: req.Notes,
|
Notes: req.Notes,
|
||||||
CreatedBy: uint64(user.Id),
|
CreatedBy: uint64(actorID),
|
||||||
}
|
}
|
||||||
|
|
||||||
items := make([]*entity.PurchaseItem, 0, len(aggregated))
|
items := make([]*entity.PurchaseItem, 0, len(aggregated))
|
||||||
@@ -332,7 +332,10 @@ func (s *purchaseService) processStaffPurchaseApproval(c *fiber.Ctx, id uint64,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
actorID := uint(1) // TODO: replace with authenticated user id once available
|
actorID, err := actorIDFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
ctx := c.Context()
|
ctx := c.Context()
|
||||||
purchase, err := s.PurchaseRepo.GetByIDWithRelations(ctx, id)
|
purchase, err := s.PurchaseRepo.GetByIDWithRelations(ctx, id)
|
||||||
@@ -476,6 +479,11 @@ func (s *purchaseService) ApproveManagerPurchase(c *fiber.Ctx, id uint64, req *v
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
actorID, err := actorIDFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
ctx := c.Context()
|
ctx := c.Context()
|
||||||
|
|
||||||
purchase, err := s.PurchaseRepo.GetByIDWithRelations(ctx, id)
|
purchase, err := s.PurchaseRepo.GetByIDWithRelations(ctx, id)
|
||||||
@@ -496,7 +504,6 @@ func (s *purchaseService) ApproveManagerPurchase(c *fiber.Ctx, id uint64, req *v
|
|||||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Purchase must reach staff purchase step before manager approval")
|
return nil, fiber.NewError(fiber.StatusBadRequest, "Purchase must reach staff purchase step before manager approval")
|
||||||
}
|
}
|
||||||
|
|
||||||
actorID := uint(1)
|
|
||||||
action := entity.ApprovalActionApproved
|
action := entity.ApprovalActionApproved
|
||||||
now := time.Now().UTC()
|
now := time.Now().UTC()
|
||||||
hasExistingPO := purchase.PoNumber != nil && strings.TrimSpace(*purchase.PoNumber) != ""
|
hasExistingPO := purchase.PoNumber != nil && strings.TrimSpace(*purchase.PoNumber) != ""
|
||||||
@@ -577,6 +584,11 @@ func (s *purchaseService) ReceiveProducts(c *fiber.Ctx, id uint64, req *validati
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
actorID, err := actorIDFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
ctx := c.Context()
|
ctx := c.Context()
|
||||||
|
|
||||||
purchase, err := s.PurchaseRepo.GetByIDWithRelations(ctx, id)
|
purchase, err := s.PurchaseRepo.GetByIDWithRelations(ctx, id)
|
||||||
@@ -674,7 +686,6 @@ func (s *purchaseService) ReceiveProducts(c *fiber.Ctx, id uint64, req *validati
|
|||||||
|
|
||||||
receivingAction := entity.ApprovalActionApproved
|
receivingAction := entity.ApprovalActionApproved
|
||||||
completedAction := entity.ApprovalActionApproved
|
completedAction := entity.ApprovalActionApproved
|
||||||
actorID := uint(1)
|
|
||||||
|
|
||||||
approvalSvc := s.approvalServiceForDB(nil)
|
approvalSvc := s.approvalServiceForDB(nil)
|
||||||
if approvalSvc != nil {
|
if approvalSvc != nil {
|
||||||
@@ -1059,6 +1070,14 @@ func (s *purchaseService) notifyExpenseItemsDeleted(ctx context.Context, purchas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func actorIDFromContext(c *fiber.Ctx) (uint, error) {
|
||||||
|
user, ok := authmiddleware.AuthenticatedUser(c)
|
||||||
|
if !ok || user == nil || user.Id == 0 {
|
||||||
|
return 0, fiber.NewError(fiber.StatusUnauthorized, "Please authenticate")
|
||||||
|
}
|
||||||
|
return user.Id, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *purchaseService) buildStaffAdjustmentPayload(
|
func (s *purchaseService) buildStaffAdjustmentPayload(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
purchase *entity.Purchase,
|
purchase *entity.Purchase,
|
||||||
|
|||||||
Reference in New Issue
Block a user