mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 05:21:57 +00:00
feat/BE/US-279/Closing unfinished
This commit is contained in:
@@ -3,7 +3,7 @@ package capabilities
|
||||
import (
|
||||
"strings"
|
||||
|
||||
recordings "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings"
|
||||
permission "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||
)
|
||||
|
||||
// FromPermissions returns a filtered map of capabilities that the frontend can use
|
||||
@@ -37,8 +37,8 @@ func normalizeAndAllow(perm string) (string, bool) {
|
||||
}
|
||||
|
||||
var allowed = map[string]struct{}{
|
||||
recordings.PermissionRecordingRead: {},
|
||||
recordings.PermissionRecordingCreate: {},
|
||||
recordings.PermissionRecordingUpdate: {},
|
||||
recordings.PermissionRecordingDelete: {},
|
||||
permission.PermissionRecordingRead: {},
|
||||
permission.PermissionRecordingCreate: {},
|
||||
permission.PermissionRecordingUpdate: {},
|
||||
permission.PermissionRecordingDelete: {},
|
||||
}
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
DROP INDEX IF EXISTS idx_project_flock_kandangs_closed_at;
|
||||
ALTER TABLE project_flock_kandangs
|
||||
DROP COLUMN IF EXISTS closed_at;
|
||||
@@ -0,0 +1,5 @@
|
||||
ALTER TABLE project_flock_kandangs
|
||||
ADD COLUMN IF NOT EXISTS closed_at TIMESTAMPTZ;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_project_flock_kandangs_closed_at
|
||||
ON project_flock_kandangs (closed_at);
|
||||
@@ -3,11 +3,12 @@ package entities
|
||||
import "time"
|
||||
|
||||
type ProjectFlockKandang struct {
|
||||
Id uint `gorm:"primaryKey"`
|
||||
ProjectFlockId uint `gorm:"not null;index:idx_project_flock_kandangs_project;uniqueIndex:idx_project_flock_kandangs_unique"`
|
||||
KandangId uint `gorm:"not null;index:idx_project_flock_kandangs_kandang;uniqueIndex:idx_project_flock_kandangs_unique"`
|
||||
Period int `gorm:"not null"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime"`
|
||||
Id uint `gorm:"primaryKey"`
|
||||
ProjectFlockId uint `gorm:"not null;index:idx_project_flock_kandangs_project;uniqueIndex:idx_project_flock_kandangs_unique"`
|
||||
KandangId uint `gorm:"not null;index:idx_project_flock_kandangs_kandang;uniqueIndex:idx_project_flock_kandangs_unique"`
|
||||
Period int `gorm:"not null"`
|
||||
ClosedAt *time.Time `gorm:"index"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime"`
|
||||
|
||||
ProjectFlock ProjectFlock `gorm:"foreignKey:ProjectFlockId;references:Id"`
|
||||
Kandang Kandang `gorm:"foreignKey:KandangId;references:Id"`
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
service "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/sso"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
@@ -107,10 +106,11 @@ func AuthenticatedUser(c *fiber.Ctx) (*entity.User, bool) {
|
||||
|
||||
func ActorIDFromContext(c *fiber.Ctx) (uint, error) {
|
||||
user, ok := AuthenticatedUser(c)
|
||||
if !ok || user == nil || user.Id == 0 {
|
||||
if !ok || user == nil || user.Id == 0 {
|
||||
return 0, fiber.NewError(fiber.StatusUnauthorized, "Please authenticate")
|
||||
}
|
||||
return user.Id, nil
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
// AuthDetails returns the full authentication context (token, claims, user).
|
||||
@@ -199,3 +199,72 @@ func hasAllScopes(have, required []string) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
// RequirePermissions ensures the authenticated user possesses all specified permissions.
|
||||
func RequirePermissions(perms ...string) fiber.Handler {
|
||||
required := canonicalPermissions(perms)
|
||||
return func(c *fiber.Ctx) error {
|
||||
if len(required) == 0 {
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
ctx, ok := AuthDetails(c)
|
||||
if !ok || ctx == nil {
|
||||
return fiber.NewError(fiber.StatusUnauthorized, "Please authenticate")
|
||||
}
|
||||
|
||||
userPerms := ctx.permissionSet()
|
||||
if len(userPerms) == 0 {
|
||||
return fiber.NewError(fiber.StatusForbidden, "Insufficient permission")
|
||||
}
|
||||
|
||||
for _, perm := range required {
|
||||
if _, has := userPerms[perm]; !has {
|
||||
return fiber.NewError(fiber.StatusForbidden, "Insufficient permission")
|
||||
}
|
||||
}
|
||||
|
||||
return c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// HasPermission reports whether the current request context includes the given permission.
|
||||
func HasPermission(c *fiber.Ctx, perm string) bool {
|
||||
ctx, ok := AuthDetails(c)
|
||||
if !ok || ctx == nil {
|
||||
return false
|
||||
}
|
||||
perm = canonicalPermission(perm)
|
||||
if perm == "" {
|
||||
return false
|
||||
}
|
||||
_, has := ctx.permissionSet()[perm]
|
||||
return has
|
||||
}
|
||||
|
||||
func (a *AuthContext) permissionSet() map[string]struct{} {
|
||||
if a == nil || a.Permissions == nil {
|
||||
return nil
|
||||
}
|
||||
return a.Permissions
|
||||
}
|
||||
|
||||
func canonicalPermissions(perms []string) []string {
|
||||
out := make([]string, 0, len(perms))
|
||||
seen := make(map[string]struct{}, len(perms))
|
||||
for _, perm := range perms {
|
||||
if canonical := canonicalPermission(perm); canonical != "" {
|
||||
if _, ok := seen[canonical]; ok {
|
||||
continue
|
||||
}
|
||||
seen[canonical] = struct{}{}
|
||||
out = append(out, canonical)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func canonicalPermission(perm string) string {
|
||||
return strings.ToLower(strings.TrimSpace(perm))
|
||||
}
|
||||
|
||||
@@ -1,75 +1,14 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
//project-flock
|
||||
const (
|
||||
PermissionProjectFlockClosing = "lti:project-flock:closing"
|
||||
)
|
||||
|
||||
// RequirePermissions ensures the authenticated user possesses all specified permissions.
|
||||
func RequirePermissions(perms ...string) fiber.Handler {
|
||||
required := canonicalPermissions(perms)
|
||||
return func(c *fiber.Ctx) error {
|
||||
if len(required) == 0 {
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
ctx, ok := AuthDetails(c)
|
||||
if !ok || ctx == nil {
|
||||
return fiber.NewError(fiber.StatusUnauthorized, "Please authenticate")
|
||||
}
|
||||
|
||||
userPerms := ctx.permissionSet()
|
||||
if len(userPerms) == 0 {
|
||||
return fiber.NewError(fiber.StatusForbidden, "Insufficient permission")
|
||||
}
|
||||
|
||||
for _, perm := range required {
|
||||
if _, has := userPerms[perm]; !has {
|
||||
return fiber.NewError(fiber.StatusForbidden, "Insufficient permission")
|
||||
}
|
||||
}
|
||||
|
||||
return c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// HasPermission reports whether the current request context includes the given permission.
|
||||
func HasPermission(c *fiber.Ctx, perm string) bool {
|
||||
ctx, ok := AuthDetails(c)
|
||||
if !ok || ctx == nil {
|
||||
return false
|
||||
}
|
||||
perm = canonicalPermission(perm)
|
||||
if perm == "" {
|
||||
return false
|
||||
}
|
||||
_, has := ctx.permissionSet()[perm]
|
||||
return has
|
||||
}
|
||||
|
||||
func (a *AuthContext) permissionSet() map[string]struct{} {
|
||||
if a == nil || a.Permissions == nil {
|
||||
return nil
|
||||
}
|
||||
return a.Permissions
|
||||
}
|
||||
|
||||
func canonicalPermissions(perms []string) []string {
|
||||
out := make([]string, 0, len(perms))
|
||||
seen := make(map[string]struct{}, len(perms))
|
||||
for _, perm := range perms {
|
||||
if canonical := canonicalPermission(perm); canonical != "" {
|
||||
if _, ok := seen[canonical]; ok {
|
||||
continue
|
||||
}
|
||||
seen[canonical] = struct{}{}
|
||||
out = append(out, canonical)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func canonicalPermission(perm string) string {
|
||||
return strings.ToLower(strings.TrimSpace(perm))
|
||||
}
|
||||
//recording
|
||||
const (
|
||||
PermissionRecordingRead = "recording.read"
|
||||
PermissionRecordingCreate = "recording.write"
|
||||
PermissionRecordingUpdate = "recording.update"
|
||||
PermissionRecordingDelete = "recording.delete"
|
||||
)
|
||||
@@ -2,9 +2,11 @@ package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -13,6 +15,8 @@ type ExpenseRepository interface {
|
||||
IdExists(ctx context.Context, id uint64) (bool, error)
|
||||
GetNextSequence(ctx context.Context) (int, error)
|
||||
GetWithSupplier(ctx context.Context, id uint64) (*entity.Expense, error)
|
||||
WithProjectFlockKandangFilter(pfkID uint) func(*gorm.DB) *gorm.DB
|
||||
CountUnfinishedByProjectFlockKandang(ctx context.Context, pfkID uint, isFinished func(*entity.Approval) bool) (int64, error)
|
||||
}
|
||||
|
||||
type ExpenseRepositoryImpl struct {
|
||||
@@ -49,3 +53,51 @@ func (r *ExpenseRepositoryImpl) GetWithSupplier(ctx context.Context, id uint64)
|
||||
}
|
||||
return &expense, nil
|
||||
}
|
||||
|
||||
func (r *ExpenseRepositoryImpl) WithProjectFlockKandangFilter(pfkID uint) func(*gorm.DB) *gorm.DB {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
if pfkID == 0 {
|
||||
return db
|
||||
}
|
||||
return db.Joins("JOIN expense_nonstocks ON expense_nonstocks.expense_id = expenses.id").
|
||||
Where("expense_nonstocks.project_flock_kandang_id = ?", pfkID)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ExpenseRepositoryImpl) CountUnfinishedByProjectFlockKandang(ctx context.Context, pfkID uint, isFinished func(*entity.Approval) bool) (int64, error) {
|
||||
if pfkID == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
var ids []uint64
|
||||
if err := r.DB().WithContext(ctx).
|
||||
Table("expenses").
|
||||
Scopes(r.WithProjectFlockKandangFilter(pfkID)).
|
||||
Group("expenses.id").
|
||||
Pluck("expenses.id", &ids).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
var unfinished int64
|
||||
for _, id := range ids {
|
||||
var latest entity.Approval
|
||||
err := r.DB().WithContext(ctx).
|
||||
Table("approvals").
|
||||
Where("approvable_type = ? AND approvable_id = ?", utils.ApprovalWorkflowExpense.String(), id).
|
||||
Order("action_at DESC").
|
||||
Limit(1).
|
||||
First(&latest).Error
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return 0, err
|
||||
}
|
||||
if isFinished != nil {
|
||||
if !isFinished(&latest) {
|
||||
unfinished++
|
||||
}
|
||||
}
|
||||
}
|
||||
return unfinished, nil
|
||||
}
|
||||
|
||||
+26
@@ -84,3 +84,29 @@ func (u *ProjectFlockKandangController) GetOne(c *fiber.Ctx) error {
|
||||
Data: dto.ToProjectFlockKandangDetailDTOWithAvailableQty(*result, availableQtys, productWarehouses),
|
||||
})
|
||||
}
|
||||
|
||||
func (u *ProjectFlockKandangController) Closing(c *fiber.Ctx) error {
|
||||
id, err := strconv.Atoi(c.Params("id"))
|
||||
if err != nil || id <= 0 {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid Id")
|
||||
}
|
||||
|
||||
req := new(validation.Closing)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
|
||||
}
|
||||
|
||||
result, err := u.ProjectFlockKandangService.Closing(c, uint(id), req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).
|
||||
JSON(response.Success{
|
||||
Code: fiber.StatusOK,
|
||||
Status: "success",
|
||||
Message: "Status closing kandang diperbarui",
|
||||
// Data: dto.ToProjectFlockKandangDetailDTO(*result),
|
||||
Data: result,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
|
||||
rExpense "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/repositories"
|
||||
rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
|
||||
rWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||
@@ -36,7 +37,8 @@ func (ProjectFlockKandangModule) RegisterRoutes(router fiber.Router, db *gorm.DB
|
||||
panic(fmt.Sprintf("failed to register project flock kandang approval workflow: %v", err))
|
||||
}
|
||||
|
||||
projectFlockKandangService := sProjectFlockKandang.NewProjectFlockKandangService(projectFlockKandangRepo, approvalService, warehouseRepo, productWarehouseRepo, projectFlockPopulationRepo, validate)
|
||||
expenseRepo := rExpense.NewExpenseRepository(db)
|
||||
projectFlockKandangService := sProjectFlockKandang.NewProjectFlockKandangService(projectFlockKandangRepo, approvalService, expenseRepo, warehouseRepo, productWarehouseRepo, projectFlockPopulationRepo, validate)
|
||||
userService := sUser.NewUserService(userRepo, validate)
|
||||
|
||||
ProjectFlockKandangRoutes(router, userService, projectFlockKandangService)
|
||||
|
||||
@@ -22,5 +22,5 @@ func ProjectFlockKandangRoutes(v1 fiber.Router, u user.UserService, s projectFlo
|
||||
|
||||
route.Get("/", ctrl.GetAll)
|
||||
route.Get("/:id", ctrl.GetOne)
|
||||
|
||||
route.Post("/:id/closing", ctrl.Closing)
|
||||
}
|
||||
|
||||
+104
-5
@@ -2,24 +2,28 @@ package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/sirupsen/logrus"
|
||||
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
m "gitlab.com/mbugroup/lti-api.git/internal/middleware"
|
||||
expenseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/repositories"
|
||||
rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
|
||||
rWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories"
|
||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project-flock-kandangs/validations"
|
||||
repository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type ProjectFlockKandangService interface {
|
||||
GetAll(ctx *fiber.Ctx, params *validation.Query) ([]entity.ProjectFlockKandang, int64, error)
|
||||
GetOne(ctx *fiber.Ctx, id uint) (*entity.ProjectFlockKandang, map[uint]float64, []entity.ProductWarehouse, error)
|
||||
Closing(ctx *fiber.Ctx, id uint, req *validation.Closing) (*entity.ProjectFlockKandang, error)
|
||||
}
|
||||
|
||||
// Note: map[uint]float64 adalah mapping dari ProductWarehouse ID ke calculated available quantity
|
||||
@@ -29,17 +33,19 @@ type projectFlockKandangService struct {
|
||||
Validate *validator.Validate
|
||||
Repository repository.ProjectFlockKandangRepository
|
||||
ApprovalSvc commonSvc.ApprovalService
|
||||
ExpenseRepo expenseRepo.ExpenseRepository
|
||||
WarehouseRepo rWarehouse.WarehouseRepository
|
||||
ProductWarehouseRepo rProductWarehouse.ProductWarehouseRepository
|
||||
PopulationRepo repository.ProjectFlockPopulationRepository
|
||||
}
|
||||
|
||||
func NewProjectFlockKandangService(repo repository.ProjectFlockKandangRepository, approvalSvc commonSvc.ApprovalService, warehouseRepo rWarehouse.WarehouseRepository, productWarehouseRepo rProductWarehouse.ProductWarehouseRepository, populationRepo repository.ProjectFlockPopulationRepository, validate *validator.Validate) ProjectFlockKandangService {
|
||||
func NewProjectFlockKandangService(repo repository.ProjectFlockKandangRepository, approvalSvc commonSvc.ApprovalService, expenseRepo expenseRepo.ExpenseRepository, warehouseRepo rWarehouse.WarehouseRepository, productWarehouseRepo rProductWarehouse.ProductWarehouseRepository, populationRepo repository.ProjectFlockPopulationRepository, validate *validator.Validate) ProjectFlockKandangService {
|
||||
return &projectFlockKandangService{
|
||||
Log: utils.Log,
|
||||
Validate: validate,
|
||||
Repository: repo,
|
||||
ApprovalSvc: approvalSvc,
|
||||
ExpenseRepo: expenseRepo,
|
||||
WarehouseRepo: warehouseRepo,
|
||||
ProductWarehouseRepo: productWarehouseRepo,
|
||||
PopulationRepo: populationRepo,
|
||||
@@ -166,6 +172,99 @@ func (s projectFlockKandangService) getAvailableQuantities(c *fiber.Ctx, project
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s projectFlockKandangService) Closing(c *fiber.Ctx, id uint, req *validation.Closing) (*entity.ProjectFlockKandang, error) {
|
||||
if err := s.Validate.Struct(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
actorID, err := m.ActorIDFromContext(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pfk, err := s.Repository.GetByID(c.Context(), id)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "ProjectFlockKandang not found")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if s.ApprovalSvc != nil {
|
||||
latest, aerr := s.ApprovalSvc.LatestByTarget(c.Context(), utils.ApprovalWorkflowProjectFlock, pfk.ProjectFlockId, nil)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
if latest == nil || latest.StepNumber != uint16(utils.ProjectFlockStepAktif) || latest.Action == nil || *latest.Action != entity.ApprovalActionApproved {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Project flock belum berstatus aktif")
|
||||
}
|
||||
}
|
||||
|
||||
action := strings.ToLower(strings.TrimSpace(req.Action))
|
||||
now := time.Now()
|
||||
|
||||
switch action {
|
||||
case "close":
|
||||
if pfk.ClosedAt != nil {
|
||||
return nil, fiber.NewError(fiber.StatusConflict, "Kandang sudah closed")
|
||||
}
|
||||
if s.ExpenseRepo != nil && s.ApprovalSvc != nil {
|
||||
unfinished, err := s.ExpenseRepo.CountUnfinishedByProjectFlockKandang(c.Context(), pfk.Id, func(appr *entity.Approval) bool {
|
||||
return appr != nil && appr.StepNumber == uint16(utils.ExpenseStepSelesai) && appr.Action != nil && *appr.Action == entity.ApprovalActionApproved
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if unfinished > 0 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Masih ada expense belum selesai untuk kandang ini")
|
||||
}
|
||||
}
|
||||
closeTime := now
|
||||
if req.ClosedDate != nil {
|
||||
parsed, perr := utils.ParseDateString(strings.TrimSpace(*req.ClosedDate))
|
||||
if perr != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "closed_date tidak valid, gunakan format YYYY-MM-DD")
|
||||
}
|
||||
closeTime = parsed
|
||||
}
|
||||
if err := s.Repository.UpdateClosedAt(c.Context(), id, &closeTime); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if s.ApprovalSvc != nil {
|
||||
closeAction := entity.ApprovalActionApproved
|
||||
if _, aerr := s.ApprovalSvc.CreateApproval(
|
||||
c.Context(),
|
||||
utils.ApprovalWorkflowProjectFlockKandang,
|
||||
id,
|
||||
utils.ProjectFlockKandangStepDisetujui,
|
||||
&closeAction,
|
||||
actorID,
|
||||
nil,
|
||||
); aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
}
|
||||
case "unclose":
|
||||
if pfk.ClosedAt == nil {
|
||||
return nil, fiber.NewError(fiber.StatusConflict, "Kandang belum closed")
|
||||
}
|
||||
openNewer, err := s.Repository.HasOpenNewerPeriod(c.Context(), pfk.KandangId, pfk.Period, &pfk.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if openNewer {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Tidak dapat un-close: ada periode yang sedang berjalan")
|
||||
}
|
||||
if err := s.Repository.UpdateClosedAt(c.Context(), id, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "action harus close atau unclose")
|
||||
}
|
||||
|
||||
return s.Repository.GetByID(c.Context(), id)
|
||||
}
|
||||
|
||||
func (s projectFlockKandangService) calculateAvailableQuantityForProductWarehouse(c *fiber.Ctx, projectFlockKandang *entity.ProjectFlockKandang, productWarehouse *entity.ProductWarehouse) (float64, error) {
|
||||
availableQty := productWarehouse.Quantity
|
||||
|
||||
|
||||
+5
@@ -22,3 +22,8 @@ type Query struct {
|
||||
SortOrder string `query:"sort_order" validate:"omitempty,oneof=ASC DESC"`
|
||||
StepName string `query:"step_name" validate:"omitempty,max=50"`
|
||||
}
|
||||
|
||||
type Closing struct {
|
||||
Action string `json:"action" validate:"required,oneof=close unclose"`
|
||||
ClosedDate *string `json:"closed_date,omitempty"`
|
||||
}
|
||||
+29
-1
@@ -3,6 +3,7 @@ package repository
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
@@ -14,6 +15,7 @@ type ProjectFlockKandangRepository interface {
|
||||
GetByID(ctx context.Context, id uint) (*entity.ProjectFlockKandang, error)
|
||||
GetByProjectFlockAndKandang(ctx context.Context, projectFlockID uint, kandangID uint) (*entity.ProjectFlockKandang, error)
|
||||
GetActiveByKandangID(ctx context.Context, kandangID uint) (*entity.ProjectFlockKandang, error)
|
||||
UpdateClosedAt(ctx context.Context, id uint, t *time.Time) error
|
||||
CreateMany(ctx context.Context, records []*entity.ProjectFlockKandang) error
|
||||
DeleteMany(ctx context.Context, projectFlockID uint, kandangIDs []uint) error
|
||||
GetAll(ctx context.Context, offset int, limit int, modifier func(*gorm.DB) *gorm.DB) ([]entity.ProjectFlockKandang, int64, error)
|
||||
@@ -23,9 +25,9 @@ type ProjectFlockKandangRepository interface {
|
||||
FindKandangsWithRecordings(ctx context.Context, projectFlockID uint, kandangIDs []uint) ([]entity.Kandang, error)
|
||||
MaxPeriodByBaseName(ctx context.Context, baseName string) (int, error)
|
||||
ProjectPeriodsByProjectIDs(ctx context.Context, projectIDs []uint) (map[uint]int, error)
|
||||
HasOpenNewerPeriod(ctx context.Context, kandangID uint, currentPeriod int, excludeID *uint) (bool, error)
|
||||
WithTx(tx *gorm.DB) ProjectFlockKandangRepository
|
||||
IdExists(ctx context.Context, id uint) (bool, error)
|
||||
DB() *gorm.DB
|
||||
}
|
||||
|
||||
type projectFlockKandangRepositoryImpl struct {
|
||||
@@ -251,6 +253,32 @@ func (r *projectFlockKandangRepositoryImpl) GetActiveByKandangID(ctx context.Con
|
||||
return record, nil
|
||||
}
|
||||
|
||||
func (r *projectFlockKandangRepositoryImpl) UpdateClosedAt(ctx context.Context, id uint, t *time.Time) error {
|
||||
return r.db.WithContext(ctx).
|
||||
Model(&entity.ProjectFlockKandang{}).
|
||||
Where("id = ?", id).
|
||||
Update("closed_at", t).Error
|
||||
}
|
||||
|
||||
func (r *projectFlockKandangRepositoryImpl) HasOpenNewerPeriod(ctx context.Context, kandangID uint, currentPeriod int, excludeID *uint) (bool, error) {
|
||||
if kandangID == 0 {
|
||||
return false, nil
|
||||
}
|
||||
q := r.db.WithContext(ctx).
|
||||
Table("project_flock_kandangs").
|
||||
Where("kandang_id = ?", kandangID).
|
||||
Where("period > ?", currentPeriod).
|
||||
Where("closed_at IS NULL")
|
||||
if excludeID != nil {
|
||||
q = q.Where("id <> ?", *excludeID)
|
||||
}
|
||||
var count int64
|
||||
if err := q.Count(&count).Error; err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
func (r *projectFlockKandangRepositoryImpl) ListExistingKandangIDs(ctx context.Context, projectFlockID uint, kandangIDs []uint) ([]uint, error) {
|
||||
if len(kandangIDs) == 0 {
|
||||
return nil, nil
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package recordings
|
||||
|
||||
const (
|
||||
PermissionRecordingRead = "recording.read"
|
||||
PermissionRecordingCreate = "recording.write"
|
||||
PermissionRecordingUpdate = "recording.update"
|
||||
PermissionRecordingDelete = "recording.delete"
|
||||
)
|
||||
Reference in New Issue
Block a user