Merge branch 'feat/BE/Sprint-5' of https://gitlab.com/mbugroup/lti-api into dev/teguh

This commit is contained in:
aguhh18
2025-11-21 11:05:33 +07:00
6 changed files with 151 additions and 101 deletions
@@ -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(&currentPeriod).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(&currentPeriod).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,