mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 05:21:57 +00:00
[FIX/BE-US] feat adjustment location and area
This commit is contained in:
@@ -82,6 +82,18 @@ func ResolveLocationScope(c *fiber.Ctx, db *gorm.DB) (ScopeFilter, error) {
|
||||
return ScopeFilter{IDs: locationIDs, Restrict: true}, nil
|
||||
}
|
||||
|
||||
func ResolveLocationAreaScopes(c *fiber.Ctx, db *gorm.DB) (ScopeFilter, ScopeFilter, error) {
|
||||
locationScope, err := ResolveLocationScope(c, db)
|
||||
if err != nil {
|
||||
return ScopeFilter{}, ScopeFilter{}, err
|
||||
}
|
||||
areaScope, err := ResolveAreaScope(c, db)
|
||||
if err != nil {
|
||||
return ScopeFilter{}, ScopeFilter{}, err
|
||||
}
|
||||
return locationScope, areaScope, nil
|
||||
}
|
||||
|
||||
func collectRoleScope(c *fiber.Ctx) (roleScope, error) {
|
||||
ctx, ok := AuthDetails(c)
|
||||
if !ok || ctx == nil {
|
||||
@@ -212,6 +224,31 @@ func ApplyAreaScope(c *fiber.Ctx, db *gorm.DB, column string) (*gorm.DB, error)
|
||||
return ApplyScopeFilter(db, scope, column), nil
|
||||
}
|
||||
|
||||
func ApplyLocationAreaScope(c *fiber.Ctx, db *gorm.DB, locationColumn, areaColumn string) (*gorm.DB, error) {
|
||||
scopeDB := db
|
||||
if db != nil {
|
||||
scopeDB = db.Session(&gorm.Session{NewDB: true})
|
||||
}
|
||||
|
||||
if locationColumn != "" {
|
||||
locationScope, err := ResolveLocationScope(c, scopeDB)
|
||||
if err != nil {
|
||||
return db, err
|
||||
}
|
||||
db = ApplyScopeFilter(db, locationScope, locationColumn)
|
||||
}
|
||||
|
||||
if areaColumn != "" {
|
||||
areaScope, err := ResolveAreaScope(c, scopeDB)
|
||||
if err != nil {
|
||||
return db, err
|
||||
}
|
||||
db = ApplyScopeFilter(db, areaScope, areaColumn)
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func EnsureWarehouseAccess(c *fiber.Ctx, db *gorm.DB, warehouseID uint) error {
|
||||
if warehouseID == 0 {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid warehouse id")
|
||||
@@ -486,7 +523,7 @@ func EnsureUniformityAccess(c *fiber.Ctx, db *gorm.DB, uniformityID uint) error
|
||||
|
||||
var count int64
|
||||
q := db.WithContext(c.Context()).
|
||||
Table("project_flock_kandang_uniformities u").
|
||||
Table("project_flock_kandang_uniformity u").
|
||||
Joins("JOIN project_flock_kandangs pfk ON pfk.id = u.project_flock_kandang_id").
|
||||
Joins("JOIN project_flocks pf ON pf.id = pfk.project_flock_id").
|
||||
Where("u.id = ?", uniformityID)
|
||||
|
||||
@@ -99,6 +99,10 @@ func (s closingService) GetAll(c *fiber.Ctx, params *validation.Query) ([]dto.Cl
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
scope, err := m.ResolveLocationScope(c, s.Repository.DB())
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
offset := (params.Page - 1) * params.Limit
|
||||
statusFilter := ""
|
||||
@@ -113,6 +117,12 @@ func (s closingService) GetAll(c *fiber.Ctx, params *validation.Query) ([]dto.Cl
|
||||
|
||||
closings, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
|
||||
db = s.withClosingRelations(db)
|
||||
if scope.Restrict {
|
||||
if len(scope.IDs) == 0 {
|
||||
return db.Where("1 = 0")
|
||||
}
|
||||
db = m.ApplyScopeFilter(db, scope, "project_flocks.location_id")
|
||||
}
|
||||
if params.LocationID != nil {
|
||||
db = db.Where("location_id = ?", *params.LocationID)
|
||||
}
|
||||
|
||||
@@ -134,6 +134,87 @@ func (s dailyChecklistService) withRelations(db *gorm.DB) *gorm.DB {
|
||||
return db.Preload("Kandang")
|
||||
}
|
||||
|
||||
func (s dailyChecklistService) ensureChecklistAccess(c *fiber.Ctx, checklistID uint) error {
|
||||
if checklistID == 0 {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid checklist id")
|
||||
}
|
||||
|
||||
db := s.Repository.DB().WithContext(c.Context()).
|
||||
Table("daily_checklists dc").
|
||||
Joins("JOIN kandangs k ON k.id = dc.kandang_id").
|
||||
Joins("JOIN locations loc ON loc.id = k.location_id").
|
||||
Joins("JOIN areas a ON a.id = loc.area_id").
|
||||
Where("dc.id = ?", checklistID)
|
||||
|
||||
scopedDB, err := m.ApplyLocationAreaScope(c, db, "loc.id", "a.id")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var count int64
|
||||
if err := scopedDB.Count(&count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if count == 0 {
|
||||
return fiber.NewError(fiber.StatusNotFound, "DailyChecklist not found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s dailyChecklistService) ensureKandangAccess(c *fiber.Ctx, kandangID uint) error {
|
||||
if kandangID == 0 {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid kandang id")
|
||||
}
|
||||
|
||||
db := s.Repository.DB().WithContext(c.Context()).
|
||||
Table("kandangs k").
|
||||
Joins("JOIN locations loc ON loc.id = k.location_id").
|
||||
Joins("JOIN areas a ON a.id = loc.area_id").
|
||||
Where("k.id = ?", kandangID)
|
||||
|
||||
scopedDB, err := m.ApplyLocationAreaScope(c, db, "loc.id", "a.id")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var count int64
|
||||
if err := scopedDB.Count(&count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if count == 0 {
|
||||
return fiber.NewError(fiber.StatusNotFound, "Kandang not found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s dailyChecklistService) ensureTaskAccess(c *fiber.Ctx, taskID uint) error {
|
||||
if taskID == 0 {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid task id")
|
||||
}
|
||||
|
||||
db := s.Repository.DB().WithContext(c.Context()).
|
||||
Table("daily_checklist_activity_tasks t").
|
||||
Joins("JOIN daily_checklists dc ON dc.id = t.checklist_id").
|
||||
Joins("JOIN kandangs k ON k.id = dc.kandang_id").
|
||||
Joins("JOIN locations loc ON loc.id = k.location_id").
|
||||
Joins("JOIN areas a ON a.id = loc.area_id").
|
||||
Where("t.id = ?", taskID)
|
||||
|
||||
scopedDB, err := m.ApplyLocationAreaScope(c, db, "loc.id", "a.id")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var count int64
|
||||
if err := scopedDB.Count(&count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if count == 0 {
|
||||
return fiber.NewError(fiber.StatusNotFound, "Task not found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s dailyChecklistService) GetAll(c *fiber.Ctx, params *validation.Query) ([]DailyChecklistListItem, int64, error) {
|
||||
if err := s.Validate.Struct(params); err != nil {
|
||||
return nil, 0, err
|
||||
@@ -143,7 +224,15 @@ func (s dailyChecklistService) GetAll(c *fiber.Ctx, params *validation.Query) ([
|
||||
|
||||
db := s.Repository.DB().WithContext(c.Context()).
|
||||
Table("daily_checklists dc").
|
||||
Joins("JOIN kandangs k ON k.id = dc.kandang_id")
|
||||
Joins("JOIN kandangs k ON k.id = dc.kandang_id").
|
||||
Joins("JOIN locations loc ON loc.id = k.location_id").
|
||||
Joins("JOIN areas a ON a.id = loc.area_id")
|
||||
|
||||
var scopeErr error
|
||||
db, scopeErr = m.ApplyLocationAreaScope(c, db, "loc.id", "a.id")
|
||||
if scopeErr != nil {
|
||||
return nil, 0, scopeErr
|
||||
}
|
||||
|
||||
if params.DateFrom != "" {
|
||||
dateFrom, err := time.Parse("2006-01-02", params.DateFrom)
|
||||
@@ -294,6 +383,9 @@ func (s dailyChecklistService) GetAll(c *fiber.Ctx, params *validation.Query) ([
|
||||
}
|
||||
|
||||
func (s dailyChecklistService) GetOne(c *fiber.Ctx, id uint) (*entity.DailyChecklist, error) {
|
||||
if err := s.ensureChecklistAccess(c, id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dailyChecklist, err := s.Repository.GetByID(c.Context(), id, s.withRelations)
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "DailyChecklist not found")
|
||||
@@ -399,6 +491,9 @@ func (s *dailyChecklistService) CreateOne(c *fiber.Ctx, req *validation.Create)
|
||||
if err := s.Validate.Struct(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.ensureKandangAccess(c, req.KandangId); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
date, err := time.Parse("2006-01-02", req.Date)
|
||||
if err != nil {
|
||||
@@ -431,6 +526,9 @@ func (s dailyChecklistService) UpdateOne(c *fiber.Ctx, req *validation.Update, i
|
||||
if err := s.Validate.Struct(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.ensureChecklistAccess(c, id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deletedIDs := make([]uint, 0)
|
||||
if req.DeletedDocumentIDs != nil {
|
||||
@@ -502,6 +600,9 @@ func (s dailyChecklistService) UpdateOne(c *fiber.Ctx, req *validation.Update, i
|
||||
}
|
||||
|
||||
func (s dailyChecklistService) DeleteOne(c *fiber.Ctx, id uint) error {
|
||||
if err := s.ensureChecklistAccess(c, id); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.Repository.DeleteOne(c.Context(), id); err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return fiber.NewError(fiber.StatusNotFound, "DailyChecklist not found")
|
||||
@@ -516,6 +617,9 @@ func (s dailyChecklistService) AssignPhases(c *fiber.Ctx, id uint, req *validati
|
||||
if err := s.Validate.Struct(req); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.ensureChecklistAccess(c, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := s.Repository.GetByID(c.Context(), id, nil); err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
@@ -597,6 +701,9 @@ func (s dailyChecklistService) AssignPhases(c *fiber.Ctx, id uint, req *validati
|
||||
}
|
||||
|
||||
func (s dailyChecklistService) RemoveAssignment(c *fiber.Ctx, id uint, employeeID uint) error {
|
||||
if err := s.ensureChecklistAccess(c, id); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := s.Repository.GetByID(c.Context(), id, nil); err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return fiber.NewError(fiber.StatusNotFound, "DailyChecklist not found")
|
||||
@@ -634,6 +741,9 @@ func (s dailyChecklistService) GetTasks(c *fiber.Ctx, checklistID uint) ([]entit
|
||||
if checklistID == 0 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "checklist_id is required")
|
||||
}
|
||||
if err := s.ensureChecklistAccess(c, checklistID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := s.Repository.GetByID(c.Context(), checklistID, nil); err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
@@ -658,6 +768,9 @@ func (s dailyChecklistService) GetChecklistPhaseIDs(c *fiber.Ctx, checklistID ui
|
||||
if checklistID == 0 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "checklist_id is required")
|
||||
}
|
||||
if err := s.ensureChecklistAccess(c, checklistID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := s.Repository.GetByID(c.Context(), checklistID, nil); err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
@@ -687,6 +800,9 @@ func (s dailyChecklistService) UpdateAssignment(c *fiber.Ctx, req *validation.Up
|
||||
if err := s.Validate.Struct(req); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.ensureTaskAccess(c, req.TaskID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
task := new(entity.DailyChecklistActivityTask)
|
||||
if err := s.Repository.DB().WithContext(c.Context()).First(task, req.TaskID).Error; err != nil {
|
||||
@@ -808,6 +924,9 @@ func (s dailyChecklistService) AssignTasks(c *fiber.Ctx, id uint, req *validatio
|
||||
if err := s.Validate.Struct(req); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.ensureChecklistAccess(c, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
employeeIDs, err := parseIDs(req.EmployeeIDs)
|
||||
if err != nil {
|
||||
@@ -900,8 +1019,16 @@ func (s dailyChecklistService) GetSummary(c *fiber.Ctx, params *validation.Summa
|
||||
Joins("JOIN daily_checklists d ON d.id = t.checklist_id").
|
||||
Joins("JOIN kandangs k ON k.id = d.kandang_id").
|
||||
Joins("JOIN employees e ON e.id = a.employee_id").
|
||||
Joins("JOIN locations loc ON loc.id = k.location_id").
|
||||
Joins("JOIN areas ar ON ar.id = loc.area_id").
|
||||
Where("d.date BETWEEN ? AND ? AND d.status = ?", dateFrom, dateTo, "APPROVED")
|
||||
|
||||
var scopeErr error
|
||||
db, scopeErr = m.ApplyLocationAreaScope(c, db, "loc.id", "ar.id")
|
||||
if scopeErr != nil {
|
||||
return nil, scopeErr
|
||||
}
|
||||
|
||||
if params.Category != "" {
|
||||
db = db.Where("d.category = ?", params.Category)
|
||||
}
|
||||
@@ -946,7 +1073,11 @@ func (s dailyChecklistService) GetReport(c *fiber.Ctx, params *validation.Report
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
scope, err := m.ResolveLocationScope(c, s.Repository.DB())
|
||||
locationScope, err := m.ResolveLocationScope(c, s.Repository.DB())
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
areaScope, err := m.ResolveAreaScope(c, s.Repository.DB())
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -967,7 +1098,8 @@ func (s dailyChecklistService) GetReport(c *fiber.Ctx, params *validation.Report
|
||||
Where("EXTRACT(YEAR FROM dc.date) = ?", params.Year).
|
||||
Where("dc.status = ?", "APPROVED")
|
||||
|
||||
db = m.ApplyScopeFilter(db, scope, "loc.id")
|
||||
db = m.ApplyScopeFilter(db, locationScope, "loc.id")
|
||||
db = m.ApplyScopeFilter(db, areaScope, "a.id")
|
||||
|
||||
if params.AreaID != nil {
|
||||
db = db.Where("a.id = ?", *params.AreaID)
|
||||
|
||||
@@ -74,6 +74,10 @@ func (s *adjustmentService) withRelations(db *gorm.DB) *gorm.DB {
|
||||
}
|
||||
|
||||
func (s *adjustmentService) GetOne(c *fiber.Ctx, id uint) (*entity.AdjustmentStock, error) {
|
||||
if err := m.EnsureStockLogAccess(c, s.StockLogsRepository.DB(), id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
adjustmentStock, err := s.AdjustmentStockRepository.GetByStockLogID(c.Context(), id)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
@@ -95,6 +99,9 @@ func (s *adjustmentService) Adjustment(c *fiber.Ctx, req *validation.Create) (*e
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := m.EnsureWarehouseAccess(c, s.WarehouseRepo.DB(), uint(req.WarehouseID)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := common.EnsureRelations(c.Context(),
|
||||
common.RelationCheck{Name: "Product", ID: &req.ProductID, Exists: s.ProductRepo.IdExists},
|
||||
common.RelationCheck{Name: "Warehouse", ID: &req.WarehouseID, Exists: s.WarehouseRepo.IdExists},
|
||||
@@ -304,6 +311,19 @@ func (s *adjustmentService) AdjustmentHistory(c *fiber.Ctx, query *validation.Qu
|
||||
Preload("ProductWarehouse.Product").
|
||||
Preload("ProductWarehouse.Warehouse")
|
||||
|
||||
scope, scopeErr := m.ResolveLocationScope(c, s.AdjustmentStockRepository.DB())
|
||||
if scopeErr != nil {
|
||||
return nil, 0, scopeErr
|
||||
}
|
||||
if scope.Restrict {
|
||||
if len(scope.IDs) == 0 {
|
||||
return []*entity.AdjustmentStock{}, 0, nil
|
||||
}
|
||||
q = q.Joins("JOIN product_warehouses pw_scope ON pw_scope.id = adjustment_stocks.product_warehouse_id").
|
||||
Joins("JOIN warehouses w_scope ON w_scope.id = pw_scope.warehouse_id")
|
||||
q = m.ApplyScopeFilter(q, scope, "w_scope.location_id")
|
||||
}
|
||||
|
||||
if query.ProductID > 0 {
|
||||
q = q.Joins("JOIN stock_logs ON stock_logs.id = adjustment_stocks.stock_log_id").
|
||||
Joins("JOIN product_warehouses ON product_warehouses.id = stock_logs.product_warehouse_id").
|
||||
|
||||
@@ -37,19 +37,49 @@ func NewProductStockService(
|
||||
}
|
||||
}
|
||||
|
||||
func (s productStockService) withRelations(db *gorm.DB) *gorm.DB {
|
||||
func (s productStockService) withRelations(db *gorm.DB, locationScope, areaScope m.ScopeFilter) *gorm.DB {
|
||||
warehouseScope := func(db *gorm.DB) *gorm.DB {
|
||||
if locationScope.Restrict {
|
||||
db = db.Where("warehouses.location_id IN ?", locationScope.IDs)
|
||||
}
|
||||
if areaScope.Restrict {
|
||||
db = db.Where("warehouses.area_id IN ?", areaScope.IDs)
|
||||
}
|
||||
return db
|
||||
}
|
||||
productWarehouseScope := func(db *gorm.DB) *gorm.DB {
|
||||
db = db.Joins("JOIN warehouses w ON w.id = product_warehouses.warehouse_id")
|
||||
if locationScope.Restrict {
|
||||
db = db.Where("w.location_id IN ?", locationScope.IDs)
|
||||
}
|
||||
if areaScope.Restrict {
|
||||
db = db.Where("w.area_id IN ?", areaScope.IDs)
|
||||
}
|
||||
return db
|
||||
}
|
||||
stockLogScope := func(db *gorm.DB) *gorm.DB {
|
||||
db = db.
|
||||
Joins("JOIN product_warehouses pw ON pw.id = stock_logs.product_warehouse_id").
|
||||
Joins("JOIN warehouses w ON w.id = pw.warehouse_id")
|
||||
if locationScope.Restrict {
|
||||
db = db.Where("w.location_id IN ?", locationScope.IDs)
|
||||
}
|
||||
if areaScope.Restrict {
|
||||
db = db.Where("w.area_id IN ?", areaScope.IDs)
|
||||
}
|
||||
return db.Order("stock_logs.created_at ASC")
|
||||
}
|
||||
|
||||
return db.
|
||||
Preload("CreatedUser").
|
||||
Preload("Uom").
|
||||
Preload("ProductCategory").
|
||||
Preload("Flags").
|
||||
Preload("ProductWarehouses").
|
||||
Preload("ProductWarehouses.Warehouse").
|
||||
Preload("ProductWarehouses", productWarehouseScope).
|
||||
Preload("ProductWarehouses.Warehouse", warehouseScope).
|
||||
Preload("ProductWarehouses.Warehouse.Location").
|
||||
Preload("ProductWarehouses.Warehouse.Location.Area").
|
||||
Preload("ProductWarehouses.StockLogs", func(db *gorm.DB) *gorm.DB {
|
||||
return db.Order("created_at ASC")
|
||||
}).
|
||||
Preload("ProductWarehouses.StockLogs", stockLogScope).
|
||||
Preload("ProductWarehouses.StockLogs.CreatedUser").
|
||||
Preload("ProductSuppliers").
|
||||
Preload("ProductSuppliers.Supplier", func(db *gorm.DB) *gorm.DB {
|
||||
@@ -62,7 +92,7 @@ func (s productStockService) GetAll(c *fiber.Ctx, params *validation.Query) ([]e
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
scope, err := m.ResolveLocationScope(c, s.ProductRepository.DB())
|
||||
locationScope, areaScope, err := m.ResolveLocationAreaScopes(c, s.ProductRepository.DB())
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -70,8 +100,8 @@ func (s productStockService) GetAll(c *fiber.Ctx, params *validation.Query) ([]e
|
||||
offset := (params.Page - 1) * params.Limit
|
||||
|
||||
productStocks, total, err := s.ProductRepository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
|
||||
if scope.Restrict {
|
||||
if len(scope.IDs) == 0 {
|
||||
if locationScope.Restrict || areaScope.Restrict {
|
||||
if (locationScope.Restrict && len(locationScope.IDs) == 0) || (areaScope.Restrict && len(areaScope.IDs) == 0) {
|
||||
return db.Where("1 = 0")
|
||||
}
|
||||
db = db.Where(`EXISTS (
|
||||
@@ -80,8 +110,12 @@ func (s productStockService) GetAll(c *fiber.Ctx, params *validation.Query) ([]e
|
||||
JOIN warehouses w ON w.id = pw.warehouse_id
|
||||
WHERE pw.product_id = products.id
|
||||
AND pw.qty > 0
|
||||
AND w.location_id IN ?
|
||||
)`, scope.IDs)
|
||||
AND (? OR w.location_id IN ?)
|
||||
AND (? OR w.area_id IN ?)
|
||||
)`,
|
||||
!locationScope.Restrict, locationScope.IDs,
|
||||
!areaScope.Restrict, areaScope.IDs,
|
||||
)
|
||||
} else {
|
||||
db = db.Where(`EXISTS (
|
||||
SELECT 1
|
||||
@@ -91,7 +125,7 @@ func (s productStockService) GetAll(c *fiber.Ctx, params *validation.Query) ([]e
|
||||
)`)
|
||||
}
|
||||
|
||||
db = s.withRelations(db)
|
||||
db = s.withRelations(db, locationScope, areaScope)
|
||||
if params.Search != "" {
|
||||
db = db.Where("products.name ILIKE ?", "%"+params.Search+"%")
|
||||
}
|
||||
@@ -106,13 +140,13 @@ func (s productStockService) GetAll(c *fiber.Ctx, params *validation.Query) ([]e
|
||||
}
|
||||
|
||||
func (s productStockService) GetOne(c *fiber.Ctx, id uint) (*entity.Product, error) {
|
||||
scope, err := m.ResolveLocationScope(c, s.ProductRepository.DB())
|
||||
locationScope, areaScope, err := m.ResolveLocationAreaScopes(c, s.ProductRepository.DB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if scope.Restrict {
|
||||
if len(scope.IDs) == 0 {
|
||||
if locationScope.Restrict || areaScope.Restrict {
|
||||
if (locationScope.Restrict && len(locationScope.IDs) == 0) || (areaScope.Restrict && len(areaScope.IDs) == 0) {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Product not found")
|
||||
}
|
||||
var count int64
|
||||
@@ -121,7 +155,8 @@ func (s productStockService) GetOne(c *fiber.Ctx, id uint) (*entity.Product, err
|
||||
Joins("JOIN warehouses w ON w.id = pw.warehouse_id").
|
||||
Where("pw.product_id = ?", id).
|
||||
Where("pw.qty > 0").
|
||||
Where("w.location_id IN ?", scope.IDs).
|
||||
Where("(? OR w.location_id IN ?)", !locationScope.Restrict, locationScope.IDs).
|
||||
Where("(? OR w.area_id IN ?)", !areaScope.Restrict, areaScope.IDs).
|
||||
Count(&count).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -130,7 +165,9 @@ func (s productStockService) GetOne(c *fiber.Ctx, id uint) (*entity.Product, err
|
||||
}
|
||||
}
|
||||
|
||||
product, err := s.ProductRepository.GetByID(c.Context(), id, s.withRelations)
|
||||
product, err := s.ProductRepository.GetByID(c.Context(), id, func(db *gorm.DB) *gorm.DB {
|
||||
return s.withRelations(db, locationScope, areaScope)
|
||||
})
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Product not found")
|
||||
}
|
||||
|
||||
+2
@@ -8,6 +8,7 @@ import (
|
||||
service "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/services"
|
||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/validations"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/response"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
@@ -30,6 +31,7 @@ func (u *ProductWarehouseController) GetAll(c *fiber.Ctx) error {
|
||||
WarehouseId: uint(c.QueryInt("warehouse_id", 0)),
|
||||
Flags: c.Query("flags", ""),
|
||||
KandangId: uint(c.QueryInt("kandang_id", 0)),
|
||||
TransferContext: c.Query(utils.TransferContextKey, ""),
|
||||
}
|
||||
|
||||
if query.Page < 1 || query.Limit < 1 {
|
||||
|
||||
+29
-7
@@ -7,11 +7,11 @@ import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/sirupsen/logrus"
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||
repository "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
|
||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/validations"
|
||||
kandangrepo "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/repositories"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -54,10 +54,18 @@ func (s productWarehouseService) GetAll(c *fiber.Ctx, params *validation.Query)
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
scope, err := m.ResolveLocationScope(c, s.Repository.DB())
|
||||
applyScope := true
|
||||
if params.TransferContext == utils.TransferContextInventoryTransfer {
|
||||
applyScope = !m.HasPermission(c, m.P_TransferCreateOne)
|
||||
}
|
||||
var scope m.ScopeFilter
|
||||
var err error
|
||||
if applyScope {
|
||||
scope, err = m.ResolveLocationScope(c, s.Repository.DB())
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
}
|
||||
|
||||
if params.ProductId > 0 {
|
||||
isProductExist, err := s.Repository.IsProductExist(c.Context(), params.ProductId)
|
||||
@@ -96,12 +104,15 @@ func (s productWarehouseService) GetAll(c *fiber.Ctx, params *validation.Query)
|
||||
productWarehouses, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
|
||||
db = s.withRelations(db)
|
||||
|
||||
db = db.Joins("JOIN warehouses w_scope ON product_warehouses.warehouse_id = w_scope.id").
|
||||
Where("w_scope.deleted_at IS NULL")
|
||||
if applyScope {
|
||||
if scope.Restrict {
|
||||
if len(scope.IDs) == 0 {
|
||||
return db.Where("1 = 0")
|
||||
}
|
||||
db = db.Joins("JOIN warehouses w_scope ON product_warehouses.warehouse_id = w_scope.id").
|
||||
Where("w_scope.location_id IN ?", scope.IDs)
|
||||
db = db.Where("w_scope.location_id IN ?", scope.IDs)
|
||||
}
|
||||
}
|
||||
|
||||
if params.ProductId != 0 {
|
||||
@@ -130,19 +141,30 @@ func (s productWarehouseService) GetAll(c *fiber.Ctx, params *validation.Query)
|
||||
}
|
||||
|
||||
func (s productWarehouseService) GetOne(c *fiber.Ctx, id uint) (*entity.ProductWarehouse, error) {
|
||||
scope, err := m.ResolveLocationScope(c, s.Repository.DB())
|
||||
applyScope := true
|
||||
if c.Query(utils.TransferContextKey, "") == utils.TransferContextInventoryTransfer {
|
||||
applyScope = !m.HasPermission(c, m.P_TransferCreateOne)
|
||||
}
|
||||
var scope m.ScopeFilter
|
||||
var err error
|
||||
if applyScope {
|
||||
scope, err = m.ResolveLocationScope(c, s.Repository.DB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
productWarehouse, err := s.Repository.GetByID(c.Context(), id, func(db *gorm.DB) *gorm.DB {
|
||||
db = s.withRelations(db)
|
||||
db = db.Joins("JOIN warehouses w_scope ON product_warehouses.warehouse_id = w_scope.id").
|
||||
Where("w_scope.deleted_at IS NULL")
|
||||
if applyScope {
|
||||
if scope.Restrict {
|
||||
if len(scope.IDs) == 0 {
|
||||
return db.Where("1 = 0")
|
||||
}
|
||||
db = db.Joins("JOIN warehouses w_scope ON product_warehouses.warehouse_id = w_scope.id").
|
||||
Where("w_scope.location_id IN ?", scope.IDs)
|
||||
db = db.Where("w_scope.location_id IN ?", scope.IDs)
|
||||
}
|
||||
}
|
||||
return db
|
||||
})
|
||||
|
||||
+1
@@ -19,4 +19,5 @@ type Query struct {
|
||||
WarehouseId uint `query:"warehouse_id" validate:"omitempty,number,min=1"`
|
||||
Flags string `query:"flags" validate:"omitempty"`
|
||||
KandangId uint `query:"kandang_id" validate:"omitempty,number,min=1"`
|
||||
TransferContext string `query:"transfer_context" validate:"omitempty,oneof=inventory_transfer"`
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ func (r *MarketingDeliveryProductRepositoryImpl) GetAllWithFilters(ctx context.C
|
||||
Joins("JOIN marketings ON marketings.id = marketing_products.marketing_id").
|
||||
Where("marketing_delivery_products.delivery_date IS NOT NULL")
|
||||
|
||||
if filters.ProductId > 0 || filters.WarehouseId > 0 || filters.AreaId > 0 || filters.LocationId > 0 || filters.Search != "" || filters.MarketingType != "" {
|
||||
if filters.ProductId > 0 || filters.WarehouseId > 0 || filters.AreaId > 0 || filters.LocationId > 0 || filters.AllowedAreaIDs != nil || filters.AllowedLocationIDs != nil || filters.Search != "" || filters.MarketingType != "" {
|
||||
db = db.Joins("LEFT JOIN product_warehouses ON product_warehouses.id = marketing_products.product_warehouse_id")
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ func (r *MarketingDeliveryProductRepositoryImpl) GetAllWithFilters(ctx context.C
|
||||
db = db.Where("product_warehouses.warehouse_id = ?", filters.WarehouseId)
|
||||
}
|
||||
|
||||
if filters.AreaId > 0 || filters.LocationId > 0 {
|
||||
if filters.AreaId > 0 || filters.LocationId > 0 || filters.AllowedAreaIDs != nil || filters.AllowedLocationIDs != nil {
|
||||
db = db.Joins("LEFT JOIN project_flock_kandangs ON project_flock_kandangs.id = product_warehouses.project_flock_kandang_id").
|
||||
Joins("LEFT JOIN project_flocks ON project_flocks.id = project_flock_kandangs.project_flock_id")
|
||||
|
||||
@@ -201,6 +201,22 @@ func (r *MarketingDeliveryProductRepositoryImpl) GetAllWithFilters(ctx context.C
|
||||
if filters.LocationId > 0 {
|
||||
db = db.Where("project_flocks.location_id = ?", filters.LocationId)
|
||||
}
|
||||
|
||||
if filters.AllowedAreaIDs != nil {
|
||||
if len(filters.AllowedAreaIDs) == 0 {
|
||||
db = db.Where("1 = 0")
|
||||
} else {
|
||||
db = db.Where("project_flocks.area_id IN ?", filters.AllowedAreaIDs)
|
||||
}
|
||||
}
|
||||
|
||||
if filters.AllowedLocationIDs != nil {
|
||||
if len(filters.AllowedLocationIDs) == 0 {
|
||||
db = db.Where("1 = 0")
|
||||
} else {
|
||||
db = db.Where("project_flocks.location_id IN ?", filters.AllowedLocationIDs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if filters.MarketingType != "" {
|
||||
|
||||
@@ -119,12 +119,17 @@ func (s deliveryOrdersService) GetAll(c *fiber.Ctx, params *validation.DeliveryO
|
||||
if len(scope.IDs) == 0 {
|
||||
return db.Where("1 = 0")
|
||||
}
|
||||
db = db.
|
||||
Joins("JOIN marketing_products mp ON mp.marketing_id = marketings.id").
|
||||
Joins("JOIN product_warehouses pw ON pw.id = mp.product_warehouse_id").
|
||||
Joins("JOIN warehouses w ON w.id = pw.warehouse_id").
|
||||
Where("w.location_id IN ?", scope.IDs).
|
||||
Distinct("marketings.*")
|
||||
db = db.Where(
|
||||
`EXISTS (
|
||||
SELECT 1
|
||||
FROM marketing_products mp
|
||||
JOIN product_warehouses pw ON pw.id = mp.product_warehouse_id
|
||||
JOIN warehouses w ON w.id = pw.warehouse_id
|
||||
WHERE mp.marketing_id = marketings.id
|
||||
AND w.location_id IN ?
|
||||
)`,
|
||||
scope.IDs,
|
||||
)
|
||||
}
|
||||
|
||||
if params.MarketingId != 0 {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"strings"
|
||||
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||
repository "gitlab.com/mbugroup/lti-api.git/internal/modules/master/employees/repositories"
|
||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/master/employees/validations"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||
@@ -43,6 +44,61 @@ func (s employeesService) withRelations(db *gorm.DB) *gorm.DB {
|
||||
Where("employees.deleted_at IS NULL")
|
||||
}
|
||||
|
||||
func (s employeesService) ensureEmployeeAccess(c *fiber.Ctx, employeeID uint) error {
|
||||
if employeeID == 0 {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid employee id")
|
||||
}
|
||||
|
||||
db := s.Repository.DB().WithContext(c.Context()).
|
||||
Table("employees e").
|
||||
Joins("JOIN employee_kandangs ek ON ek.employee_id = e.id").
|
||||
Joins("JOIN kandangs k ON k.id = ek.kandang_id").
|
||||
Joins("JOIN locations loc ON loc.id = k.location_id").
|
||||
Joins("JOIN areas a ON a.id = loc.area_id").
|
||||
Where("e.id = ?", employeeID).
|
||||
Where("e.deleted_at IS NULL")
|
||||
|
||||
scopedDB, err := m.ApplyLocationAreaScope(c, db, "loc.id", "a.id")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var count int64
|
||||
if err := scopedDB.Count(&count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if count == 0 {
|
||||
return fiber.NewError(fiber.StatusNotFound, "Employees not found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s employeesService) ensureKandangIDsAccess(c *fiber.Ctx, kandangIDs []uint) error {
|
||||
if len(kandangIDs) == 0 {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "kandang_ids must contain at least one valid id")
|
||||
}
|
||||
|
||||
db := s.Repository.DB().WithContext(c.Context()).
|
||||
Table("kandangs k").
|
||||
Joins("JOIN locations loc ON loc.id = k.location_id").
|
||||
Joins("JOIN areas a ON a.id = loc.area_id").
|
||||
Where("k.id IN ?", kandangIDs)
|
||||
|
||||
scopedDB, err := m.ApplyLocationAreaScope(c, db, "loc.id", "a.id")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var count int64
|
||||
if err := scopedDB.Count(&count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if count != int64(len(kandangIDs)) {
|
||||
return fiber.NewError(fiber.StatusNotFound, "Kandang not found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s employeesService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.Employees, int64, error) {
|
||||
if err := s.Validate.Struct(params); err != nil {
|
||||
return nil, 0, err
|
||||
@@ -52,17 +108,29 @@ func (s employeesService) GetAll(c *fiber.Ctx, params *validation.Query) ([]enti
|
||||
|
||||
employeess, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
|
||||
db = s.withRelations(db)
|
||||
db = db.Joins("JOIN employee_kandangs ek ON ek.employee_id = employees.id").
|
||||
Joins("JOIN kandangs k ON k.id = ek.kandang_id").
|
||||
Joins("JOIN locations loc ON loc.id = k.location_id").
|
||||
Joins("JOIN areas a ON a.id = loc.area_id")
|
||||
var scopeErr error
|
||||
db, scopeErr = m.ApplyLocationAreaScope(c, db, "loc.id", "a.id")
|
||||
if scopeErr != nil {
|
||||
return db.Where("1 = 0")
|
||||
}
|
||||
if params.Search != "" {
|
||||
db = db.Where("employees.name ILIKE ?", "%"+params.Search+"%")
|
||||
}
|
||||
if params.KandangId != nil {
|
||||
db = db.Joins("JOIN employee_kandangs ek ON ek.employee_id = employees.id").
|
||||
Where("ek.kandang_id = ?", *params.KandangId)
|
||||
db = db.Where("ek.kandang_id = ?", *params.KandangId)
|
||||
}
|
||||
if params.IsActive != nil {
|
||||
db = db.Where("employees.is_active = ?", *params.IsActive)
|
||||
}
|
||||
return db.Order("employees.created_at DESC").Order("employees.updated_at DESC")
|
||||
return db.
|
||||
Select("employees.id, employees.name, employees.is_active, employees.created_at, employees.updated_at").
|
||||
Group("employees.id, employees.name, employees.is_active, employees.created_at, employees.updated_at").
|
||||
Order("employees.created_at DESC").
|
||||
Order("employees.updated_at DESC")
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
@@ -73,6 +141,9 @@ func (s employeesService) GetAll(c *fiber.Ctx, params *validation.Query) ([]enti
|
||||
}
|
||||
|
||||
func (s employeesService) GetOne(c *fiber.Ctx, id uint) (*entity.Employees, error) {
|
||||
if err := s.ensureEmployeeAccess(c, id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
employees, err := s.Repository.GetByID(c.Context(), id, s.withRelations)
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Employees not found")
|
||||
@@ -98,6 +169,9 @@ func (s *employeesService) CreateOne(c *fiber.Ctx, req *validation.Create) (*ent
|
||||
if len(kandangIDs) == 0 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "kandang_ids must contain at least one valid id")
|
||||
}
|
||||
if err := s.ensureKandangIDsAccess(c, kandangIDs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := s.Repository.First(c.Context(), func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("LOWER(name) = ?", strings.ToLower(name))
|
||||
@@ -147,6 +221,9 @@ func (s employeesService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin
|
||||
if err := s.Validate.Struct(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.ensureEmployeeAccess(c, id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updateBody := make(map[string]any)
|
||||
var (
|
||||
@@ -181,6 +258,9 @@ func (s employeesService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin
|
||||
if len(ids) == 0 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "kandang_ids must contain at least one valid id")
|
||||
}
|
||||
if err := s.ensureKandangIDsAccess(c, ids); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kandangIDs = ids
|
||||
needKandangUpdate = true
|
||||
@@ -234,6 +314,9 @@ func (s employeesService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin
|
||||
}
|
||||
|
||||
func (s employeesService) DeleteOne(c *fiber.Ctx, id uint) error {
|
||||
if err := s.ensureEmployeeAccess(c, id); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.Repository.DeleteOne(c.Context(), id); err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return fiber.NewError(fiber.StatusNotFound, "Employees not found")
|
||||
|
||||
@@ -3,11 +3,13 @@ package controller
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/dto"
|
||||
service "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/services"
|
||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/validations"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/response"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
@@ -23,6 +25,11 @@ func NewWarehouseController(warehouseService service.WarehouseService) *Warehous
|
||||
}
|
||||
|
||||
func (u *WarehouseController) GetAll(c *fiber.Ctx) error {
|
||||
excludeIDs, err := parseCommaSeparatedUint(c.Query("exclude_id", ""))
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
query := &validation.Query{
|
||||
Page: c.QueryInt("page", 1),
|
||||
Limit: c.QueryInt("limit", 10),
|
||||
@@ -30,6 +37,8 @@ func (u *WarehouseController) GetAll(c *fiber.Ctx) error {
|
||||
AreaId: c.QueryInt("area_id", 0),
|
||||
LocationId: c.QueryInt("location_id", 0),
|
||||
ActiveProjectFlockOnly: c.QueryBool("active_project_flock", false),
|
||||
TransferContext: c.Query(utils.TransferContextKey, ""),
|
||||
ExcludeIDs: excludeIDs,
|
||||
}
|
||||
|
||||
if query.Page < 1 || query.Limit < 1 {
|
||||
@@ -56,6 +65,28 @@ func (u *WarehouseController) GetAll(c *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
func parseCommaSeparatedUint(raw string) ([]uint, error) {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
parts := strings.Split(raw, ",")
|
||||
out := make([]uint, 0, len(parts))
|
||||
for _, part := range parts {
|
||||
part = strings.TrimSpace(part)
|
||||
if part == "" {
|
||||
continue
|
||||
}
|
||||
value, err := strconv.ParseUint(part, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "invalid exclude_id")
|
||||
}
|
||||
out = append(out, uint(value))
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (u *WarehouseController) GetOne(c *fiber.Ctx) error {
|
||||
param := c.Params("id")
|
||||
|
||||
|
||||
@@ -54,7 +54,14 @@ func (s warehouseService) GetAll(c *fiber.Ctx, params *validation.Query) ([]enti
|
||||
|
||||
warehouses, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
|
||||
db = s.withRelations(db)
|
||||
db, scopeErr = m.ApplyAreaScope(c, db, "warehouses.area_id")
|
||||
applyScope := true
|
||||
if params.TransferContext == utils.TransferContextInventoryTransfer {
|
||||
applyScope = !m.HasPermission(c, m.P_TransferCreateOne)
|
||||
}
|
||||
|
||||
if applyScope {
|
||||
db, scopeErr = m.ApplyLocationAreaScope(c, db, "warehouses.location_id", "warehouses.area_id")
|
||||
}
|
||||
if params.Search != "" {
|
||||
db = db.Where("warehouses.name ILIKE ?", "%"+params.Search+"%")
|
||||
}
|
||||
@@ -81,6 +88,9 @@ func (s warehouseService) GetAll(c *fiber.Ctx, params *validation.Query) ([]enti
|
||||
)
|
||||
`, "Aktif")
|
||||
}
|
||||
if len(params.ExcludeIDs) > 0 {
|
||||
db = db.Where("warehouses.id NOT IN ?", params.ExcludeIDs)
|
||||
}
|
||||
return db.Order("created_at DESC").Order("updated_at DESC")
|
||||
})
|
||||
|
||||
@@ -99,7 +109,7 @@ func (s warehouseService) GetOne(c *fiber.Ctx, id uint) (*entity.Warehouse, erro
|
||||
|
||||
warehouse, err := s.Repository.GetByID(c.Context(), id, func(db *gorm.DB) *gorm.DB {
|
||||
db = s.withRelations(db)
|
||||
db, scopeErr = m.ApplyAreaScope(c, db, "warehouses.area_id")
|
||||
db, scopeErr = m.ApplyLocationAreaScope(c, db, "warehouses.location_id", "warehouses.area_id")
|
||||
return db
|
||||
})
|
||||
if scopeErr != nil {
|
||||
|
||||
@@ -23,4 +23,6 @@ type Query struct {
|
||||
AreaId int `query:"area_id" validate:"omitempty,number,gt=0"`
|
||||
LocationId int `query:"location_id" validate:"omitempty,number,gt=0"`
|
||||
ActiveProjectFlockOnly bool `query:"active_project_flock"`
|
||||
ExcludeIDs []uint `query:"-" validate:"omitempty,dive,gt=0"`
|
||||
TransferContext string `query:"transfer_context" validate:"omitempty,oneof=inventory_transfer"`
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
service "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/services"
|
||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/validations"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/response"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
@@ -76,6 +77,7 @@ func (u *ProjectflockController) GetAll(c *fiber.Ctx) error {
|
||||
query.Category = category
|
||||
|
||||
}
|
||||
query.TransferContext = c.Query(utils.TransferContextKey, "")
|
||||
|
||||
if kandangRaw := c.Query("kandang_id", c.Query("kandang_ids", "")); kandangRaw != "" {
|
||||
ids, err := parseUintList(kandangRaw)
|
||||
|
||||
@@ -121,6 +121,12 @@ func (s projectflockService) GetAll(c *fiber.Ctx, params *validation.Query) ([]e
|
||||
if err != nil {
|
||||
return nil, 0, nil, err
|
||||
}
|
||||
if params.TransferContext == utils.TransferContextTransferToLaying {
|
||||
if m.HasPermission(c, m.P_TransferToLaying_CreateOne) || m.HasPermission(c, m.P_TransferToLaying_UpdateOne) {
|
||||
scope.Restrict = false
|
||||
scope.IDs = nil
|
||||
}
|
||||
}
|
||||
|
||||
offset := (params.Page - 1) * params.Limit
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ type Query struct {
|
||||
Period int `query:"period" validate:"omitempty,number,gt=0"`
|
||||
Category string `query:"category" validate:"omitempty"`
|
||||
KandangIds []uint `query:"kandang_id" validate:"omitempty,dive,gt=0"`
|
||||
TransferContext string `query:"transfer_context" validate:"omitempty,oneof=transfer_to_laying"`
|
||||
}
|
||||
|
||||
type Approve struct {
|
||||
|
||||
@@ -106,15 +106,31 @@ func (s transferLayingService) GetAll(c *fiber.Ctx, params *validation.Query) ([
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
scope, err := m.ResolveLocationScope(c, s.Repository.DB())
|
||||
if 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 {
|
||||
needFlockJoin := scope.Restrict || params.Search != ""
|
||||
if needFlockJoin {
|
||||
db = db.Joins("LEFT JOIN project_flocks AS pf_from ON laying_transfers.from_project_flock_id = pf_from.id").
|
||||
Joins("LEFT JOIN project_flocks AS pf_to ON laying_transfers.to_project_flock_id = pf_to.id")
|
||||
}
|
||||
|
||||
if scope.Restrict {
|
||||
if len(scope.IDs) == 0 {
|
||||
return db.Where("1 = 0")
|
||||
}
|
||||
db = db.Where("(pf_from.location_id IN ? OR pf_to.location_id IN ?)", scope.IDs, scope.IDs)
|
||||
}
|
||||
|
||||
// Apply search and filters
|
||||
if params.Search != "" {
|
||||
searchPattern := "%" + params.Search + "%"
|
||||
db = db.Joins("LEFT JOIN project_flocks AS pf_from ON laying_transfers.from_project_flock_id = pf_from.id").
|
||||
Joins("LEFT JOIN project_flocks AS pf_to ON laying_transfers.to_project_flock_id = pf_to.id").
|
||||
Where("laying_transfers.transfer_number ILIKE ? OR laying_transfers.notes ILIKE ? OR pf_from.flock_name ILIKE ? OR pf_to.flock_name ILIKE ?",
|
||||
db = db.Where("laying_transfers.transfer_number ILIKE ? OR laying_transfers.notes ILIKE ? OR pf_from.flock_name ILIKE ? OR pf_to.flock_name ILIKE ?",
|
||||
searchPattern, searchPattern, searchPattern, searchPattern)
|
||||
}
|
||||
|
||||
@@ -181,12 +197,11 @@ func (s *transferLayingService) CreateOne(c *fiber.Ctx, req *validation.Create)
|
||||
if err := s.Validate.Struct(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := m.EnsureProjectFlockAccess(c, s.Repository.DB(), req.SourceProjectFlockId); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !m.HasPermission(c, m.P_TransferToLaying_CreateOne) {
|
||||
if err := m.EnsureProjectFlockAccess(c, s.Repository.DB(), req.TargetProjectFlockId); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
actorID, err := m.ActorIDFromContext(c)
|
||||
if err != nil {
|
||||
@@ -415,15 +430,11 @@ func (s *transferLayingService) UpdateOne(c *fiber.Ctx, req *validation.Update,
|
||||
if err := s.Validate.Struct(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := m.EnsureLayingTransferAccess(c, s.Repository.DB(), id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := m.EnsureProjectFlockAccess(c, s.Repository.DB(), req.SourceProjectFlockId); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !m.HasPermission(c, m.P_TransferToLaying_UpdateOne) {
|
||||
if err := m.EnsureProjectFlockAccess(c, s.Repository.DB(), req.TargetProjectFlockId); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
existingTransfer, err := s.Repository.GetByID(c.Context(), id, func(db *gorm.DB) *gorm.DB {
|
||||
return db.Preload("Sources.ProductWarehouse").Preload("Targets")
|
||||
@@ -599,6 +610,9 @@ func (s *transferLayingService) UpdateOne(c *fiber.Ctx, req *validation.Update,
|
||||
}
|
||||
|
||||
func (s transferLayingService) DeleteOne(c *fiber.Ctx, id uint) error {
|
||||
if err := m.EnsureLayingTransferAccess(c, s.Repository.DB(), id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := s.Repository.GetByID(c.Context(), id, func(db *gorm.DB) *gorm.DB {
|
||||
return db.Preload("Sources.ProductWarehouse").Preload("Targets")
|
||||
@@ -669,6 +683,12 @@ func (s transferLayingService) Approval(c *fiber.Ctx, req *validation.Approve) (
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "approvable_ids must contain at least one id")
|
||||
}
|
||||
|
||||
for _, approvableID := range approvableIDs {
|
||||
if err := m.EnsureLayingTransferAccess(c, s.Repository.DB(), approvableID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
step := utils.TransferToLayingStepPengajuan
|
||||
if action == entity.ApprovalActionApproved {
|
||||
step = utils.TransferToLayingStepDisetujui
|
||||
|
||||
@@ -92,7 +92,7 @@ func (s uniformityService) GetAll(c *fiber.Ctx, params *validation.Query) ([]ent
|
||||
offset := (params.Page - 1) * params.Limit
|
||||
uniformitys, total, err := s.Repository.GetAllWithFilters(c.Context(), offset, params.Limit, params, func(db *gorm.DB) *gorm.DB {
|
||||
db = db.
|
||||
Joins("JOIN project_flock_kandangs pfk ON pfk.id = project_flock_kandang_uniformities.project_flock_kandang_id").
|
||||
Joins("JOIN project_flock_kandangs pfk ON pfk.id = project_flock_kandang_uniformity.project_flock_kandang_id").
|
||||
Joins("JOIN project_flocks pf ON pf.id = pfk.project_flock_id")
|
||||
db, scopeErr = m.ApplyLocationScope(c, db, "pf.location_id")
|
||||
return db
|
||||
|
||||
@@ -112,6 +112,10 @@ func (c *RepportController) GetMarketing(ctx *fiber.Ctx) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
areaScope, err := m.ResolveAreaScope(ctx, c.RepportService.DB())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if locationScope.Restrict {
|
||||
allowed := toInt64Slice(locationScope.IDs)
|
||||
if len(allowed) == 0 {
|
||||
@@ -119,6 +123,13 @@ func (c *RepportController) GetMarketing(ctx *fiber.Ctx) error {
|
||||
}
|
||||
query.AllowedLocationIDs = allowed
|
||||
}
|
||||
if areaScope.Restrict {
|
||||
allowed := toInt64Slice(areaScope.IDs)
|
||||
if len(allowed) == 0 {
|
||||
allowed = []int64{-1}
|
||||
}
|
||||
query.AllowedAreaIDs = allowed
|
||||
}
|
||||
|
||||
if query.Page < 1 || query.Limit < 1 {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "page and limit must be greater than 0")
|
||||
@@ -220,6 +231,21 @@ func (c *RepportController) GetDebtSupplier(ctx *fiber.Ctx) error {
|
||||
SortOrder: ctx.Query("sort_order", ""),
|
||||
}
|
||||
|
||||
locationScope, err := m.ResolveLocationScope(ctx, c.RepportService.DB())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
areaScope, err := m.ResolveAreaScope(ctx, c.RepportService.DB())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if locationScope.Restrict {
|
||||
query.AllowedLocationIDs = toInt64Slice(locationScope.IDs)
|
||||
}
|
||||
if areaScope.Restrict {
|
||||
query.AllowedAreaIDs = toInt64Slice(areaScope.IDs)
|
||||
}
|
||||
|
||||
if query.Page < 1 || query.Limit < 1 {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "page and limit must be greater than 0")
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ type CustomerPaymentTransaction struct {
|
||||
type CustomerPaymentRepository interface {
|
||||
GetCustomerPaymentTransactions(ctx context.Context, customerID *uint) ([]CustomerPaymentTransaction, error)
|
||||
GetInitialBalanceByCustomer(ctx context.Context, customerID uint) (float64, error)
|
||||
GetCustomerIDsWithTransactions(ctx context.Context, limit, offset int) ([]uint, int64, error)
|
||||
GetCustomerIDsWithTransactions(ctx context.Context, limit, offset int, allowedCustomerIDs []uint) ([]uint, int64, error)
|
||||
}
|
||||
|
||||
type customerPaymentRepositoryImpl struct {
|
||||
@@ -146,7 +146,7 @@ func (r *customerPaymentRepositoryImpl) GetInitialBalanceByCustomer(ctx context.
|
||||
return result.Nominal, nil
|
||||
}
|
||||
|
||||
func (r *customerPaymentRepositoryImpl) GetCustomerIDsWithTransactions(ctx context.Context, limit, offset int) ([]uint, int64, error) {
|
||||
func (r *customerPaymentRepositoryImpl) GetCustomerIDsWithTransactions(ctx context.Context, limit, offset int, allowedCustomerIDs []uint) ([]uint, int64, error) {
|
||||
subQuery := r.db.WithContext(ctx).
|
||||
Table("(" +
|
||||
"SELECT DISTINCT c.id as customer_id FROM marketing_delivery_products mdp " +
|
||||
@@ -161,13 +161,17 @@ func (r *customerPaymentRepositoryImpl) GetCustomerIDsWithTransactions(ctx conte
|
||||
"AND p.transaction_type = 'PENJUALAN' AND p.deleted_at IS NULL AND c.deleted_at IS NULL" +
|
||||
") as customer_ids")
|
||||
|
||||
if len(allowedCustomerIDs) > 0 {
|
||||
subQuery = subQuery.Where("customer_id IN ?", allowedCustomerIDs)
|
||||
}
|
||||
|
||||
var total int64
|
||||
if err := subQuery.Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
var customerIDs []uint
|
||||
err := r.db.WithContext(ctx).
|
||||
query := r.db.WithContext(ctx).
|
||||
Table("(" +
|
||||
"SELECT DISTINCT c.id as customer_id FROM marketing_delivery_products mdp " +
|
||||
"INNER JOIN marketing_products mp ON mp.id = mdp.marketing_product_id " +
|
||||
@@ -180,7 +184,13 @@ func (r *customerPaymentRepositoryImpl) GetCustomerIDsWithTransactions(ctx conte
|
||||
"WHERE p.party_type = 'CUSTOMER' AND p.direction = 'IN' " +
|
||||
"AND p.transaction_type = 'PENJUALAN' AND p.deleted_at IS NULL AND c.deleted_at IS NULL" +
|
||||
") as customer_ids").
|
||||
Select("customer_id").
|
||||
Select("customer_id")
|
||||
|
||||
if len(allowedCustomerIDs) > 0 {
|
||||
query = query.Where("customer_id IN ?", allowedCustomerIDs)
|
||||
}
|
||||
|
||||
err := query.
|
||||
Order("customer_id ASC").
|
||||
Limit(limit).
|
||||
Offset(offset).
|
||||
|
||||
@@ -70,6 +70,7 @@ func (r *debtSupplierRepositoryImpl) baseSupplierQuery(ctx context.Context, filt
|
||||
Model(&entity.Supplier{}).
|
||||
Joins("JOIN purchases ON purchases.supplier_id = suppliers.id").
|
||||
Joins("JOIN purchase_items ON purchase_items.purchase_id = purchases.id").
|
||||
Joins("JOIN warehouses w ON w.id = purchase_items.warehouse_id").
|
||||
Joins("JOIN (?) AS la ON la.approvable_id = purchases.id", r.latestPurchaseApproval(ctx)).
|
||||
Where("la.step_number >= ?", uint16(utils.PurchaseStepReceiving)).
|
||||
Where("(la.action IS NULL OR la.action != ?)", string(entity.ApprovalActionRejected)).
|
||||
@@ -79,6 +80,22 @@ func (r *debtSupplierRepositoryImpl) baseSupplierQuery(ctx context.Context, filt
|
||||
db = db.Where("suppliers.id IN ?", filters.SupplierIDs)
|
||||
}
|
||||
|
||||
if filters.AllowedAreaIDs != nil {
|
||||
if len(filters.AllowedAreaIDs) == 0 {
|
||||
db = db.Where("1 = 0")
|
||||
} else {
|
||||
db = db.Where("w.area_id IN ?", filters.AllowedAreaIDs)
|
||||
}
|
||||
}
|
||||
|
||||
if filters.AllowedLocationIDs != nil {
|
||||
if len(filters.AllowedLocationIDs) == 0 {
|
||||
db = db.Where("1 = 0")
|
||||
} else {
|
||||
db = db.Where("w.location_id IN ?", filters.AllowedLocationIDs)
|
||||
}
|
||||
}
|
||||
|
||||
if filters.StartDate != "" {
|
||||
if dateFrom, err := utils.ParseDateString(filters.StartDate); err == nil {
|
||||
db = db.Where(fmt.Sprintf("DATE(%s) >= ?", dateColumn), dateFrom)
|
||||
@@ -226,12 +243,29 @@ func (r *debtSupplierRepositoryImpl) getPurchaseIDs(ctx context.Context, supplie
|
||||
Table("purchases").
|
||||
Select("DISTINCT purchases.id").
|
||||
Joins("JOIN purchase_items ON purchase_items.purchase_id = purchases.id").
|
||||
Joins("JOIN warehouses w ON w.id = purchase_items.warehouse_id").
|
||||
Joins("JOIN (?) AS la ON la.approvable_id = purchases.id", r.latestPurchaseApproval(ctx)).
|
||||
Where("purchases.supplier_id IN ?", supplierIDs).
|
||||
Where("la.step_number >= ?", uint16(utils.PurchaseStepReceiving)).
|
||||
Where("(la.action IS NULL OR la.action != ?)", string(entity.ApprovalActionRejected)).
|
||||
Where("purchase_items.received_date IS NOT NULL")
|
||||
|
||||
if filters.AllowedAreaIDs != nil {
|
||||
if len(filters.AllowedAreaIDs) == 0 {
|
||||
db = db.Where("1 = 0")
|
||||
} else {
|
||||
db = db.Where("w.area_id IN ?", filters.AllowedAreaIDs)
|
||||
}
|
||||
}
|
||||
|
||||
if filters.AllowedLocationIDs != nil {
|
||||
if len(filters.AllowedLocationIDs) == 0 {
|
||||
db = db.Where("1 = 0")
|
||||
} else {
|
||||
db = db.Where("w.location_id IN ?", filters.AllowedLocationIDs)
|
||||
}
|
||||
}
|
||||
|
||||
if filters.StartDate != "" {
|
||||
if dateFrom, err := utils.ParseDateString(filters.StartDate); err == nil {
|
||||
db = db.Where(fmt.Sprintf("DATE(%s) >= ?", dateColumn), dateFrom)
|
||||
|
||||
@@ -46,12 +46,13 @@ type RepportService interface {
|
||||
GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangResponseData, *dto.HppPerKandangMetaDTO, error)
|
||||
GetProductionResult(ctx *fiber.Ctx, params *validation.ProductionResultQuery) ([]dto.ProductionResultDTO, int64, error)
|
||||
GetCustomerPayment(ctx *fiber.Ctx, params *validation.CustomerPaymentQuery) ([]dto.CustomerPaymentReportItem, int64, error)
|
||||
DB() *gorm.DB
|
||||
}
|
||||
|
||||
type repportService struct {
|
||||
Log *logrus.Logger
|
||||
Validate *validator.Validate
|
||||
DB *gorm.DB
|
||||
db *gorm.DB
|
||||
ExpenseRealizationRepo expenseRepo.ExpenseRealizationRepository
|
||||
MarketingDeliveryRepo marketingRepo.MarketingDeliveryProductRepository
|
||||
PurchaseRepo purchaseRepo.PurchaseRepository
|
||||
@@ -100,7 +101,7 @@ func NewRepportService(
|
||||
return &repportService{
|
||||
Log: utils.Log,
|
||||
Validate: validate,
|
||||
DB: db,
|
||||
db: db,
|
||||
ExpenseRealizationRepo: expenseRealizationRepo,
|
||||
MarketingDeliveryRepo: marketingDeliveryRepo,
|
||||
PurchaseRepo: purchaseRepo,
|
||||
@@ -119,6 +120,9 @@ func NewRepportService(
|
||||
}
|
||||
}
|
||||
|
||||
func (s *repportService) DB() *gorm.DB {
|
||||
return s.db
|
||||
}
|
||||
|
||||
func (s *repportService) GetExpense(c *fiber.Ctx, params *validation.ExpenseQuery) ([]dto.RepportExpenseListDTO, int64, error) {
|
||||
if err := s.Validate.Struct(params); err != nil {
|
||||
@@ -407,11 +411,38 @@ func (s *repportService) GetCustomerPayment(ctx *fiber.Ctx, params *validation.C
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
locationScope, err := m.ResolveLocationScope(ctx, s.DB())
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
areaScope, err := m.ResolveAreaScope(ctx, s.DB())
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
restrictScope := locationScope.Restrict || areaScope.Restrict
|
||||
var allowedCustomerIDs []uint
|
||||
if restrictScope {
|
||||
if (locationScope.Restrict && len(locationScope.IDs) == 0) || (areaScope.Restrict && len(areaScope.IDs) == 0) {
|
||||
return []dto.CustomerPaymentReportItem{}, 0, nil
|
||||
}
|
||||
allowedCustomerIDs, err = s.getCustomerIDsByScope(ctx.Context(), locationScope.IDs, areaScope.IDs)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if len(allowedCustomerIDs) == 0 {
|
||||
return []dto.CustomerPaymentReportItem{}, 0, nil
|
||||
}
|
||||
}
|
||||
|
||||
var customerIDs []uint
|
||||
var totalCustomers int64
|
||||
|
||||
if len(params.CustomerIDs) > 0 {
|
||||
customerIDs = params.CustomerIDs
|
||||
if restrictScope {
|
||||
customerIDs = intersectUint(customerIDs, allowedCustomerIDs)
|
||||
}
|
||||
totalCustomers = int64(len(customerIDs))
|
||||
|
||||
if len(customerIDs) == 0 {
|
||||
@@ -430,7 +461,7 @@ func (s *repportService) GetCustomerPayment(ctx *fiber.Ctx, params *validation.C
|
||||
offset := (page - 1) * limit
|
||||
|
||||
var err error
|
||||
customerIDs, totalCustomers, err = s.CustomerPaymentRepo.GetCustomerIDsWithTransactions(ctx.Context(), limit, offset)
|
||||
customerIDs, totalCustomers, err = s.CustomerPaymentRepo.GetCustomerIDsWithTransactions(ctx.Context(), limit, offset, allowedCustomerIDs)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -456,6 +487,37 @@ func (s *repportService) GetCustomerPayment(ctx *fiber.Ctx, params *validation.C
|
||||
return result, totalCustomers, nil
|
||||
}
|
||||
|
||||
func (s *repportService) getCustomerIDsByScope(ctx context.Context, locationIDs, areaIDs []uint) ([]uint, error) {
|
||||
if len(locationIDs) == 0 && len(areaIDs) == 0 {
|
||||
return []uint{}, nil
|
||||
}
|
||||
|
||||
db := s.db.WithContext(ctx).
|
||||
Table("customers c").
|
||||
Select("DISTINCT c.id").
|
||||
Joins("JOIN marketings m ON m.customer_id = c.id").
|
||||
Joins("JOIN marketing_products mp ON mp.marketing_id = m.id").
|
||||
Joins("JOIN marketing_delivery_products mdp ON mdp.marketing_product_id = mp.id").
|
||||
Joins("JOIN product_warehouses pw ON pw.id = mdp.product_warehouse_id").
|
||||
Joins("JOIN warehouses w ON w.id = pw.warehouse_id").
|
||||
Where("mdp.delivery_date IS NOT NULL").
|
||||
Where("m.deleted_at IS NULL").
|
||||
Where("c.deleted_at IS NULL")
|
||||
|
||||
if len(locationIDs) > 0 {
|
||||
db = db.Where("w.location_id IN ?", locationIDs)
|
||||
}
|
||||
if len(areaIDs) > 0 {
|
||||
db = db.Where("w.area_id IN ?", areaIDs)
|
||||
}
|
||||
|
||||
var customerIDs []uint
|
||||
if err := db.Pluck("c.id", &customerIDs).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return customerIDs, nil
|
||||
}
|
||||
|
||||
func (s *repportService) processCustomerPayment(ctx context.Context, customerID uint, params *validation.CustomerPaymentQuery) (dto.CustomerPaymentReportItem, error) {
|
||||
|
||||
customer, err := s.CustomerRepo.GetByID(ctx, customerID, nil)
|
||||
@@ -803,7 +865,7 @@ func (s *repportService) getUniformityByWeek(ctx context.Context, projectFlockKa
|
||||
}
|
||||
|
||||
var rows []entity.ProjectFlockKandangUniformity
|
||||
if err := s.DB.WithContext(ctx).
|
||||
if err := s.db.WithContext(ctx).
|
||||
Model(&entity.ProjectFlockKandangUniformity{}).
|
||||
Select("week, uniformity, uniform_date, id, chart_data").
|
||||
Where("project_flock_kandang_id = ?", projectFlockKandangID).
|
||||
@@ -2007,6 +2069,23 @@ func intersectInt64(a, b []int64) []int64 {
|
||||
return out
|
||||
}
|
||||
|
||||
func intersectUint(a, b []uint) []uint {
|
||||
if len(a) == 0 || len(b) == 0 {
|
||||
return nil
|
||||
}
|
||||
set := make(map[uint]struct{}, len(b))
|
||||
for _, id := range b {
|
||||
set[id] = struct{}{}
|
||||
}
|
||||
out := make([]uint, 0, len(a))
|
||||
for _, id := range a {
|
||||
if _, ok := set[id]; ok {
|
||||
out = append(out, id)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func parseOptionalFloat64(raw string) (*float64, error) {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
|
||||
@@ -33,6 +33,7 @@ type MarketingQuery struct {
|
||||
EndDate string `query:"end_date" validate:"omitempty,datetime=2006-01-02"`
|
||||
SortBy string `query:"sort_by" validate:"omitempty,oneof=so_date realization_date customer warehouse product sales_person vehicle_number sales_amount hpp_amount qty average_weight total_weight sales_price hpp_price aging_days"`
|
||||
SortOrder string `query:"sort_order" validate:"omitempty,oneof=asc desc"`
|
||||
AllowedAreaIDs []int64 `query:"-"`
|
||||
AllowedLocationIDs []int64 `query:"-"`
|
||||
}
|
||||
|
||||
@@ -58,6 +59,8 @@ type DebtSupplierQuery struct {
|
||||
EndDate string `query:"end_date" validate:"omitempty,datetime=2006-01-02"`
|
||||
FilterBy string `query:"filter_by" validate:"omitempty,oneof=received_date po_date"`
|
||||
SortOrder string `query:"sort_order" validate:"omitempty,oneof=asc desc"`
|
||||
AllowedAreaIDs []int64 `query:"-"`
|
||||
AllowedLocationIDs []int64 `query:"-"`
|
||||
}
|
||||
|
||||
type HppPerKandangQuery struct {
|
||||
|
||||
@@ -117,6 +117,16 @@ const (
|
||||
StockLogTypeRecording StockLogType = "RECORDING"
|
||||
)
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Transfer context
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
const (
|
||||
TransferContextKey = "transfer_context"
|
||||
TransferContextInventoryTransfer = "inventory_transfer"
|
||||
TransferContextTransferToLaying = "transfer_to_laying"
|
||||
)
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// WarehouseType
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
@@ -1,446 +0,0 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/glebarez/sqlite"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
|
||||
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"
|
||||
rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
|
||||
recordingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories"
|
||||
servicePkg "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/services"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils/fifo"
|
||||
)
|
||||
|
||||
func TestRecordingFIFO_CreatePendingWithoutStock(t *testing.T) {
|
||||
db, svc, _, _ := setupRecordingFIFOTableTest(t)
|
||||
ctx := context.Background()
|
||||
|
||||
recordingID := uint(1)
|
||||
productWarehouse := createProductWarehouseRow(t, db, 0)
|
||||
stock := createRecordingStockRow(t, db, recordingID, productWarehouse.Id, 10)
|
||||
|
||||
if err := svc.ConsumeRecordingStocks(ctx, db, []entity.RecordingStock{stock}); err != nil {
|
||||
t.Fatalf("consumeRecordingStocks (pending) failed: %v", err)
|
||||
}
|
||||
|
||||
updated := fetchRecordingStock(t, db, stock.Id)
|
||||
assertFloatEqual(t, 0, updated.UsageQty, "usage_qty should remain zero when no stock is available")
|
||||
assertFloatEqual(t, 10, updated.PendingQty, "pending_qty should capture the entire request")
|
||||
assertWarehouseQuantity(t, db, productWarehouse.Id, 0)
|
||||
assertAllocationCount(t, db, 0)
|
||||
|
||||
assertAllocationCount(t, db, 0)
|
||||
}
|
||||
|
||||
func TestRecordingFIFO_EditReallocatesUsage(t *testing.T) {
|
||||
db, svc, fifoSvc, stockableKey := setupRecordingFIFOTableTest(t)
|
||||
ctx := context.Background()
|
||||
|
||||
recordingID := uint(1)
|
||||
productWarehouse := createProductWarehouseRow(t, db, 0)
|
||||
stock := createRecordingStockRow(t, db, recordingID, productWarehouse.Id, 10)
|
||||
lot := createStockLot(t, db, productWarehouse.Id)
|
||||
|
||||
if _, err := fifoSvc.Replenish(ctx, commonSvc.StockReplenishRequest{
|
||||
StockableKey: stockableKey,
|
||||
StockableID: lot.Id,
|
||||
ProductWarehouseID: productWarehouse.Id,
|
||||
Quantity: 12,
|
||||
}); err != nil {
|
||||
t.Fatalf("replenish failed: %v", err)
|
||||
}
|
||||
|
||||
if err := svc.ConsumeRecordingStocks(ctx, db, []entity.RecordingStock{stock}); err != nil {
|
||||
t.Fatalf("consumeRecordingStocks (initial) failed: %v", err)
|
||||
}
|
||||
|
||||
assertWarehouseQuantity(t, db, productWarehouse.Id, 2)
|
||||
|
||||
desired := 4.0
|
||||
stock.UsageQty = &desired
|
||||
|
||||
if err := svc.ConsumeRecordingStocks(ctx, db, []entity.RecordingStock{stock}); err != nil {
|
||||
t.Fatalf("consumeRecordingStocks (edit) failed: %v", err)
|
||||
}
|
||||
|
||||
updated := fetchRecordingStock(t, db, stock.Id)
|
||||
assertFloatEqual(t, 4, updated.UsageQty, "usage_qty should reflect edited request")
|
||||
assertFloatEqual(t, 0, updated.PendingQty, "pending_qty should remain zero after downsize")
|
||||
assertWarehouseQuantity(t, db, productWarehouse.Id, 8)
|
||||
|
||||
alloc := fetchSingleAllocation(t, db, stock.Id)
|
||||
if alloc.Status != entity.StockAllocationStatusActive {
|
||||
t.Fatalf("expected ACTIVE allocation, got %s", alloc.Status)
|
||||
}
|
||||
if mathAbs(alloc.Qty-4) > 1e-6 {
|
||||
t.Fatalf("expected allocation qty 4, got %.3f", alloc.Qty)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecordingFIFO_DeleteReleasesStock(t *testing.T) {
|
||||
db, svc, fifoSvc, stockableKey := setupRecordingFIFOTableTest(t)
|
||||
ctx := context.Background()
|
||||
|
||||
recordingID := uint(1)
|
||||
productWarehouse := createProductWarehouseRow(t, db, 0)
|
||||
stock := createRecordingStockRow(t, db, recordingID, productWarehouse.Id, 10)
|
||||
lot := createStockLot(t, db, productWarehouse.Id)
|
||||
|
||||
if _, err := fifoSvc.Replenish(ctx, commonSvc.StockReplenishRequest{
|
||||
StockableKey: stockableKey,
|
||||
StockableID: lot.Id,
|
||||
ProductWarehouseID: productWarehouse.Id,
|
||||
Quantity: 10,
|
||||
}); err != nil {
|
||||
t.Fatalf("replenish failed: %v", err)
|
||||
}
|
||||
if err := svc.ConsumeRecordingStocks(ctx, db, []entity.RecordingStock{stock}); err != nil {
|
||||
t.Fatalf("consumeRecordingStocks failed: %v", err)
|
||||
}
|
||||
|
||||
if err := svc.ReleaseRecordingStocks(ctx, db, []entity.RecordingStock{stock}); err != nil {
|
||||
t.Fatalf("releaseRecordingStocks failed: %v", err)
|
||||
}
|
||||
|
||||
updated := fetchRecordingStock(t, db, stock.Id)
|
||||
assertFloatEqual(t, 0, updated.UsageQty, "usage_qty should be cleared after delete")
|
||||
assertFloatEqual(t, 0, updated.PendingQty, "pending_qty should be cleared after delete")
|
||||
assertWarehouseQuantity(t, db, productWarehouse.Id, 10)
|
||||
|
||||
alloc := fetchSingleAllocation(t, db, stock.Id)
|
||||
if alloc.Status != entity.StockAllocationStatusReleased {
|
||||
t.Fatalf("expected allocation to be released, got %s", alloc.Status)
|
||||
}
|
||||
}
|
||||
|
||||
// --- helpers ----------------------------------------------------------------
|
||||
|
||||
type recordingStockTable struct {
|
||||
Id uint `gorm:"primaryKey"`
|
||||
RecordingId uint `gorm:"column:recording_id;not null"`
|
||||
ProductWarehouseId uint `gorm:"column:product_warehouse_id;not null"`
|
||||
UsageQty *float64 `gorm:"column:usage_qty"`
|
||||
PendingQty *float64 `gorm:"column:pending_qty"`
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
func (recordingStockTable) TableName() string { return "recording_stocks" }
|
||||
|
||||
type productWarehouseTable struct {
|
||||
Id uint `gorm:"primaryKey"`
|
||||
ProductId uint `gorm:"column:product_id"`
|
||||
WarehouseId uint `gorm:"column:warehouse_id"`
|
||||
Quantity float64 `gorm:"column:quantity"`
|
||||
CreatedBy uint `gorm:"column:created_by"`
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||
}
|
||||
|
||||
func (productWarehouseTable) TableName() string { return "product_warehouses" }
|
||||
|
||||
type stockAllocationTable struct {
|
||||
Id uint `gorm:"primaryKey"`
|
||||
ProductWarehouseId uint `gorm:"not null"`
|
||||
StockableType string `gorm:"size:100"`
|
||||
StockableId uint
|
||||
UsableType string `gorm:"size:100"`
|
||||
UsableId uint
|
||||
Qty float64 `gorm:"column:qty"`
|
||||
Status string `gorm:"size:20"`
|
||||
Note *string `gorm:"type:text"`
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
ReleasedAt *time.Time
|
||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||
}
|
||||
|
||||
func (stockAllocationTable) TableName() string { return "stock_allocations" }
|
||||
|
||||
type testStockSource struct {
|
||||
Id uint `gorm:"primaryKey"`
|
||||
ProductWarehouseId uint `gorm:"column:product_warehouse_id;not null"`
|
||||
TotalQty float64 `gorm:"column:total_qty"`
|
||||
TotalUsedQty float64 `gorm:"column:total_used_qty"`
|
||||
CreatedAt time.Time `gorm:"column:created_at"`
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
func (testStockSource) TableName() string { return "test_fifo_stockables" }
|
||||
|
||||
func setupRecordingFIFOTableTest(t *testing.T) (*gorm.DB, servicePkg.RecordingFIFOIntegrationService, commonSvc.FifoService, fifo.StockableKey) {
|
||||
t.Helper()
|
||||
|
||||
db, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{
|
||||
Logger: logger.Default.LogMode(logger.Silent),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("open sqlite: %v", err)
|
||||
}
|
||||
|
||||
if err := db.AutoMigrate(
|
||||
&recordingStockTable{},
|
||||
&productWarehouseTable{},
|
||||
&stockAllocationTable{},
|
||||
&testStockSource{},
|
||||
); err != nil {
|
||||
t.Fatalf("auto migrate: %v", err)
|
||||
}
|
||||
|
||||
if err := db.AutoMigrate(
|
||||
&entity.ProductWarehouse{},
|
||||
&entity.StockAllocation{},
|
||||
&entity.RecordingStock{},
|
||||
); err != nil {
|
||||
t.Fatalf("auto migrate entities: %v", err)
|
||||
}
|
||||
|
||||
stockAllocRepo := newFifoTestStockAllocationRepo(db)
|
||||
productWarehouseRepo := rProductWarehouse.NewProductWarehouseRepository(db)
|
||||
fifoSvc := commonSvc.NewFifoService(db, stockAllocRepo, productWarehouseRepo, utils.Log)
|
||||
|
||||
registerRecordingUsable(t, fifoSvc)
|
||||
|
||||
key := fifo.StockableKey(fmt.Sprintf("TEST_STOCKABLE_%s_%d", sanitizeKey(t.Name()), time.Now().UnixNano()))
|
||||
if err := fifoSvc.RegisterStockable(fifo.StockableConfig{
|
||||
Key: key,
|
||||
Table: "test_fifo_stockables",
|
||||
Columns: fifo.StockableColumns{
|
||||
ID: "id",
|
||||
ProductWarehouseID: "product_warehouse_id",
|
||||
TotalQuantity: "total_qty",
|
||||
TotalUsedQuantity: "total_used_qty",
|
||||
CreatedAt: "created_at",
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatalf("register stockable: %v", err)
|
||||
}
|
||||
|
||||
svc := servicePkg.NewRecordingFIFOIntegrationService(
|
||||
recordingRepo.NewRecordingRepository(db),
|
||||
productWarehouseRepo,
|
||||
fifoSvc,
|
||||
)
|
||||
|
||||
return db, svc, fifoSvc, key
|
||||
}
|
||||
|
||||
func registerRecordingUsable(t *testing.T, fifoSvc commonSvc.FifoService) {
|
||||
t.Helper()
|
||||
err := fifoSvc.RegisterUsable(fifo.UsableConfig{
|
||||
Key: fifo.UsableKeyRecordingStock,
|
||||
Table: "recording_stocks",
|
||||
Columns: fifo.UsableColumns{
|
||||
ID: "id",
|
||||
ProductWarehouseID: "product_warehouse_id",
|
||||
UsageQuantity: "usage_qty",
|
||||
PendingQuantity: "pending_qty",
|
||||
CreatedAt: "created_at",
|
||||
},
|
||||
})
|
||||
if err != nil && !strings.Contains(strings.ToLower(err.Error()), "already registered") {
|
||||
t.Fatalf("register usable: %v", err)
|
||||
}
|
||||
if _, ok := fifo.Usable(fifo.UsableKeyRecordingStock); !ok {
|
||||
t.Fatal("recording stock usable key not registered")
|
||||
}
|
||||
}
|
||||
|
||||
func createProductWarehouseRow(t *testing.T, db *gorm.DB, qty float64) entity.ProductWarehouse {
|
||||
t.Helper()
|
||||
pw := entity.ProductWarehouse{
|
||||
ProductId: 1,
|
||||
WarehouseId: 1,
|
||||
Quantity: qty,
|
||||
// CreatedBy: 1,
|
||||
}
|
||||
if err := db.Create(&pw).Error; err != nil {
|
||||
t.Fatalf("create product warehouse: %v", err)
|
||||
}
|
||||
return pw
|
||||
}
|
||||
|
||||
func createRecordingStockRow(t *testing.T, db *gorm.DB, recordingID, productWarehouseID uint, desired float64) entity.RecordingStock {
|
||||
t.Helper()
|
||||
stock := entity.RecordingStock{
|
||||
RecordingId: recordingID,
|
||||
ProductWarehouseId: productWarehouseID,
|
||||
UsageQty: floatPtr(0),
|
||||
PendingQty: floatPtr(0),
|
||||
}
|
||||
if err := db.Create(&stock).Error; err != nil {
|
||||
t.Fatalf("create recording stock: %v", err)
|
||||
}
|
||||
stock.UsageQty = floatPtr(desired)
|
||||
return stock
|
||||
}
|
||||
|
||||
func createStockLot(t *testing.T, db *gorm.DB, productWarehouseID uint) testStockSource {
|
||||
t.Helper()
|
||||
lot := testStockSource{
|
||||
ProductWarehouseId: productWarehouseID,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
if err := db.Create(&lot).Error; err != nil {
|
||||
t.Fatalf("create stock lot: %v", err)
|
||||
}
|
||||
return lot
|
||||
}
|
||||
|
||||
func fetchRecordingStock(t *testing.T, db *gorm.DB, id uint) entity.RecordingStock {
|
||||
t.Helper()
|
||||
var stock entity.RecordingStock
|
||||
if err := db.First(&stock, id).Error; err != nil {
|
||||
t.Fatalf("fetch recording stock: %v", err)
|
||||
}
|
||||
return stock
|
||||
}
|
||||
|
||||
func fetchSingleAllocation(t *testing.T, db *gorm.DB, usableID uint) entity.StockAllocation {
|
||||
t.Helper()
|
||||
var alloc entity.StockAllocation
|
||||
if err := db.Where("usable_id = ?", usableID).Order("created_at ASC").First(&alloc).Error; err != nil {
|
||||
t.Fatalf("fetch allocation: %v", err)
|
||||
}
|
||||
return alloc
|
||||
}
|
||||
|
||||
func assertAllocationCount(t *testing.T, db *gorm.DB, expected int64) {
|
||||
t.Helper()
|
||||
var count int64
|
||||
if err := db.Model(&entity.StockAllocation{}).Count(&count).Error; err != nil {
|
||||
t.Fatalf("count allocations: %v", err)
|
||||
}
|
||||
if count != expected {
|
||||
t.Fatalf("expected %d allocations, got %d", expected, count)
|
||||
}
|
||||
}
|
||||
|
||||
func assertWarehouseQuantity(t *testing.T, db *gorm.DB, id uint, expected float64) {
|
||||
t.Helper()
|
||||
var pw entity.ProductWarehouse
|
||||
if err := db.First(&pw, id).Error; err != nil {
|
||||
t.Fatalf("fetch product warehouse: %v", err)
|
||||
}
|
||||
if mathAbs(pw.Quantity-expected) > 1e-6 {
|
||||
t.Fatalf("expected warehouse quantity %.3f, got %.3f", expected, pw.Quantity)
|
||||
}
|
||||
}
|
||||
|
||||
func assertFloatEqual(t *testing.T, expected float64, value *float64, msg string) {
|
||||
t.Helper()
|
||||
if value == nil {
|
||||
t.Fatalf("expected %s %.3f, got nil", msg, expected)
|
||||
}
|
||||
if mathAbs(*value-expected) > 1e-6 {
|
||||
t.Fatalf("%s: expected %.3f, got %.3f", msg, expected, *value)
|
||||
}
|
||||
}
|
||||
|
||||
func floatPtr(v float64) *float64 {
|
||||
p := new(float64)
|
||||
*p = v
|
||||
return p
|
||||
}
|
||||
|
||||
func mathAbs(v float64) float64 {
|
||||
if v < 0 {
|
||||
return -v
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func sanitizeKey(name string) string {
|
||||
if name == "" {
|
||||
return "CASE"
|
||||
}
|
||||
clean := strings.Map(func(r rune) rune {
|
||||
if (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') {
|
||||
return r
|
||||
}
|
||||
if r >= 'a' && r <= 'z' {
|
||||
return r - 32
|
||||
}
|
||||
return '_'
|
||||
}, name)
|
||||
return clean
|
||||
}
|
||||
|
||||
type fifoTestStockAllocationRepo struct {
|
||||
commonRepo.StockAllocationRepository
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func newFifoTestStockAllocationRepo(db *gorm.DB) commonRepo.StockAllocationRepository {
|
||||
return &fifoTestStockAllocationRepo{
|
||||
StockAllocationRepository: commonRepo.NewStockAllocationRepository(db),
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *fifoTestStockAllocationRepo) PatchOne(
|
||||
ctx context.Context,
|
||||
id uint,
|
||||
updates map[string]any,
|
||||
modifier func(*gorm.DB) *gorm.DB,
|
||||
) error {
|
||||
base := r.db
|
||||
|
||||
setClauses := make([]string, 0, len(updates))
|
||||
args := make([]any, 0, len(updates)+1)
|
||||
for column, value := range updates {
|
||||
colName := column
|
||||
if strings.EqualFold(column, "quantity") {
|
||||
colName = "qty"
|
||||
}
|
||||
setClauses = append(setClauses, fmt.Sprintf("%s = ?", colName))
|
||||
args = append(args, value)
|
||||
}
|
||||
args = append(args, id)
|
||||
sql := fmt.Sprintf("UPDATE stock_allocations SET %s WHERE id = ?", strings.Join(setClauses, ", "))
|
||||
|
||||
result := base.Exec(sql, args...)
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return gorm.ErrRecordNotFound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *fifoTestStockAllocationRepo) ReleaseByUsable(
|
||||
ctx context.Context,
|
||||
usableType string,
|
||||
usableID uint,
|
||||
note *string,
|
||||
modifier func(*gorm.DB) *gorm.DB,
|
||||
) error {
|
||||
base := r.db
|
||||
|
||||
setClause := "status = ?, released_at = ?"
|
||||
args := []any{entity.StockAllocationStatusReleased, time.Now()}
|
||||
if note != nil {
|
||||
setClause += ", note = ?"
|
||||
args = append(args, *note)
|
||||
}
|
||||
args = append(args, usableType, usableID, entity.StockAllocationStatusActive)
|
||||
sql := fmt.Sprintf(
|
||||
"UPDATE stock_allocations SET %s WHERE usable_type = ? AND usable_id = ? AND status = ?",
|
||||
setClause,
|
||||
)
|
||||
|
||||
result := base.Exec(sql, args...)
|
||||
return result.Error
|
||||
}
|
||||
Reference in New Issue
Block a user